| 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);
|
| + }
|
| +}
|
|
|