| 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 b9619809ad8f20fade08951fe17483d775e188ab..01d3f3846b537d93960ebf69a5648c39cb24dff9 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
|
| @@ -37,6 +37,7 @@ import '../../elements/elements.dart' show
|
| FunctionSignature,
|
| LibraryElement,
|
| MethodElement,
|
| + Name,
|
| ParameterElement,
|
| TypedefElement,
|
| VariableElement;
|
| @@ -44,7 +45,10 @@ import '../../js/js.dart' as js;
|
| import '../../js_backend/js_backend.dart' show
|
| Namer,
|
| JavaScriptBackend,
|
| - JavaScriptConstantCompiler;
|
| + JavaScriptConstantCompiler,
|
| + StringBackedName;
|
| +import '../../universe/call_structure.dart' show
|
| + CallStructure;
|
| import '../../universe/selector.dart' show
|
| Selector;
|
| import '../../universe/universe.dart' show
|
| @@ -163,6 +167,8 @@ class ProgramBuilder {
|
| nativeClasses, interceptorClassesNeededByConstants,
|
| classesModifiedByEmitRTISupport);
|
|
|
| + _addJsInteropStubs(_registry.mainLibrariesMap);
|
| +
|
| MainFragment mainFragment = _buildMainFragment(_registry.mainLibrariesMap);
|
| Iterable<Fragment> deferredFragments =
|
| _registry.deferredLibrariesMap.map(_buildDeferredFragment);
|
| @@ -340,6 +346,99 @@ class ProgramBuilder {
|
| return libraries;
|
| }
|
|
|
| + void _addJsInteropStubs(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.jsJavaScriptObjectClass];
|
| + 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 candidateParameterNames =
|
| + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMOPQRSTUVWXYZ';
|
| + var parameters = new List<String>.generate(argumentCount,
|
| + (i) => candidateParameterNames[i]);
|
| +
|
| + interceptorClass.callStubs.add(_buildStubMethod(
|
| + stubName,
|
| + js.js('function(receiver, #) { return receiver.#(#) }',
|
| + [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) {
|
| @@ -387,6 +486,11 @@ class ProgramBuilder {
|
|
|
| Class _buildClass(ClassElement element) {
|
| bool onlyForRti = collector.classesOnlyNeededForRti.contains(element);
|
| + if (element.isJsInterop) {
|
| + // TODO(jacobr): check whether the class has any active static fields
|
| + // if it does not we can suppress it completely.
|
| + onlyForRti = true;
|
| + }
|
|
|
| List<Method> methods = [];
|
| List<StubMethod> callStubs = <StubMethod>[];
|
| @@ -467,28 +571,35 @@ class ProgramBuilder {
|
| 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 (element.isJsInterop) {
|
| + typeTests.properties.forEach((js.Name name, js.Node code) {
|
| + _classes[backend.jsInterceptorClass].isChecks.add(
|
| + _buildStubMethod(name, code));
|
| + });
|
| + } else {
|
| + 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));
|
| + });
|
| + }
|
|
|
| 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 =
|
| + bool isInstantiated = !element.isJsInterop &&
|
| _compiler.codegenWorld.directlyInstantiatedClasses.contains(element);
|
|
|
| Class result;
|
|
|