OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:collection' show HashSet, HashMap; | 7 import 'dart:collection' show HashSet, HashMap; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
61 final HashSet<FieldElement> _fieldsNeedingStorage; | 61 final HashSet<FieldElement> _fieldsNeedingStorage; |
62 | 62 |
63 /// The variable for the target of the current `..` cascade expression. | 63 /// The variable for the target of the current `..` cascade expression. |
64 SimpleIdentifier _cascadeTarget; | 64 SimpleIdentifier _cascadeTarget; |
65 | 65 |
66 /// The variable for the current catch clause | 66 /// The variable for the current catch clause |
67 SimpleIdentifier _catchParameter; | 67 SimpleIdentifier _catchParameter; |
68 | 68 |
69 ConstantEvaluator _constEvaluator; | 69 ConstantEvaluator _constEvaluator; |
70 | 70 |
| 71 /// Imported libraries, and the temporaries used to refer to them. |
| 72 final _imports = new Map<LibraryElement, JS.TemporaryId>(); |
71 final _exports = new Set<String>(); | 73 final _exports = new Set<String>(); |
72 final _lazyFields = <VariableDeclaration>[]; | 74 final _lazyFields = <VariableDeclaration>[]; |
73 final _properties = <FunctionDeclaration>[]; | 75 final _properties = <FunctionDeclaration>[]; |
74 final _privateNames = new HashMap<String, JS.TemporaryId>(); | 76 final _privateNames = new HashMap<String, JS.TemporaryId>(); |
75 final _extensionMethodNames = new HashSet<String>(); | 77 final _extensionMethodNames = new HashSet<String>(); |
76 final _pendingStatements = <JS.Statement>[]; | 78 final _pendingStatements = <JS.Statement>[]; |
77 final _temps = new HashMap<Element, JS.TemporaryId>(); | 79 final _temps = new HashMap<Element, JS.TemporaryId>(); |
78 | 80 |
79 /// The name for the library's exports inside itself. | 81 /// The name for the library's exports inside itself. |
80 /// This much be a constant because we interpolate it into template strings, | |
81 /// and otherwise it would break caching for them. | |
82 /// `exports` was chosen as the most similar to ES module patterns. | 82 /// `exports` was chosen as the most similar to ES module patterns. |
83 final _exportsVar = new JS.TemporaryId('exports'); | 83 final _exportsVar = new JS.TemporaryId('exports'); |
84 final _namedArgTemp = new JS.TemporaryId('opts'); | 84 final _namedArgTemp = new JS.TemporaryId('opts'); |
85 | 85 |
86 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | 86 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or |
87 /// [ClassTypeAlias]. | 87 /// [ClassTypeAlias]. |
88 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); | 88 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); |
89 | 89 |
90 /// Memoized results of [_lazyClass]. | 90 /// Memoized results of [_lazyClass]. |
91 final _lazyClassMemo = new HashMap<Element, bool>(); | 91 final _lazyClassMemo = new HashMap<Element, bool>(); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 | 134 |
135 assert(_pendingClasses.isEmpty); | 135 assert(_pendingClasses.isEmpty); |
136 | 136 |
137 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); | 137 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); |
138 | 138 |
139 // TODO(jmesserly): make these immutable in JS? | 139 // TODO(jmesserly): make these immutable in JS? |
140 for (var name in _exports) { | 140 for (var name in _exports) { |
141 body.add(js.statement('#.# = #;', [_exportsVar, name, name])); | 141 body.add(js.statement('#.# = #;', [_exportsVar, name, name])); |
142 } | 142 } |
143 | 143 |
144 var name = jsLibraryName(libraryInfo.library); | 144 var name = new JS.Identifier(jsLibraryName(currentLibrary)); |
145 | 145 |
146 var defaultValue = js.call(jsDefaultValue); | 146 // TODO(jmesserly): it would be great to run the renamer on the body, |
147 return new JS.Program([ | 147 // then figure out if we really need each of these parameters. |
148 js.statement('var #;', name), | 148 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
149 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [ | 149 var program = [ |
150 _exportsVar, | 150 js.statement('var # = dart.defineLibrary(#, #);', [ |
151 body, | |
152 name, | 151 name, |
153 name, | 152 name, |
154 defaultValue | 153 js.call(jsDefaultValue) |
155 ]) | 154 ]) |
156 ]); | 155 ]; |
| 156 |
| 157 var params = [_exportsVar]; |
| 158 var args = [name]; |
| 159 _imports.forEach((library, temp) { |
| 160 var name = new JS.Identifier(temp.name); |
| 161 params.add(temp); |
| 162 args.add(name); |
| 163 var helper = _libraryMightNotBeLoaded(library) ? 'lazyImport' : 'import'; |
| 164 program.add(js.statement('var # = dart.#(#);', [name, helper, name])); |
| 165 }); |
| 166 |
| 167 program.add(js.statement( |
| 168 "(function(#) { 'use strict'; #; })(#);", [params, body, args])); |
| 169 |
| 170 return new JS.Program(program); |
157 } | 171 } |
158 | 172 |
159 JS.Identifier _initSymbol(JS.Identifier id) { | 173 JS.Identifier _initSymbol(JS.Identifier id) { |
160 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | 174 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); |
161 _pendingStatements.add(s); | 175 _pendingStatements.add(s); |
162 return id; | 176 return id; |
163 } | 177 } |
164 | 178 |
165 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 179 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
166 // until we have better name tracking. | 180 // until we have better name tracking. |
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
470 /// on this library via some transitive import path. | 484 /// on this library via some transitive import path. |
471 /// | 485 /// |
472 /// If we could control the global import ordering, we could eliminate some | 486 /// If we could control the global import ordering, we could eliminate some |
473 /// of these cases, by ordering the imports of the cyclic libraries in an | 487 /// of these cases, by ordering the imports of the cyclic libraries in an |
474 /// optimal way. For example, we could order the libraries in a cycle to | 488 /// optimal way. For example, we could order the libraries in a cycle to |
475 /// minimize laziness. However, we currently assume we cannot control the | 489 /// minimize laziness. However, we currently assume we cannot control the |
476 /// order that the cycle of libraries will be loaded in. | 490 /// order that the cycle of libraries will be loaded in. |
477 bool _typeMightNotBeLoaded(DartType type) { | 491 bool _typeMightNotBeLoaded(DartType type) { |
478 var library = type.element.library; | 492 var library = type.element.library; |
479 if (library == currentLibrary) return _lazyClass(type); | 493 if (library == currentLibrary) return _lazyClass(type); |
| 494 return _libraryMightNotBeLoaded(library); |
| 495 } |
480 | 496 |
| 497 bool _libraryMightNotBeLoaded(LibraryElement library) { |
481 // The SDK is a special case: we optimize the order to prevent laziness. | 498 // The SDK is a special case: we optimize the order to prevent laziness. |
482 if (library.isInSdk) { | 499 if (library.isInSdk) { |
483 // SDK is loaded before non-SDK libraies | 500 // SDK is loaded before non-SDK libraies |
484 if (!currentLibrary.isInSdk) return false; | 501 if (!currentLibrary.isInSdk) return false; |
485 | 502 |
486 // Compute the order of both SDK libraries. If unknown, assume it's after. | 503 // Compute the order of both SDK libraries. If unknown, assume it's after. |
487 var classOrder = corelibOrder.indexOf(library.name); | 504 var classOrder = corelibOrder.indexOf(library.name); |
488 if (classOrder == -1) classOrder = corelibOrder.length; | 505 if (classOrder == -1) classOrder = corelibOrder.length; |
489 | 506 |
490 var currentOrder = corelibOrder.indexOf(currentLibrary.name); | 507 var currentOrder = corelibOrder.indexOf(currentLibrary.name); |
(...skipping 603 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1094 } | 1111 } |
1095 | 1112 |
1096 // Get the original declaring element. If we had a property accessor, this | 1113 // Get the original declaring element. If we had a property accessor, this |
1097 // indirects back to a (possibly synthetic) field. | 1114 // indirects back to a (possibly synthetic) field. |
1098 var element = accessor; | 1115 var element = accessor; |
1099 if (element is PropertyAccessorElement) element = accessor.variable; | 1116 if (element is PropertyAccessorElement) element = accessor.variable; |
1100 var name = element.name; | 1117 var name = element.name; |
1101 | 1118 |
1102 // library member | 1119 // library member |
1103 if (element.enclosingElement is CompilationUnitElement && | 1120 if (element.enclosingElement is CompilationUnitElement && |
1104 (element.library != libraryInfo.library || | 1121 (element.library != currentLibrary || |
1105 element is TopLevelVariableElement && !element.isConst)) { | 1122 element is TopLevelVariableElement && !element.isConst)) { |
1106 var memberName = _emitMemberName(name, isStatic: true); | 1123 var memberName = _emitMemberName(name, isStatic: true); |
1107 return js.call('#.#', [_libraryName(element.library), memberName]); | 1124 return js.call('#.#', [_libraryName(element.library), memberName]); |
1108 } | 1125 } |
1109 | 1126 |
1110 // Unqualified class member. This could mean implicit-this, or implicit | 1127 // Unqualified class member. This could mean implicit-this, or implicit |
1111 // call to a static from the same class. | 1128 // call to a static from the same class. |
1112 if (element is ClassMemberElement && element is! ConstructorElement) { | 1129 if (element is ClassMemberElement && element is! ConstructorElement) { |
1113 bool isStatic = element.isStatic; | 1130 bool isStatic = element.isStatic; |
1114 var type = element.enclosingElement.type; | 1131 var type = element.enclosingElement.type; |
(...skipping 1121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2236 | 2253 |
2237 @override | 2254 @override |
2238 visitNullLiteral(NullLiteral node) => new JS.LiteralNull(); | 2255 visitNullLiteral(NullLiteral node) => new JS.LiteralNull(); |
2239 | 2256 |
2240 @override | 2257 @override |
2241 visitSymbolLiteral(SymbolLiteral node) { | 2258 visitSymbolLiteral(SymbolLiteral node) { |
2242 // TODO(vsm): When we canonicalize, we need to treat private symbols | 2259 // TODO(vsm): When we canonicalize, we need to treat private symbols |
2243 // correctly. | 2260 // correctly. |
2244 var name = js.string(node.components.join('.'), "'"); | 2261 var name = js.string(node.components.join('.'), "'"); |
2245 var nameHint = 'symbol_' + node.components.join('_'); | 2262 var nameHint = 'symbol_' + node.components.join('_'); |
2246 return _const(node, js.call('new core.Symbol(#)', name), nameHint); | 2263 return _const( |
| 2264 node, new JS.New(_emitTypeName(types.symbolType), [name]), nameHint); |
2247 } | 2265 } |
2248 | 2266 |
2249 @override | 2267 @override |
2250 visitListLiteral(ListLiteral node) { | 2268 visitListLiteral(ListLiteral node) { |
2251 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); | 2269 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); |
2252 | 2270 |
2253 ParameterizedType type = node.staticType; | 2271 ParameterizedType type = node.staticType; |
2254 if (type.typeArguments.any((a) => a != types.dynamicType)) { | 2272 if (type.typeArguments.any((a) => a != types.dynamicType)) { |
2255 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); | 2273 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); |
2256 } | 2274 } |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2485 bool _externalOrNative(node) => | 2503 bool _externalOrNative(node) => |
2486 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2504 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
2487 | 2505 |
2488 FunctionBody _functionBody(node) => | 2506 FunctionBody _functionBody(node) => |
2489 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2507 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
2490 | 2508 |
2491 /// Choose a canonical name from the library element. | 2509 /// Choose a canonical name from the library element. |
2492 /// This never uses the library's name (the identifier in the `library` | 2510 /// This never uses the library's name (the identifier in the `library` |
2493 /// declaration) as it doesn't have any meaningful rules enforced. | 2511 /// declaration) as it doesn't have any meaningful rules enforced. |
2494 JS.Identifier _libraryName(LibraryElement library) { | 2512 JS.Identifier _libraryName(LibraryElement library) { |
2495 if (library == libraryInfo.library) return _exportsVar; | 2513 if (library == currentLibrary) return _exportsVar; |
2496 return new JS.Identifier(jsLibraryName(library)); | 2514 return _imports.putIfAbsent( |
| 2515 library, () => new JS.TemporaryId(jsLibraryName(library))); |
2497 } | 2516 } |
2498 | 2517 |
2499 DartType getStaticType(Expression e) => rules.getStaticType(e); | 2518 DartType getStaticType(Expression e) => rules.getStaticType(e); |
2500 } | 2519 } |
2501 | 2520 |
2502 class JSGenerator extends CodeGenerator { | 2521 class JSGenerator extends CodeGenerator { |
2503 final CompilerOptions options; | 2522 final CompilerOptions options; |
2504 | 2523 |
2505 /// For fast lookup of extension methods, we first check the name, then do a | 2524 /// For fast lookup of extension methods, we first check the name, then do a |
2506 /// (possibly expensive) subtype test to see if it matches one of the types | 2525 /// (possibly expensive) subtype test to see if it matches one of the types |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2570 // TODO(jmesserly): validate the library. See issue #135. | 2589 // TODO(jmesserly): validate the library. See issue #135. |
2571 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2590 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2572 | 2591 |
2573 bool _isJsPeerInterface(DartObjectImpl value) => | 2592 bool _isJsPeerInterface(DartObjectImpl value) => |
2574 value.type.name == 'JsPeerInterface'; | 2593 value.type.name == 'JsPeerInterface'; |
2575 | 2594 |
2576 // TODO(jacobr): we would like to do something like the following | 2595 // TODO(jacobr): we would like to do something like the following |
2577 // but we don't have summary support yet. | 2596 // but we don't have summary support yet. |
2578 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2597 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2579 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2598 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
OLD | NEW |