| 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 |