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

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: about to land Created 5 years, 2 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 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;
« no previous file with comments | « pkg/compiler/lib/src/js_emitter/native_emitter.dart ('k') | pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698