Chromium Code Reviews| 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 assert(value.isNull); | |
|
sra1
2015/10/13 03:31:36
@Js(1)
external method();
Should generate an
| |
| 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.compiler.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.compiler.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 |