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, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
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; |
11 import 'package:analyzer/src/generated/constant.dart'; | 11 import 'package:analyzer/src/generated/constant.dart'; |
12 import 'package:analyzer/src/generated/element.dart'; | 12 import 'package:analyzer/src/generated/element.dart'; |
13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
14 import 'package:analyzer/src/generated/scanner.dart' | 14 import 'package:analyzer/src/generated/scanner.dart' |
15 show StringToken, Token, TokenType; | 15 show StringToken, Token, TokenType; |
16 import 'package:analyzer/src/generated/type_system.dart' | 16 import 'package:analyzer/src/generated/type_system.dart' |
17 show StrongTypeSystemImpl; | 17 show StrongTypeSystemImpl; |
18 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; | 18 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; |
19 | 19 |
20 import 'ast_builder.dart' show AstBuilder; | 20 import 'ast_builder.dart' show AstBuilder; |
21 import 'reify_coercions.dart' show CoercionReifier, Tuple2; | 21 import 'reify_coercions.dart' show CoercionReifier, Tuple2; |
22 | 22 |
23 // TODO(jmesserly): import from its own package | 23 // TODO(jmesserly): import from its own package |
24 import '../js/js_ast.dart' as JS; | 24 import '../js/js_ast.dart' as JS; |
25 import '../js/js_ast.dart' show js; | 25 import '../js/js_ast.dart' show js; |
26 | 26 |
27 import '../closure/closure_annotator.dart' show ClosureAnnotator; | 27 import '../closure/closure_annotator.dart' show ClosureAnnotator; |
28 import '../compiler.dart' show AbstractCompiler; | 28 import '../compiler.dart' |
| 29 show AbstractCompiler, corelibOrder, getCorelibModuleName; |
29 import '../info.dart'; | 30 import '../info.dart'; |
30 import '../options.dart' show CodegenOptions; | 31 import '../options.dart' show CodegenOptions; |
31 import '../utils.dart'; | 32 import '../utils.dart'; |
32 | 33 |
33 import 'code_generator.dart'; | 34 import 'code_generator.dart'; |
34 import 'js_field_storage.dart'; | 35 import 'js_field_storage.dart'; |
35 import 'js_interop.dart'; | 36 import 'js_interop.dart'; |
36 import 'js_names.dart' as JS; | 37 import 'js_names.dart' as JS; |
37 import 'js_metalet.dart' as JS; | 38 import 'js_metalet.dart' as JS; |
38 import 'js_module_item_order.dart'; | 39 import 'js_module_item_order.dart'; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 Expression _cascadeTarget; | 75 Expression _cascadeTarget; |
75 | 76 |
76 /// The variable for the current catch clause | 77 /// The variable for the current catch clause |
77 SimpleIdentifier _catchParameter; | 78 SimpleIdentifier _catchParameter; |
78 | 79 |
79 /// In an async* function, this represents the stream controller parameter. | 80 /// In an async* function, this represents the stream controller parameter. |
80 JS.TemporaryId _asyncStarController; | 81 JS.TemporaryId _asyncStarController; |
81 | 82 |
82 /// Imported libraries, and the temporaries used to refer to them. | 83 /// Imported libraries, and the temporaries used to refer to them. |
83 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 84 final _imports = new Map<LibraryElement, JS.TemporaryId>(); |
84 final _exports = new Set<String>(); | 85 final _exports = <String, String>{}; |
85 final _properties = <FunctionDeclaration>[]; | 86 final _properties = <FunctionDeclaration>[]; |
86 final _privateNames = new HashMap<String, JS.TemporaryId>(); | 87 final _privateNames = new HashMap<String, JS.TemporaryId>(); |
87 final _moduleItems = <JS.Statement>[]; | 88 final _moduleItems = <JS.Statement>[]; |
88 final _temps = new HashMap<Element, JS.TemporaryId>(); | 89 final _temps = new HashMap<Element, JS.TemporaryId>(); |
89 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); | 90 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); |
90 | 91 |
91 /// The name for the library's exports inside itself. | 92 /// The name for the library's exports inside itself. |
92 /// `exports` was chosen as the most similar to ES module patterns. | 93 /// `exports` was chosen as the most similar to ES module patterns. |
93 final _dartxVar = new JS.Identifier('dartx'); | 94 final _dartxVar = new JS.Identifier('dartx'); |
94 final _exportsVar = new JS.TemporaryId('exports'); | 95 final _exportsVar = new JS.TemporaryId('exports'); |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 var id = elementIdPairs.e1; | 178 var id = elementIdPairs.e1; |
178 id.setQualified(!_loader.isLoaded(element)); | 179 id.setQualified(!_loader.isLoaded(element)); |
179 } | 180 } |
180 | 181 |
181 var moduleBuilder = new ModuleBuilder(options.moduleFormat); | 182 var moduleBuilder = new ModuleBuilder(options.moduleFormat); |
182 | 183 |
183 _exports.forEach(moduleBuilder.addExport); | 184 _exports.forEach(moduleBuilder.addExport); |
184 | 185 |
185 var items = <JS.ModuleItem>[]; | 186 var items = <JS.ModuleItem>[]; |
186 if (!_isDartRuntime) { | 187 if (!_isDartRuntime) { |
| 188 if (currentLibrary.source.isInSystemLibrary) { |
| 189 // Force the import order of runtime libs. |
| 190 // TODO(ochafik): Reduce this to a minimum. |
| 191 for (var lib in corelibOrder.reversed) { |
| 192 // TODO(ochafik): Use uris instead in corelibOrder. |
| 193 moduleBuilder.addImport(getCorelibModuleName(lib), null); |
| 194 } |
| 195 } |
187 moduleBuilder.addImport('dart/_runtime', _runtimeLibVar); | 196 moduleBuilder.addImport('dart/_runtime', _runtimeLibVar); |
188 | 197 |
189 var dartxImport = | 198 var dartxImport = |
190 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); | 199 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); |
191 items.add(dartxImport); | 200 items.add(dartxImport); |
192 } | 201 } |
193 items.addAll(_moduleItems); | 202 items.addAll(_moduleItems); |
194 | 203 |
195 _imports.forEach((LibraryElement lib, JS.TemporaryId temp) { | 204 _imports.forEach((LibraryElement lib, JS.TemporaryId temp) { |
196 moduleBuilder.addImport(compiler.getModuleName(lib.source.uri), temp, | 205 moduleBuilder.addImport(compiler.getModuleName(lib.source.uri), temp, |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 .where((s) => !currentLibNames.containsKey(s)) | 266 .where((s) => !currentLibNames.containsKey(s)) |
258 .map((s) => js.string(s, "'"))); | 267 .map((s) => js.string(s, "'"))); |
259 } | 268 } |
260 if (hide != null) { | 269 if (hide != null) { |
261 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); | 270 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); |
262 } | 271 } |
263 args.add(new JS.ArrayInitializer(shownNames)); | 272 args.add(new JS.ArrayInitializer(shownNames)); |
264 args.add(new JS.ArrayInitializer(hiddenNames)); | 273 args.add(new JS.ArrayInitializer(hiddenNames)); |
265 } | 274 } |
266 | 275 |
267 // When we compile _runtime.js, we need to source export_ from _utils.js: | |
268 _moduleItems.add(js.statement('dart.export(#);', [args])); | 276 _moduleItems.add(js.statement('dart.export(#);', [args])); |
269 } | 277 } |
270 | 278 |
271 JS.Identifier _initSymbol(JS.Identifier id) { | 279 JS.Identifier _initSymbol(JS.Identifier id) { |
272 var s = | 280 var s = |
273 js.statement('const # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | 281 js.statement('const # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); |
274 _moduleItems.add(s); | 282 _moduleItems.add(s); |
275 return id; | 283 return id; |
276 } | 284 } |
277 | 285 |
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
531 var genericInst = _emitTypeName(dynType, lowerGeneric: true); | 539 var genericInst = _emitTypeName(dynType, lowerGeneric: true); |
532 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | 540 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); |
533 } | 541 } |
534 return body; | 542 return body; |
535 } | 543 } |
536 | 544 |
537 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { | 545 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { |
538 var name = type.name; | 546 var name = type.name; |
539 var genericName = '$name\$'; | 547 var genericName = '$name\$'; |
540 var typeParams = _typeFormalsOf(type).map((p) => p.name); | 548 var typeParams = _typeFormalsOf(type).map((p) => p.name); |
541 if (isPublic(name)) _exports.add(genericName); | 549 if (isPublic(name)) _addExport(genericName); |
542 return js.statement('const # = dart.generic(function(#) { #; return #; });', | 550 return js.statement('const # = dart.generic(function(#) { #; return #; });', |
543 [genericName, typeParams, body, name]); | 551 [genericName, typeParams, body, name]); |
544 } | 552 } |
545 | 553 |
546 final _hasDeferredSupertype = new HashSet<ClassElement>(); | 554 final _hasDeferredSupertype = new HashSet<ClassElement>(); |
547 | 555 |
548 bool _deferIfNeeded(DartType type, ClassElement current) { | 556 bool _deferIfNeeded(DartType type, ClassElement current) { |
549 if (type is ParameterizedType) { | 557 if (type is ParameterizedType) { |
550 var typeArguments = type.typeArguments; | 558 var typeArguments = type.typeArguments; |
551 for (var typeArg in typeArguments) { | 559 for (var typeArg in typeArguments) { |
(...skipping 743 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1295 | 1303 |
1296 if (node.isGetter || node.isSetter) { | 1304 if (node.isGetter || node.isSetter) { |
1297 // Add these later so we can use getter/setter syntax. | 1305 // Add these later so we can use getter/setter syntax. |
1298 _properties.add(node); | 1306 _properties.add(node); |
1299 return null; | 1307 return null; |
1300 } | 1308 } |
1301 | 1309 |
1302 var body = <JS.Statement>[]; | 1310 var body = <JS.Statement>[]; |
1303 _flushLibraryProperties(body); | 1311 _flushLibraryProperties(body); |
1304 | 1312 |
1305 var name = _getJSExportName(node.element) ?? node.name.name; | 1313 var name = node.name.name; |
1306 | 1314 |
1307 var fn = _visit(node.functionExpression); | 1315 var fn = _visit(node.functionExpression); |
1308 | 1316 |
1309 if (currentLibrary.source.isInSystemLibrary && | 1317 if (currentLibrary.source.isInSystemLibrary && |
1310 _isInlineJSFunction(node.functionExpression)) { | 1318 _isInlineJSFunction(node.functionExpression)) { |
1311 fn = _simplifyPassThroughArrowFunCallBody(fn); | 1319 fn = _simplifyPassThroughArrowFunCallBody(fn); |
1312 } | 1320 } |
1313 | 1321 |
1314 var id = new JS.Identifier(name); | 1322 var id = new JS.Identifier(name); |
1315 body.add(annotate(new JS.FunctionDeclaration(id, fn), node.element)); | 1323 body.add(annotate(new JS.FunctionDeclaration(id, fn), node.element)); |
1316 if (!_isDartRuntime) { | 1324 if (!_isDartRuntime) { |
1317 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) | 1325 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) |
1318 .toStatement()); | 1326 .toStatement()); |
1319 } | 1327 } |
1320 | 1328 |
1321 if (isPublic(name)) _addExport(name); | 1329 if (isPublic(name)) { |
| 1330 _addExport(name, _getJSExportName(node.element) ?? name); |
| 1331 } |
1322 return _statement(body); | 1332 return _statement(body); |
1323 } | 1333 } |
1324 | 1334 |
1325 bool _isInlineJSFunction(FunctionExpression functionExpression) { | 1335 bool _isInlineJSFunction(FunctionExpression functionExpression) { |
1326 var body = functionExpression.body; | 1336 var body = functionExpression.body; |
1327 if (body is ExpressionFunctionBody) { | 1337 if (body is ExpressionFunctionBody) { |
1328 return _isJSInvocation(body.expression); | 1338 return _isJSInvocation(body.expression); |
1329 } else if (body is BlockFunctionBody) { | 1339 } else if (body is BlockFunctionBody) { |
1330 if (body.block.statements.length == 1) { | 1340 if (body.block.statements.length == 1) { |
1331 var stat = body.block.statements.single; | 1341 var stat = body.block.statements.single; |
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1744 var genericName = _emitTopLevelName(element, suffix: '\$'); | 1754 var genericName = _emitTopLevelName(element, suffix: '\$'); |
1745 return js.call('#(#)', [genericName, jsArgs]); | 1755 return js.call('#(#)', [genericName, jsArgs]); |
1746 } | 1756 } |
1747 } | 1757 } |
1748 | 1758 |
1749 return _emitTopLevelName(element); | 1759 return _emitTopLevelName(element); |
1750 } | 1760 } |
1751 | 1761 |
1752 JS.Expression _emitTopLevelName(Element e, {String suffix: ''}) { | 1762 JS.Expression _emitTopLevelName(Element e, {String suffix: ''}) { |
1753 var libName = _libraryName(e.library); | 1763 var libName = _libraryName(e.library); |
1754 var nameExpr = _propertyName((_getJSExportName(e) ?? e.name) + suffix); | |
1755 | 1764 |
1756 // Always qualify: | 1765 // Always qualify: |
1757 // * mutable top-level fields | 1766 // * mutable top-level fields |
1758 // * elements from other libraries | 1767 // * elements from other libraries |
1759 bool mutableTopLevel = e is TopLevelVariableElement && | 1768 bool mutableTopLevel = e is TopLevelVariableElement && |
1760 !e.isConst && | 1769 !e.isConst && |
1761 !_isFinalJSDecl(e.computeNode()); | 1770 !_isFinalJSDecl(e.computeNode()); |
1762 bool fromAnotherLibrary = e.library != currentLibrary; | 1771 bool fromAnotherLibrary = e.library != currentLibrary; |
| 1772 var nameExpr; |
| 1773 if (fromAnotherLibrary) { |
| 1774 nameExpr = _propertyName((_getJSExportName(e) ?? e.name) + suffix); |
| 1775 } else { |
| 1776 nameExpr = _propertyName(e.name + suffix); |
| 1777 } |
1763 if (mutableTopLevel || fromAnotherLibrary) { | 1778 if (mutableTopLevel || fromAnotherLibrary) { |
1764 return new JS.PropertyAccess(libName, nameExpr); | 1779 return new JS.PropertyAccess(libName, nameExpr); |
1765 } | 1780 } |
1766 | 1781 |
1767 var id = new JS.MaybeQualifiedId(libName, nameExpr); | 1782 var id = new JS.MaybeQualifiedId(libName, nameExpr); |
1768 _qualifiedIds.add(new Tuple2(e, id)); | 1783 _qualifiedIds.add(new Tuple2(e, id)); |
1769 return id; | 1784 return id; |
1770 } | 1785 } |
1771 | 1786 |
1772 @override | 1787 @override |
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2130 return new JS.Yield(_visit(node.expression)); | 2145 return new JS.Yield(_visit(node.expression)); |
2131 } | 2146 } |
2132 | 2147 |
2133 @override | 2148 @override |
2134 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 2149 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
2135 for (var v in node.variables.variables) { | 2150 for (var v in node.variables.variables) { |
2136 _loader.loadDeclaration(v, v.element); | 2151 _loader.loadDeclaration(v, v.element); |
2137 } | 2152 } |
2138 } | 2153 } |
2139 | 2154 |
2140 _addExport(String name) { | 2155 /// Emits static fields. |
2141 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; | 2156 /// |
| 2157 /// Instance fields are emitted in [_initializeFields]. |
| 2158 /// |
| 2159 /// These are generally treated the same as top-level fields, see |
| 2160 /// [visitTopLevelVariableDeclaration]. |
| 2161 @override |
| 2162 visitFieldDeclaration(FieldDeclaration node) { |
| 2163 if (!node.isStatic) return; |
| 2164 |
| 2165 for (var f in node.fields.variables) { |
| 2166 _loader.loadDeclaration(f, f.element); |
| 2167 } |
| 2168 } |
| 2169 |
| 2170 _addExport(String name, [String exportName]) { |
| 2171 if (_exports.containsKey(name)) { |
| 2172 throw 'Duplicate top level name found: $name'; |
| 2173 } |
| 2174 _exports[name] = exportName ?? name; |
2142 } | 2175 } |
2143 | 2176 |
2144 @override | 2177 @override |
2145 JS.Statement visitVariableDeclarationStatement( | 2178 JS.Statement visitVariableDeclarationStatement( |
2146 VariableDeclarationStatement node) { | 2179 VariableDeclarationStatement node) { |
2147 // Special case a single variable with an initializer. | 2180 // Special case a single variable with an initializer. |
2148 // This helps emit cleaner code for things like: | 2181 // This helps emit cleaner code for things like: |
2149 // var result = []..add(1)..add(2); | 2182 // var result = []..add(1)..add(2); |
2150 if (node.variables.variables.length == 1) { | 2183 if (node.variables.variables.length == 1) { |
2151 var v = node.variables.variables.single; | 2184 var v = node.variables.variables.single; |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2240 // later on when we emit lazy fields. That seems busted. | 2273 // later on when we emit lazy fields. That seems busted. |
2241 jsInit = _visitInitializer(field); | 2274 jsInit = _visitInitializer(field); |
2242 eagerInit = false; | 2275 eagerInit = false; |
2243 } | 2276 } |
2244 | 2277 |
2245 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 2278 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile |
2246 // runtime helpers. | 2279 // runtime helpers. |
2247 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | 2280 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); |
2248 if (isJSTopLevel) eagerInit = true; | 2281 if (isJSTopLevel) eagerInit = true; |
2249 | 2282 |
2250 var fieldName = _getJSExportName(element) ?? field.name.name; | 2283 var fieldName = field.name.name; |
2251 if (field.isConst && eagerInit || isJSTopLevel) { | 2284 var exportName = fieldName; |
| 2285 if (element is TopLevelVariableElement) { |
| 2286 exportName = _getJSExportName(element) ?? fieldName; |
| 2287 } |
| 2288 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || |
| 2289 isJSTopLevel) { |
2252 // constant fields don't change, so we can generate them as `let` | 2290 // constant fields don't change, so we can generate them as `let` |
2253 // but add them to the module's exports. However, make sure we generate | 2291 // but add them to the module's exports. However, make sure we generate |
2254 // anything they depend on first. | 2292 // anything they depend on first. |
2255 if (isPublic(fieldName)) _addExport(fieldName); | 2293 |
| 2294 if (isPublic(fieldName)) _addExport(fieldName, exportName); |
2256 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; | 2295 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; |
2257 return annotateVariable( | 2296 return annotateVariable( |
2258 js.statement( | 2297 js.statement( |
2259 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]), | 2298 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]), |
2260 field.element); | 2299 field.element); |
2261 } | 2300 } |
2262 | 2301 |
2263 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { | 2302 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { |
2264 return annotateVariable( | 2303 return annotateVariable( |
2265 js.statement('# = #;', [_visit(field.name), jsInit]), field.element); | 2304 js.statement('# = #;', [_visit(field.name), jsInit]), field.element); |
(...skipping 1283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3549 | 3588 |
3550 /// A special kind of element created by the compiler, signifying a temporary | 3589 /// A special kind of element created by the compiler, signifying a temporary |
3551 /// variable. These objects use instance equality, and should be shared | 3590 /// variable. These objects use instance equality, and should be shared |
3552 /// everywhere in the tree where they are treated as the same variable. | 3591 /// everywhere in the tree where they are treated as the same variable. |
3553 class TemporaryVariableElement extends LocalVariableElementImpl { | 3592 class TemporaryVariableElement extends LocalVariableElementImpl { |
3554 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3593 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3555 | 3594 |
3556 int get hashCode => identityHashCode(this); | 3595 int get hashCode => identityHashCode(this); |
3557 bool operator ==(Object other) => identical(this, other); | 3596 bool operator ==(Object other) => identical(this, other); |
3558 } | 3597 } |
OLD | NEW |