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; |
(...skipping 19 matching lines...) Expand all Loading... |
30 import '../utils.dart'; | 30 import '../utils.dart'; |
31 | 31 |
32 import 'code_generator.dart'; | 32 import 'code_generator.dart'; |
33 import 'js_field_storage.dart'; | 33 import 'js_field_storage.dart'; |
34 import 'js_interop.dart'; | 34 import 'js_interop.dart'; |
35 import 'js_names.dart' as JS; | 35 import 'js_names.dart' as JS; |
36 import 'js_metalet.dart' as JS; | 36 import 'js_metalet.dart' as JS; |
37 import 'js_module_item_order.dart'; | 37 import 'js_module_item_order.dart'; |
38 import 'js_printer.dart' show writeJsLibrary; | 38 import 'js_printer.dart' show writeJsLibrary; |
39 import 'side_effect_analysis.dart'; | 39 import 'side_effect_analysis.dart'; |
| 40 import 'package:collection/equality.dart'; |
40 | 41 |
41 // Various dynamic helpers we call. | 42 // Various dynamic helpers we call. |
42 // If renaming these, make sure to check other places like the | 43 // If renaming these, make sure to check other places like the |
43 // _runtime.js file and comments. | 44 // _runtime.js file and comments. |
44 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 45 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can |
45 // import and generate calls to, rather than dart_runtime.js | 46 // import and generate calls to, rather than dart_runtime.js |
46 const DPUT = 'dput'; | 47 const DPUT = 'dput'; |
47 const DLOAD = 'dload'; | 48 const DLOAD = 'dload'; |
48 const DINDEX = 'dindex'; | 49 const DINDEX = 'dindex'; |
49 const DSETINDEX = 'dsetindex'; | 50 const DSETINDEX = 'dsetindex'; |
50 const DCALL = 'dcall'; | 51 const DCALL = 'dcall'; |
51 const DSEND = 'dsend'; | 52 const DSEND = 'dsend'; |
52 | 53 |
| 54 const ListEquality _listEquality = const ListEquality(); |
| 55 |
53 class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { | 56 class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
54 final AbstractCompiler compiler; | 57 final AbstractCompiler compiler; |
55 final CodegenOptions options; | 58 final CodegenOptions options; |
56 final TypeRules rules; | 59 final TypeRules rules; |
57 final LibraryElement currentLibrary; | 60 final LibraryElement currentLibrary; |
58 | 61 |
59 /// The global extension type table. | 62 /// The global extension type table. |
60 final HashSet<ClassElement> _extensionTypes; | 63 final HashSet<ClassElement> _extensionTypes; |
61 | 64 |
62 /// Information that is precomputed for this library, indicates which fields | 65 /// Information that is precomputed for this library, indicates which fields |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 ConstFieldVisitor _constField; | 100 ConstFieldVisitor _constField; |
98 | 101 |
99 ModuleItemLoadOrder _loader; | 102 ModuleItemLoadOrder _loader; |
100 | 103 |
101 /// _interceptors.JSArray<E>, used for List literals. | 104 /// _interceptors.JSArray<E>, used for List literals. |
102 ClassElement _jsArray; | 105 ClassElement _jsArray; |
103 | 106 |
104 /// The default value of the module object. See [visitLibraryDirective]. | 107 /// The default value of the module object. See [visitLibraryDirective]. |
105 String _jsModuleValue; | 108 String _jsModuleValue; |
106 | 109 |
| 110 bool _isDartUtils; |
| 111 |
107 Map<String, DartType> _objectMembers; | 112 Map<String, DartType> _objectMembers; |
108 | 113 |
109 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, | 114 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, |
110 this._extensionTypes, this._fieldsNeedingStorage) | 115 this._extensionTypes, this._fieldsNeedingStorage) |
111 : compiler = compiler, | 116 : compiler = compiler, |
112 options = compiler.options.codegenOptions, | 117 options = compiler.options.codegenOptions, |
113 _types = compiler.context.typeProvider { | 118 _types = compiler.context.typeProvider { |
114 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 119 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
115 | 120 |
116 var context = compiler.context; | 121 var context = compiler.context; |
117 var src = context.sourceFactory.forUri('dart:_interceptors'); | 122 var src = context.sourceFactory.forUri('dart:_interceptors'); |
118 var interceptors = context.computeLibraryElement(src); | 123 var interceptors = context.computeLibraryElement(src); |
119 _jsArray = interceptors.getType('JSArray'); | 124 _jsArray = interceptors.getType('JSArray'); |
| 125 _isDartUtils = currentLibrary.source.uri.toString() == 'dart:_utils'; |
120 | 126 |
121 _objectMembers = getObjectMemberMap(types); | 127 _objectMembers = getObjectMemberMap(types); |
122 } | 128 } |
123 | 129 |
124 TypeProvider get types => rules.provider; | 130 TypeProvider get types => rules.provider; |
125 | 131 |
126 JS.Program emitLibrary(LibraryUnit library) { | 132 JS.Program emitLibrary(LibraryUnit library) { |
127 // Modify the AST to make coercions explicit. | 133 // Modify the AST to make coercions explicit. |
128 new CoercionReifier(library, rules).reify(); | 134 new CoercionReifier(library, rules).reify(); |
129 | 135 |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 // TODO(jmesserly): it would be great to run the renamer on the body, | 195 // TODO(jmesserly): it would be great to run the renamer on the body, |
190 // then figure out if we really need each of these parameters. | 196 // then figure out if we really need each of these parameters. |
191 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 | 197 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
192 var params = [_exportsVar, _runtimeLibVar]; | 198 var params = [_exportsVar, _runtimeLibVar]; |
193 var processImport = | 199 var processImport = |
194 (LibraryElement library, JS.TemporaryId temp, List list) { | 200 (LibraryElement library, JS.TemporaryId temp, List list) { |
195 params.add(temp); | 201 params.add(temp); |
196 list.add(js.string(compiler.getModuleName(library.source.uri), "'")); | 202 list.add(js.string(compiler.getModuleName(library.source.uri), "'")); |
197 }; | 203 }; |
198 | 204 |
199 var imports = <JS.Expression>[js.string('dart/_runtime')]; | 205 var needsDartRuntime = !_isDartUtils; |
| 206 |
| 207 var imports = <JS.Expression>[]; |
| 208 var moduleStatements = <JS.Statement>[]; |
| 209 if (needsDartRuntime) { |
| 210 imports.add(js.string('dart/_runtime')); |
| 211 |
| 212 var dartxImport = |
| 213 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); |
| 214 moduleStatements.add(dartxImport); |
| 215 } |
| 216 moduleStatements.addAll(_moduleItems); |
| 217 |
200 _imports.forEach((library, temp) { | 218 _imports.forEach((library, temp) { |
201 if (_loader.libraryIsLoaded(library)) { | 219 if (_loader.libraryIsLoaded(library)) { |
202 processImport(library, temp, imports); | 220 processImport(library, temp, imports); |
203 } | 221 } |
204 }); | 222 }); |
205 | 223 |
206 var lazyImports = <JS.Expression>[]; | 224 var lazyImports = <JS.Expression>[]; |
207 _imports.forEach((library, temp) { | 225 _imports.forEach((library, temp) { |
208 if (!_loader.libraryIsLoaded(library)) { | 226 if (!_loader.libraryIsLoaded(library)) { |
209 processImport(library, temp, lazyImports); | 227 processImport(library, temp, lazyImports); |
210 } | 228 } |
211 }); | 229 }); |
212 | 230 |
213 var dartxImport = | 231 var module = |
214 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); | 232 js.call("function(#) { 'use strict'; #; }", [params, moduleStatements]); |
215 | |
216 var module = js.call("function(#) { 'use strict'; #; #; }", | |
217 [params, dartxImport, _moduleItems]); | |
218 | 233 |
219 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ | 234 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ |
220 js.string(jsPath, "'"), | 235 js.string(jsPath, "'"), |
221 _jsModuleValue ?? new JS.LiteralNull(), | 236 _jsModuleValue ?? new JS.LiteralNull(), |
222 js.commentExpression( | 237 js.commentExpression( |
223 "Imports", new JS.ArrayInitializer(imports, multiline: true)), | 238 "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
224 js.commentExpression("Lazy imports", | 239 js.commentExpression("Lazy imports", |
225 new JS.ArrayInitializer(lazyImports, multiline: true)), | 240 new JS.ArrayInitializer(lazyImports, multiline: true)), |
226 module | 241 module |
227 ]); | 242 ]); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
282 .map((i) => i.name) | 297 .map((i) => i.name) |
283 .where((s) => !currentLibNames.containsKey(s)) | 298 .where((s) => !currentLibNames.containsKey(s)) |
284 .map((s) => js.string(s, "'"))); | 299 .map((s) => js.string(s, "'"))); |
285 } | 300 } |
286 if (hide != null) { | 301 if (hide != null) { |
287 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); | 302 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); |
288 } | 303 } |
289 args.add(new JS.ArrayInitializer(shownNames)); | 304 args.add(new JS.ArrayInitializer(shownNames)); |
290 args.add(new JS.ArrayInitializer(hiddenNames)); | 305 args.add(new JS.ArrayInitializer(hiddenNames)); |
291 } | 306 } |
292 _moduleItems.add(js.statement('dart.export(#);', [args])); | 307 _moduleItems.add(js.statement('dart.export_(#);', [args])); |
293 } | 308 } |
294 | 309 |
295 JS.Identifier _initSymbol(JS.Identifier id) { | 310 JS.Identifier _initSymbol(JS.Identifier id) { |
296 var s = | 311 var s = |
297 js.statement('const # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | 312 js.statement('const # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); |
298 _moduleItems.add(s); | 313 _moduleItems.add(s); |
299 return id; | 314 return id; |
300 } | 315 } |
301 | 316 |
302 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 317 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
(...skipping 975 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1278 // Add these later so we can use getter/setter syntax. | 1293 // Add these later so we can use getter/setter syntax. |
1279 _properties.add(node); | 1294 _properties.add(node); |
1280 return null; | 1295 return null; |
1281 } | 1296 } |
1282 | 1297 |
1283 var body = <JS.Statement>[]; | 1298 var body = <JS.Statement>[]; |
1284 _flushLibraryProperties(body); | 1299 _flushLibraryProperties(body); |
1285 | 1300 |
1286 var name = node.name.name; | 1301 var name = node.name.name; |
1287 | 1302 |
| 1303 var fn = _visit(node.functionExpression); |
| 1304 bool needsTagging = true; |
| 1305 |
| 1306 if (currentLibrary.source.isInSystemLibrary && |
| 1307 _isInlineJSFunction(node.functionExpression)) { |
| 1308 fn = _simplifyPassThroughArrowFunCallBody(fn); |
| 1309 needsTagging = !_isDartUtils; |
| 1310 } |
| 1311 |
1288 var id = new JS.Identifier(name); | 1312 var id = new JS.Identifier(name); |
1289 body.add(annotate( | 1313 body.add(annotate(new JS.FunctionDeclaration(id, fn), node.element)); |
1290 new JS.FunctionDeclaration(id, _visit(node.functionExpression)), | 1314 if (needsTagging) { |
1291 node.element)); | 1315 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) |
1292 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) | 1316 .toStatement()); |
1293 .toStatement()); | 1317 } |
1294 | 1318 |
1295 if (isPublic(name)) _addExport(name); | 1319 if (isPublic(name)) _addExport(name); |
1296 return _statement(body); | 1320 return _statement(body); |
1297 } | 1321 } |
1298 | 1322 |
| 1323 bool _isInlineJSFunction(FunctionExpression functionExpression) { |
| 1324 bool isJsInvocation(Expression expr) => |
| 1325 expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); |
| 1326 |
| 1327 var body = functionExpression.body; |
| 1328 if (body is ExpressionFunctionBody) { |
| 1329 return isJsInvocation(body.expression); |
| 1330 } else if (body is BlockFunctionBody) { |
| 1331 if (body.block.statements.length == 1) { |
| 1332 var stat = body.block.statements.single; |
| 1333 if (stat is ReturnStatement) { |
| 1334 return isJsInvocation(stat.expression); |
| 1335 } |
| 1336 } |
| 1337 } |
| 1338 return false; |
| 1339 } |
| 1340 |
| 1341 // Simplify `(args) => ((x, y) => { ... })(x, y)` to `(args) => { ... }`. |
| 1342 // Note: we don't check if the top-level args match the ones passed through |
| 1343 // the arrow function, which allows silently passing args through to the |
| 1344 // body (which only works if we don't do weird renamings of Dart params). |
| 1345 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) { |
| 1346 String getIdent(JS.Node node) => node is JS.Identifier ? node.name : null; |
| 1347 List<String> getIdents(List params) => |
| 1348 params.map(getIdent).toList(growable: false); |
| 1349 |
| 1350 if (fn.body is JS.Block && fn.body.statements.length == 1) { |
| 1351 var stat = fn.body.statements.single; |
| 1352 if (stat is JS.Return && stat.value is JS.Call) { |
| 1353 JS.Call call = stat.value; |
| 1354 if (call.target is JS.ArrowFun) { |
| 1355 var passedArgs = getIdents(call.arguments); |
| 1356 JS.ArrowFun innerFun = call.target; |
| 1357 if (_listEquality.equals(getIdents(innerFun.params), passedArgs)) { |
| 1358 return new JS.Fun(fn.params, innerFun.body); |
| 1359 } |
| 1360 } |
| 1361 } |
| 1362 } |
| 1363 return fn; |
| 1364 } |
| 1365 |
1299 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { | 1366 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
1300 var name = node.name.name; | 1367 var name = node.name.name; |
1301 return annotate( | 1368 return annotate( |
1302 new JS.Method(_propertyName(name), _visit(node.functionExpression), | 1369 new JS.Method(_propertyName(name), _visit(node.functionExpression), |
1303 isGetter: node.isGetter, isSetter: node.isSetter), | 1370 isGetter: node.isGetter, isSetter: node.isSetter), |
1304 node.element); | 1371 node.element); |
1305 } | 1372 } |
1306 | 1373 |
1307 bool _executesAtTopLevel(AstNode node) { | 1374 bool _executesAtTopLevel(AstNode node) { |
1308 var ancestor = node.getAncestor((n) => n is FunctionBody || | 1375 var ancestor = node.getAncestor((n) => n is FunctionBody || |
(...skipping 2041 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3350 | 3417 |
3351 /// A special kind of element created by the compiler, signifying a temporary | 3418 /// A special kind of element created by the compiler, signifying a temporary |
3352 /// variable. These objects use instance equality, and should be shared | 3419 /// variable. These objects use instance equality, and should be shared |
3353 /// everywhere in the tree where they are treated as the same variable. | 3420 /// everywhere in the tree where they are treated as the same variable. |
3354 class TemporaryVariableElement extends LocalVariableElementImpl { | 3421 class TemporaryVariableElement extends LocalVariableElementImpl { |
3355 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3422 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3356 | 3423 |
3357 int get hashCode => identityHashCode(this); | 3424 int get hashCode => identityHashCode(this); |
3358 bool operator ==(Object other) => identical(this, other); | 3425 bool operator ==(Object other) => identical(this, other); |
3359 } | 3426 } |
OLD | NEW |