Chromium Code Reviews| 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..b4748ad3930af01b2e86cd3a447e6ba9e0789875 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); |
|
sra1
2015/10/01 20:55:28
_addJsInteropStubs()
By convention _buildXXX retur
Jacob
2015/10/02 20:08:16
Done.
|
| // 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))); |
| + } |
|
sra1
2015/10/01 20:55:28
You can just use strings of names.
var parameters
Jacob
2015/10/02 20:08:16
Done.
|
| + interceptorClass.callStubs.add(_buildStubMethod( |
| + stubName, |
| + js.js('function(obj, #) { return obj.#(#) }', |
|
sra1
2015/10/01 20:55:28
obj -> receiver for consistency with other stubs.
Jacob
2015/10/02 20:08:16
Done.
|
| + [parameters, member.name, parameters]), |
| + element: member)); |
|
sra1
2015/10/01 20:55:28
indentation is off
Jacob
2015/10/02 20:08:15
Done.
|
| + } |
| + } |
| + } |
| + }); |
| + } |
| + } |
| + }); |
| + } |
| + |
| // 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; |
| + } |
| List<Method> methods = []; |
| List<StubMethod> callStubs = <StubMethod>[]; |
| ClassStubGenerator classStubGenerator = |
| - new ClassStubGenerator(_compiler, namer, backend); |
| + new ClassStubGenerator(_compiler, namer, backend); |
|
sra1
2015/10/01 20:55:28
repair indentation
Jacob
2015/10/02 20:08:16
Done.
|
| RuntimeTypeGenerator runtimeTypeGenerator = |
| - new RuntimeTypeGenerator(_compiler, _task, namer); |
| + new RuntimeTypeGenerator(_compiler, _task, namer); |
|
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:15
Done.
|
| 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); |
|
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:15
Done.
|
| 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); |
|
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:16
done... arghhh... intelij
|
| List<StubMethod> noSuchMethodStubs = <StubMethod>[]; |
| if (backend.enabledNoSuchMethod && element == _compiler.objectClass) { |
| Map<js.Name, Selector> selectors = |
| - classStubGenerator.computeSelectorsForNsmHandlers(); |
| + classStubGenerator.computeSelectorsForNsmHandlers(); |
|
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:15
Done.
|
| 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)); |
|
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:16
Done.
|
| }); |
| } |
| @@ -436,39 +538,47 @@ class ProgramBuilder { |
| } |
| List<Field> instanceFields = |
| - onlyForRti ? const <Field>[] : _buildFields(element, false); |
| + onlyForRti ? const <Field>[] : _buildFields(element, false); |
|
sra1
2015/10/01 20:55:28
ditto
Jacob
2015/10/02 20:08:16
Done.
|
| 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) { |
|
sra1
2015/10/01 20:55:28
use one of element.isJsInterop or onlyJsInterop co
Jacob
2015/10/02 20:08:16
Done.
|
| + // TODO(jacobr): make sure we aren't adding the same is checks multiple times.. |
|
sra1
2015/10/01 20:55:28
line lengyh
Jacob
2015/10/02 20:08:15
fixedh
|
| + 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) { |