OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /// Analysis to determine how to generate code for typed JavaScript interop. |
| 6 library compiler.src.js_backend.js_interop_analysis; |
| 7 |
| 8 import '../common/names.dart' show Identifiers; |
| 9 import '../compiler.dart' show Compiler; |
| 10 import '../diagnostics/messages.dart' show MessageKind; |
| 11 import '../constants/values.dart' |
| 12 show |
| 13 ConstantValue, |
| 14 ConstructedConstantValue, |
| 15 ListConstantValue, |
| 16 NullConstantValue, |
| 17 StringConstantValue, |
| 18 TypeConstantValue; |
| 19 import '../elements/elements.dart' |
| 20 show |
| 21 ClassElement, |
| 22 Element, |
| 23 FieldElement, |
| 24 FunctionElement, |
| 25 LibraryElement, |
| 26 MetadataAnnotation; |
| 27 |
| 28 import '../js/js.dart' as jsAst; |
| 29 import '../js/js.dart' show js; |
| 30 import '../universe/selector.dart' show Selector; |
| 31 import '../universe/universe.dart' show SelectorConstraints; |
| 32 |
| 33 import 'js_backend.dart' show JavaScriptBackend; |
| 34 |
| 35 class JsInteropAnalysis { |
| 36 final JavaScriptBackend backend; |
| 37 |
| 38 /// The resolved [FieldElement] for `Js.name`. |
| 39 FieldElement nameField; |
| 40 bool enabledJsInterop = false; |
| 41 |
| 42 /// Whether the backend is currently processing the codegen queue. |
| 43 bool _inCodegen = false; |
| 44 |
| 45 JsInteropAnalysis(this.backend); |
| 46 |
| 47 void onQueueClosed() { |
| 48 if (_inCodegen) return; |
| 49 |
| 50 if (backend.jsAnnotationClass != null) { |
| 51 nameField = backend.jsAnnotationClass.lookupMember('name'); |
| 52 backend.compiler.libraryLoader.libraries |
| 53 .forEach(processJsInteropAnnotationsInLibrary); |
| 54 } |
| 55 } |
| 56 |
| 57 void onCodegenStart() { |
| 58 _inCodegen = true; |
| 59 } |
| 60 |
| 61 void processJsInteropAnnotation(Element e) { |
| 62 for (MetadataAnnotation annotation in e.implementation.metadata) { |
| 63 ConstantValue constant = backend.compiler.constants.getConstantValue( |
| 64 annotation.constant); |
| 65 if (constant == null || constant is! ConstructedConstantValue) continue; |
| 66 ConstructedConstantValue constructedConstant = constant; |
| 67 if (constructedConstant.type.element == backend.jsAnnotationClass) { |
| 68 ConstantValue value = constructedConstant.fields[nameField]; |
| 69 if (value.isString) { |
| 70 StringConstantValue stringValue = value; |
| 71 e.setJsInteropName(stringValue.primitiveValue.slowToString()); |
| 72 } else { |
| 73 // TODO(jacobr): report a warning if the value is not a String. |
| 74 e.setJsInteropName(''); |
| 75 } |
| 76 enabledJsInterop = true; |
| 77 return; |
| 78 } |
| 79 } |
| 80 } |
| 81 |
| 82 void processJsInteropAnnotationsInLibrary(LibraryElement library) { |
| 83 processJsInteropAnnotation(library); |
| 84 library.implementation.forEachLocalMember((Element element) { |
| 85 processJsInteropAnnotation(element); |
| 86 if (!element.isClass || !element.isJsInterop) return; |
| 87 |
| 88 ClassElement classElement = element; |
| 89 |
| 90 if (!classElement |
| 91 .implementsInterface(backend.jsJavaScriptObjectClass)) { |
| 92 backend.reporter.reportErrorMessage(classElement, |
| 93 MessageKind.JS_INTEROP_CLASS_CANNOT_EXTEND_DART_CLASS, { |
| 94 'cls': classElement.name, |
| 95 'superclass': classElement.superclass.name |
| 96 }); |
| 97 } |
| 98 |
| 99 classElement.forEachMember( |
| 100 (ClassElement classElement, Element member) { |
| 101 processJsInteropAnnotation(member); |
| 102 |
| 103 if (!member.isSynthesized && |
| 104 classElement.isJsInterop && |
| 105 member is FunctionElement) { |
| 106 FunctionElement fn = member; |
| 107 if (!fn.isExternal && !fn.isAbstract) { |
| 108 backend.reporter.reportErrorMessage( |
| 109 fn, |
| 110 MessageKind.JS_INTEROP_CLASS_NON_EXTERNAL_MEMBER, |
| 111 {'cls': classElement.name, 'member': member.name}); |
| 112 } |
| 113 } |
| 114 }); |
| 115 }); |
| 116 } |
| 117 |
| 118 jsAst.Statement buildJsInteropBootstrap() { |
| 119 if (!enabledJsInterop) return null; |
| 120 List<jsAst.Statement> statements = <jsAst.Statement>[]; |
| 121 backend.compiler.codegenWorld.forEachInvokedName( |
| 122 (String name, Map<Selector, SelectorConstraints> selectors) { |
| 123 selectors.forEach((Selector selector, SelectorConstraints constraints) { |
| 124 if (selector.isClosureCall) { |
| 125 // TODO(jacobr): support named arguments. |
| 126 if (selector.namedArgumentCount > 0) return; |
| 127 int argumentCount = selector.argumentCount; |
| 128 var candidateParameterNames = |
| 129 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMOPQRSTUVWXYZ'; |
| 130 var parameters = new List<String>.generate( |
| 131 argumentCount, (i) => candidateParameterNames[i]); |
| 132 |
| 133 var name = backend.namer.invocationName(selector); |
| 134 statements.add(js.statement( |
| 135 'Function.prototype.# = function(#) { return this(#) }', |
| 136 [name, parameters, parameters])); |
| 137 } |
| 138 }); |
| 139 }); |
| 140 return new jsAst.Block(statements); |
| 141 } |
| 142 } |
OLD | NEW |