| Index: pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
|
| diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
|
| index b10abf2aca6eac24e5d1e881138d6ab3ce8a2e49..ac34f0c34148dcb34a3ab9c78d2594ef488860a0 100644
|
| --- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
|
| +++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
|
| @@ -14,7 +14,8 @@ import '../../js/js.dart' as js;
|
| import '../../js_backend/js_backend.dart' show
|
| Namer,
|
| JavaScriptBackend,
|
| - JavaScriptConstantCompiler;
|
| + JavaScriptConstantCompiler,
|
| + StringBackedName;
|
|
|
| import '../js_emitter.dart' show
|
| ClassStubGenerator,
|
| @@ -28,6 +29,7 @@ import '../js_emitter.dart' show
|
| import '../../elements/elements.dart' show
|
| FieldElement,
|
| MethodElement,
|
| + Name,
|
| ParameterElement;
|
|
|
| import '../../universe/universe.dart' show Universe, ReceiverMaskSet;
|
| @@ -201,6 +203,7 @@ class ProgramBuilder {
|
| }
|
|
|
| MainFragment _buildMainFragment(LibrariesMap librariesMap) {
|
| + _buildJsInteropStubs(librariesMap);
|
| // Construct the main output from the libraries and the registered holders.
|
| MainFragment result = new MainFragment(
|
| librariesMap.outputUnit,
|
| @@ -319,6 +322,99 @@ class ProgramBuilder {
|
| return libraries;
|
| }
|
|
|
| + void _buildJsInteropStubs(LibrariesMap librariesMap) {
|
| + if (_classes.containsKey(_compiler.objectClass)) {
|
| + var toStringInvocation = namer.invocationName(new Selector.call(
|
| + new Name("toString", _compiler.objectClass.library),
|
| + CallStructure.NO_ARGS));
|
| + // TODO(jacobr): register toString as used so that it is always accessible
|
| + // from JavaScript.
|
| + _classes[_compiler.objectClass].callStubs.add(_buildStubMethod(
|
| + new StringBackedName("toString"),
|
| + js.js('function() { return this.#(this) }', toStringInvocation)));
|
| + }
|
| +
|
| + // We add all members from classes marked with isJsInterop to the base
|
| + // Interceptor class with implementations that directly call the
|
| + // corresponding JavaScript member. We do not attempt to bind this when
|
| + // tearing off JavaScript methods as we cannot distinguish between calling
|
| + // a regular getter that returns a JavaScript function and tearing off
|
| + // a method in the case where there exist multiple JavaScript classes
|
| + // that conflict on whether the member is a getter or a method.
|
| + var interceptorClass = _classes[backend.jsInterceptorClass];
|
| + var stubNames = new Set<String>();
|
| + librariesMap.forEach((LibraryElement library, List<Element> elements) {
|
| + for (Element e in elements) {
|
| + if (e is ClassElement && e.isJsInterop) {
|
| + e.declaration.forEachMember((_, Element member) {
|
| + if (!member.isInstanceMember) return;
|
| + if (member.isGetter || member.isField || member.isFunction) {
|
| + var selectors =
|
| + _compiler.codegenWorld.getterInvocationsByName(member.name);
|
| + if (selectors != null && !selectors.isEmpty) {
|
| + for (var selector in selectors.keys) {
|
| + var stubName = namer.invocationName(selector);
|
| + if (stubNames.add(stubName.key)) {
|
| + interceptorClass.callStubs.add(_buildStubMethod(
|
| + stubName,
|
| + js.js(
|
| + 'function(obj) { return obj.# }', [member.name]),
|
| + element: member));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (member.isSetter || (member.isField && !member.isConst)) {
|
| + var selectors =
|
| + _compiler.codegenWorld.setterInvocationsByName(member.name);
|
| + if (selectors != null && !selectors.isEmpty) {
|
| + var stubName = namer.setterForElement(member);
|
| + if (stubNames.add(stubName.key)) {
|
| + interceptorClass.callStubs.add(_buildStubMethod(
|
| + stubName,
|
| + js.js('function(obj, v) { return obj.# = v }',
|
| + [member.name]),
|
| + element: member));
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (member.isFunction) {
|
| + var selectors =
|
| + _compiler.codegenWorld.invocationsByName(member.name);
|
| + FunctionElement fn = member;
|
| + // Named arguments are not yet supported. In the future we
|
| + // may want to map named arguments to an object literal containing
|
| + // all named arguments.
|
| + if (selectors != null && !selectors.isEmpty) {
|
| + for (var selector in selectors.keys) {
|
| + // Check whether the arity matches this member.
|
| + var argumentCount = selector.argumentCount;
|
| + if (argumentCount > fn.parameters.length) break;
|
| + if (argumentCount < fn.parameters.length &&
|
| + !fn.parameters[argumentCount].isOptional) break;
|
| + var stubName = namer.invocationName(selector);
|
| + if (!stubNames.add(stubName.key)) break;
|
| + var parameters = <js.Parameter>[];
|
| + for (var i = 0; i < argumentCount; i++) {
|
| + parameters.add(
|
| + new js.Parameter(new String.fromCharCode(97 + i)));
|
| + }
|
| + interceptorClass.callStubs.add(_buildStubMethod(
|
| + stubName,
|
| + js.js('function(obj, #) { return obj.#(#) }',
|
| + [parameters, member.name, parameters]),
|
| + element: member));
|
| + }
|
| + }
|
| + }
|
| + });
|
| + }
|
| + }
|
| + });
|
| + }
|
| +
|
| // Note that a library-element may have multiple [Library]s, if it is split
|
| // into multiple output units.
|
| Library _buildLibrary(LibraryElement library, List<Element> elements) {
|
| @@ -366,14 +462,20 @@ class ProgramBuilder {
|
|
|
| Class _buildClass(ClassElement element) {
|
| bool onlyForRti = collector.classesOnlyNeededForRti.contains(element);
|
| + bool onlyJsInterop = element.isJsInterop;
|
| + if (onlyJsInterop) {
|
| + // TODO(jacobr): check whether the class has any active static fields
|
| + // if it does not we can suppress it completelly.
|
| + onlyForRti = true; // XXX is this right?
|
| + }
|
|
|
| List<Method> methods = [];
|
| List<StubMethod> callStubs = <StubMethod>[];
|
|
|
| ClassStubGenerator classStubGenerator =
|
| - new ClassStubGenerator(_compiler, namer, backend);
|
| + new ClassStubGenerator(_compiler, namer, backend);
|
| RuntimeTypeGenerator runtimeTypeGenerator =
|
| - new RuntimeTypeGenerator(_compiler, _task, namer);
|
| + new RuntimeTypeGenerator(_compiler, _task, namer);
|
|
|
| void visitMember(ClassElement enclosing, Element member) {
|
| assert(invariant(element, member.isDeclaration));
|
| @@ -390,7 +492,7 @@ class ProgramBuilder {
|
| if (selectors != null && !selectors.isEmpty) {
|
|
|
| Map<js.Name, js.Expression> callStubsForMember =
|
| - classStubGenerator.generateCallStubsForGetter(member, selectors);
|
| + classStubGenerator.generateCallStubsForGetter(member, selectors);
|
| callStubsForMember.forEach((js.Name name, js.Expression code) {
|
| callStubs.add(_buildStubMethod(name, code, element: member));
|
| });
|
| @@ -399,13 +501,13 @@ class ProgramBuilder {
|
| }
|
|
|
| List<StubMethod> typeVariableReaderStubs =
|
| - runtimeTypeGenerator.generateTypeVariableReaderStubs(element);
|
| + runtimeTypeGenerator.generateTypeVariableReaderStubs(element);
|
|
|
| List<StubMethod> noSuchMethodStubs = <StubMethod>[];
|
|
|
| if (backend.enabledNoSuchMethod && element == _compiler.objectClass) {
|
| Map<js.Name, Selector> selectors =
|
| - classStubGenerator.computeSelectorsForNsmHandlers();
|
| + classStubGenerator.computeSelectorsForNsmHandlers();
|
| selectors.forEach((js.Name name, Selector selector) {
|
| // If the program contains `const Symbol` names we have to retain them.
|
| String selectorName = selector.name;
|
| @@ -414,8 +516,8 @@ class ProgramBuilder {
|
| _symbolsMap[name] = selectorName;
|
| }
|
| noSuchMethodStubs
|
| - .add(classStubGenerator.generateStubForNoSuchMethod(name,
|
| - selector));
|
| + .add(classStubGenerator.generateStubForNoSuchMethod(name,
|
| + selector));
|
| });
|
| }
|
|
|
| @@ -436,39 +538,47 @@ class ProgramBuilder {
|
| }
|
|
|
| List<Field> instanceFields =
|
| - onlyForRti ? const <Field>[] : _buildFields(element, false);
|
| + onlyForRti ? const <Field>[] : _buildFields(element, false);
|
| List<Field> staticFieldsForReflection =
|
| - onlyForRti ? const <Field>[] : _buildFields(element, true);
|
| + onlyForRti ? const <Field>[] : _buildFields(element, true);
|
|
|
| TypeTestProperties typeTests =
|
| - runtimeTypeGenerator.generateIsTests(
|
| - element,
|
| - storeFunctionTypeInMetadata: _storeFunctionTypesInMetadata);
|
| + runtimeTypeGenerator.generateIsTests(
|
| + element,
|
| + storeFunctionTypeInMetadata: _storeFunctionTypesInMetadata);
|
|
|
| List<StubMethod> checkedSetters = <StubMethod>[];
|
| - for (Field field in instanceFields) {
|
| - if (field.needsCheckedSetter) {
|
| - assert(!field.needsUncheckedSetter);
|
| - Element element = field.element;
|
| - js.Expression code = backend.generatedCode[element];
|
| - assert(code != null);
|
| - js.Name name = namer.deriveSetterName(field.accessorName);
|
| - checkedSetters.add(_buildStubMethod(name, code, element: element));
|
| + List<StubMethod> isChecks = <StubMethod>[];
|
| + if (!onlyJsInterop) {
|
| + for (Field field in instanceFields) {
|
| + if (field.needsCheckedSetter) {
|
| + assert(!field.needsUncheckedSetter);
|
| + Element element = field.element;
|
| + js.Expression code = backend.generatedCode[element];
|
| + assert(code != null);
|
| + js.Name name = namer.deriveSetterName(field.accessorName);
|
| + checkedSetters.add(_buildStubMethod(name, code, element: element));
|
| + }
|
| }
|
| - }
|
|
|
| - List<StubMethod> isChecks = <StubMethod>[];
|
| - typeTests.properties.forEach((js.Name name, js.Node code) {
|
| - isChecks.add(_buildStubMethod(name, code));
|
| - });
|
| + typeTests.properties.forEach((js.Name name, js.Node code) {
|
| + isChecks.add(_buildStubMethod(name, code));
|
| + });
|
| + }
|
| + if (element.isJsInterop) {
|
| + // TODO(jacobr): make sure we aren't adding the same is checks multiple times..
|
| + typeTests.properties.forEach((js.Name name, js.Node code) {
|
| + _classes[backend.jsInterceptorClass].isChecks.add(_buildStubMethod(name, code));
|
| + });
|
| + }
|
|
|
| js.Name name = namer.className(element);
|
| String holderName = namer.globalObjectFor(element);
|
| // TODO(floitsch): we shouldn't update the registry in the middle of
|
| // building a class.
|
| Holder holder = _registry.registerHolder(holderName);
|
| - bool isInstantiated =
|
| - _compiler.codegenWorld.directlyInstantiatedClasses.contains(element);
|
| + bool isInstantiated = onlyJsInterop ? false :
|
| + _compiler.codegenWorld.directlyInstantiatedClasses.contains(element);
|
|
|
| Class result;
|
| if (element.isMixinApplication && !onlyForRti) {
|
|
|