Index: pkg/compiler/lib/src/js_backend/js_interop_analysis.dart |
diff --git a/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart b/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..777a36829bd1f376da78379774af9a7cc7a453ba |
--- /dev/null |
+++ b/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart |
@@ -0,0 +1,142 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+/// Analysis to determine how to generate code for typed JavaScript interop. |
+library compiler.src.js_backend.js_interop_analysis; |
+ |
+import '../common/names.dart' show Identifiers; |
+import '../compiler.dart' show Compiler; |
+import '../diagnostics/messages.dart' show MessageKind; |
+import '../constants/values.dart' |
+ show |
+ ConstantValue, |
+ ConstructedConstantValue, |
+ ListConstantValue, |
+ NullConstantValue, |
+ StringConstantValue, |
+ TypeConstantValue; |
+import '../elements/elements.dart' |
+ show |
+ ClassElement, |
+ Element, |
+ FieldElement, |
+ FunctionElement, |
+ LibraryElement, |
+ MetadataAnnotation; |
+ |
+import '../js/js.dart' as jsAst; |
+import '../js/js.dart' show js; |
+import '../universe/selector.dart' show Selector; |
+import '../universe/universe.dart' show SelectorConstraints; |
+ |
+import 'js_backend.dart' show JavaScriptBackend; |
+ |
+class JsInteropAnalysis { |
+ final JavaScriptBackend backend; |
+ |
+ /// The resolved [FieldElement] for `Js.name`. |
+ FieldElement nameField; |
+ bool enabledJsInterop = false; |
+ |
+ /// Whether the backend is currently processing the codegen queue. |
+ bool _inCodegen = false; |
+ |
+ JsInteropAnalysis(this.backend); |
+ |
+ void onQueueClosed() { |
+ if (_inCodegen) return; |
+ |
+ if (backend.jsAnnotationClass != null) { |
+ nameField = backend.jsAnnotationClass.lookupMember('name'); |
+ backend.compiler.libraryLoader.libraries |
+ .forEach(processJsInteropAnnotationsInLibrary); |
+ } |
+ } |
+ |
+ void onCodegenStart() { |
+ _inCodegen = true; |
+ } |
+ |
+ void processJsInteropAnnotation(Element e) { |
+ for (MetadataAnnotation annotation in e.implementation.metadata) { |
+ ConstantValue constant = backend.compiler.constants.getConstantValue( |
+ annotation.constant); |
+ if (constant == null || constant is! ConstructedConstantValue) continue; |
+ ConstructedConstantValue constructedConstant = constant; |
+ if (constructedConstant.type.element == backend.jsAnnotationClass) { |
+ ConstantValue value = constructedConstant.fields[nameField]; |
+ if (value.isString) { |
+ StringConstantValue stringValue = value; |
+ e.setJsInteropName(stringValue.primitiveValue.slowToString()); |
+ } else { |
+ // TODO(jacobr): report a warning if the value is not a String. |
+ e.setJsInteropName(''); |
+ } |
+ enabledJsInterop = true; |
+ return; |
+ } |
+ } |
+ } |
+ |
+ void processJsInteropAnnotationsInLibrary(LibraryElement library) { |
+ processJsInteropAnnotation(library); |
+ library.implementation.forEachLocalMember((Element element) { |
+ processJsInteropAnnotation(element); |
+ if (!element.isClass || !element.isJsInterop) return; |
+ |
+ ClassElement classElement = element; |
+ |
+ if (!classElement |
+ .implementsInterface(backend.jsJavaScriptObjectClass)) { |
+ backend.reporter.reportErrorMessage(classElement, |
+ MessageKind.JS_INTEROP_CLASS_CANNOT_EXTEND_DART_CLASS, { |
+ 'cls': classElement.name, |
+ 'superclass': classElement.superclass.name |
+ }); |
+ } |
+ |
+ classElement.forEachMember( |
+ (ClassElement classElement, Element member) { |
+ processJsInteropAnnotation(member); |
+ |
+ if (!member.isSynthesized && |
+ classElement.isJsInterop && |
+ member is FunctionElement) { |
+ FunctionElement fn = member; |
+ if (!fn.isExternal && !fn.isAbstract) { |
+ backend.reporter.reportErrorMessage( |
+ fn, |
+ MessageKind.JS_INTEROP_CLASS_NON_EXTERNAL_MEMBER, |
+ {'cls': classElement.name, 'member': member.name}); |
+ } |
+ } |
+ }); |
+ }); |
+ } |
+ |
+ jsAst.Statement buildJsInteropBootstrap() { |
+ if (!enabledJsInterop) return null; |
+ List<jsAst.Statement> statements = <jsAst.Statement>[]; |
+ backend.compiler.codegenWorld.forEachInvokedName( |
+ (String name, Map<Selector, SelectorConstraints> selectors) { |
+ selectors.forEach((Selector selector, SelectorConstraints constraints) { |
+ if (selector.isClosureCall) { |
+ // TODO(jacobr): support named arguments. |
+ if (selector.namedArgumentCount > 0) return; |
+ int argumentCount = selector.argumentCount; |
+ var candidateParameterNames = |
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMOPQRSTUVWXYZ'; |
+ var parameters = new List<String>.generate( |
+ argumentCount, (i) => candidateParameterNames[i]); |
+ |
+ var name = backend.namer.invocationName(selector); |
+ statements.add(js.statement( |
+ 'Function.prototype.# = function(#) { return this(#) }', |
+ [name, parameters, parameters])); |
+ } |
+ }); |
+ }); |
+ return new jsAst.Block(statements); |
+ } |
+} |