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); |
alexandre.ardhuin
2015/09/03 20:14:11
There are some strange indentations in the rest of
|
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) { |