Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(480)

Unified Diff: pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart

Issue 1318043005: Support user generated custom native JS classes. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: ptal Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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) {

Powered by Google App Engine
This is Rietveld 408576698