Chromium Code Reviews| 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 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 ConstFieldVisitor _constField; | 97 ConstFieldVisitor _constField; |
| 98 | 98 |
| 99 ModuleItemLoadOrder _loader; | 99 ModuleItemLoadOrder _loader; |
| 100 | 100 |
| 101 /// _interceptors.JSArray<E>, used for List literals. | 101 /// _interceptors.JSArray<E>, used for List literals. |
| 102 ClassElement _jsArray; | 102 ClassElement _jsArray; |
| 103 | 103 |
| 104 /// The default value of the module object. See [visitLibraryDirective]. | 104 /// The default value of the module object. See [visitLibraryDirective]. |
| 105 String _jsModuleValue; | 105 String _jsModuleValue; |
| 106 | 106 |
| 107 bool _isDartUtils; | |
| 108 | |
| 107 Map<String, DartType> _objectMembers; | 109 Map<String, DartType> _objectMembers; |
| 108 | 110 |
| 109 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, | 111 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, |
| 110 this._extensionTypes, this._fieldsNeedingStorage) | 112 this._extensionTypes, this._fieldsNeedingStorage) |
| 111 : compiler = compiler, | 113 : compiler = compiler, |
| 112 options = compiler.options.codegenOptions, | 114 options = compiler.options.codegenOptions, |
| 113 _types = compiler.context.typeProvider { | 115 _types = compiler.context.typeProvider { |
| 114 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 116 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
| 115 | 117 |
| 116 var context = compiler.context; | 118 var context = compiler.context; |
| 117 var src = context.sourceFactory.forUri('dart:_interceptors'); | 119 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 118 var interceptors = context.computeLibraryElement(src); | 120 var interceptors = context.computeLibraryElement(src); |
| 119 _jsArray = interceptors.getType('JSArray'); | 121 _jsArray = interceptors.getType('JSArray'); |
| 122 _isDartUtils = currentLibrary.isInSdk && | |
|
Jennifer Messerly
2015/11/30 18:54:11
you shouldn't need the isInSdk check, it's just ch
| |
| 123 currentLibrary.source.uri.toString() == 'dart:_utils'; | |
| 120 | 124 |
| 121 _objectMembers = getObjectMemberMap(types); | 125 _objectMembers = getObjectMemberMap(types); |
| 122 } | 126 } |
| 123 | 127 |
| 124 TypeProvider get types => rules.provider; | 128 TypeProvider get types => rules.provider; |
| 125 | 129 |
| 126 JS.Program emitLibrary(LibraryUnit library) { | 130 JS.Program emitLibrary(LibraryUnit library) { |
| 127 // Modify the AST to make coercions explicit. | 131 // Modify the AST to make coercions explicit. |
| 128 new CoercionReifier(library, rules).reify(); | 132 new CoercionReifier(library, rules).reify(); |
| 129 | 133 |
| (...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, | 193 // 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. | 194 // then figure out if we really need each of these parameters. |
| 191 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 | 195 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
| 192 var params = [_exportsVar, _runtimeLibVar]; | 196 var params = [_exportsVar, _runtimeLibVar]; |
| 193 var processImport = | 197 var processImport = |
| 194 (LibraryElement library, JS.TemporaryId temp, List list) { | 198 (LibraryElement library, JS.TemporaryId temp, List list) { |
| 195 params.add(temp); | 199 params.add(temp); |
| 196 list.add(js.string(compiler.getModuleName(library.source.uri), "'")); | 200 list.add(js.string(compiler.getModuleName(library.source.uri), "'")); |
| 197 }; | 201 }; |
| 198 | 202 |
| 199 var imports = <JS.Expression>[js.string('dart/_runtime')]; | 203 var needsDartRuntime = !_isDartUtils; |
| 204 | |
| 205 var imports = <JS.Expression>[]; | |
| 206 var moduleStatements = <JS.Statement>[]; | |
| 207 if (needsDartRuntime) { | |
| 208 imports.add(js.string('dart/_runtime')); | |
| 209 | |
| 210 var dartxImport = | |
| 211 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); | |
|
Jennifer Messerly
2015/11/30 18:54:11
const?
| |
| 212 moduleStatements.add(dartxImport); | |
| 213 } | |
| 214 moduleStatements.addAll(_moduleItems); | |
| 215 | |
| 200 _imports.forEach((library, temp) { | 216 _imports.forEach((library, temp) { |
| 201 if (_loader.libraryIsLoaded(library)) { | 217 if (_loader.libraryIsLoaded(library)) { |
| 202 processImport(library, temp, imports); | 218 processImport(library, temp, imports); |
| 203 } | 219 } |
| 204 }); | 220 }); |
| 205 | 221 |
| 206 var lazyImports = <JS.Expression>[]; | 222 var lazyImports = <JS.Expression>[]; |
| 207 _imports.forEach((library, temp) { | 223 _imports.forEach((library, temp) { |
| 208 if (!_loader.libraryIsLoaded(library)) { | 224 if (!_loader.libraryIsLoaded(library)) { |
| 209 processImport(library, temp, lazyImports); | 225 processImport(library, temp, lazyImports); |
| 210 } | 226 } |
| 211 }); | 227 }); |
| 212 | 228 |
| 213 var dartxImport = | 229 var module = |
| 214 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); | 230 js.call("function(#) { 'use strict'; #; }", [params, moduleStatements]); |
| 215 | |
| 216 var module = js.call("function(#) { 'use strict'; #; #; }", | |
| 217 [params, dartxImport, _moduleItems]); | |
| 218 | 231 |
| 219 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ | 232 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ |
| 220 js.string(jsPath, "'"), | 233 js.string(jsPath, "'"), |
| 221 _jsModuleValue ?? new JS.LiteralNull(), | 234 _jsModuleValue ?? new JS.LiteralNull(), |
| 222 js.commentExpression( | 235 js.commentExpression( |
| 223 "Imports", new JS.ArrayInitializer(imports, multiline: true)), | 236 "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
| 224 js.commentExpression("Lazy imports", | 237 js.commentExpression("Lazy imports", |
| 225 new JS.ArrayInitializer(lazyImports, multiline: true)), | 238 new JS.ArrayInitializer(lazyImports, multiline: true)), |
| 226 module | 239 module |
| 227 ]); | 240 ]); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 282 .map((i) => i.name) | 295 .map((i) => i.name) |
| 283 .where((s) => !currentLibNames.containsKey(s)) | 296 .where((s) => !currentLibNames.containsKey(s)) |
| 284 .map((s) => js.string(s, "'"))); | 297 .map((s) => js.string(s, "'"))); |
| 285 } | 298 } |
| 286 if (hide != null) { | 299 if (hide != null) { |
| 287 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); | 300 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); |
| 288 } | 301 } |
| 289 args.add(new JS.ArrayInitializer(shownNames)); | 302 args.add(new JS.ArrayInitializer(shownNames)); |
| 290 args.add(new JS.ArrayInitializer(hiddenNames)); | 303 args.add(new JS.ArrayInitializer(hiddenNames)); |
| 291 } | 304 } |
| 292 _moduleItems.add(js.statement('dart.export(#);', [args])); | 305 _moduleItems.add(js.statement('dart.export_(#);', [args])); |
| 293 } | 306 } |
| 294 | 307 |
| 295 JS.Identifier _initSymbol(JS.Identifier id) { | 308 JS.Identifier _initSymbol(JS.Identifier id) { |
| 296 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | 309 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); |
| 297 _moduleItems.add(s); | 310 _moduleItems.add(s); |
| 298 return id; | 311 return id; |
| 299 } | 312 } |
| 300 | 313 |
| 301 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 314 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
| 302 // until we have better name tracking. | 315 // until we have better name tracking. |
| (...skipping 974 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1277 // Add these later so we can use getter/setter syntax. | 1290 // Add these later so we can use getter/setter syntax. |
| 1278 _properties.add(node); | 1291 _properties.add(node); |
| 1279 return null; | 1292 return null; |
| 1280 } | 1293 } |
| 1281 | 1294 |
| 1282 var body = <JS.Statement>[]; | 1295 var body = <JS.Statement>[]; |
| 1283 _flushLibraryProperties(body); | 1296 _flushLibraryProperties(body); |
| 1284 | 1297 |
| 1285 var name = node.name.name; | 1298 var name = node.name.name; |
| 1286 | 1299 |
| 1300 var fn = _visit(node.functionExpression); | |
| 1301 bool needsTagging = true; | |
| 1302 if (currentLibrary.isInSdk && | |
|
Jennifer Messerly
2015/11/30 18:54:11
I try and avoid LibraryElement.isInSdk, because an
ochafik
2015/12/01 14:41:03
Done.
| |
| 1303 _isInlineJSBody(node.functionExpression.body)) { | |
| 1304 fn = _simplifyFun(fn); | |
| 1305 needsTagging = !_isDartUtils; | |
| 1306 } | |
| 1287 var id = new JS.Identifier(name); | 1307 var id = new JS.Identifier(name); |
| 1288 body.add(annotate( | 1308 body.add(annotate( |
| 1289 new JS.FunctionDeclaration(id, _visit(node.functionExpression)), | 1309 new JS.FunctionDeclaration(id, fn), |
| 1290 node.element)); | 1310 node.element)); |
| 1291 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) | 1311 if (needsTagging) { |
| 1292 .toStatement()); | 1312 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) |
| 1313 .toStatement()); | |
| 1314 } | |
| 1293 | 1315 |
| 1294 if (isPublic(name)) _addExport(name); | 1316 if (isPublic(name)) _addExport(name); |
| 1295 return _statement(body); | 1317 return _statement(body); |
| 1296 } | 1318 } |
| 1297 | 1319 |
| 1320 /// Simplify `f(x) => (function() { ... })()` to `f(x) => { ... }`. | |
| 1321 /// | |
| 1322 /// This is useful for runtime utils written with JS intrinsics, e.g. | |
| 1323 /// `foo(x) => JS('', '(function() { let x = #; ... })()', x);`. | |
| 1324 JS.Fun _simplifyFun(JS.Fun fn) { | |
|
Jennifer Messerly
2015/11/30 18:54:11
love this!
Regarding:
JS('', '(function() { l
ochafik
2015/12/01 14:41:03
I was equally bothered by `let x = x` and the sile
| |
| 1325 if (fn.body is JS.Block && fn.body.statements.length == 1) { | |
| 1326 var stat = fn.body.statements.single; | |
| 1327 if (stat is JS.Return) { | |
| 1328 if (stat.value is JS.Call) { | |
| 1329 JS.Call call = stat.value; | |
| 1330 if (call.target is JS.Fun && call.arguments.isEmpty) { | |
| 1331 JS.Fun innerFun = call.target; | |
| 1332 return new JS.Fun(fn.params, innerFun.body); | |
| 1333 } | |
| 1334 } | |
| 1335 } | |
| 1336 } | |
| 1337 return fn; | |
| 1338 } | |
| 1339 | |
| 1340 bool _isInlineJSBody(FunctionBody body) { | |
|
Jennifer Messerly
2015/11/30 18:54:11
It might be nice to combine this with the simplifi
ochafik
2015/12/01 14:41:03
I can't find a very clean way to combine them (hav
| |
| 1341 bool isJsInvocation(Expression expr) => | |
| 1342 expr is MethodInvocation && | |
| 1343 isInlineJS(expr.methodName.staticElement); | |
| 1344 | |
| 1345 if (body is ExpressionFunctionBody) { | |
| 1346 return isJsInvocation(body.expression); | |
| 1347 } else if (body is BlockFunctionBody) { | |
| 1348 if (body.block.statements.length == 1) { | |
| 1349 var stat = body.block.statements.single; | |
| 1350 if (stat is ReturnStatement) { | |
| 1351 return isJsInvocation(stat.expression); | |
| 1352 } | |
| 1353 } | |
| 1354 } | |
| 1355 return false; | |
| 1356 } | |
| 1357 | |
| 1298 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { | 1358 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
| 1299 var name = node.name.name; | 1359 var name = node.name.name; |
| 1300 return annotate( | 1360 return annotate( |
| 1301 new JS.Method(_propertyName(name), _visit(node.functionExpression), | 1361 new JS.Method(_propertyName(name), _visit(node.functionExpression), |
| 1302 isGetter: node.isGetter, isSetter: node.isSetter), | 1362 isGetter: node.isGetter, isSetter: node.isSetter), |
| 1303 node.element); | 1363 node.element); |
| 1304 } | 1364 } |
| 1305 | 1365 |
| 1306 bool _executesAtTopLevel(AstNode node) { | 1366 bool _executesAtTopLevel(AstNode node) { |
| 1307 var ancestor = node.getAncestor((n) => n is FunctionBody || | 1367 var ancestor = node.getAncestor((n) => n is FunctionBody || |
| (...skipping 2039 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3347 | 3407 |
| 3348 /// A special kind of element created by the compiler, signifying a temporary | 3408 /// A special kind of element created by the compiler, signifying a temporary |
| 3349 /// variable. These objects use instance equality, and should be shared | 3409 /// variable. These objects use instance equality, and should be shared |
| 3350 /// everywhere in the tree where they are treated as the same variable. | 3410 /// everywhere in the tree where they are treated as the same variable. |
| 3351 class TemporaryVariableElement extends LocalVariableElementImpl { | 3411 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 3352 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3412 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 3353 | 3413 |
| 3354 int get hashCode => identityHashCode(this); | 3414 int get hashCode => identityHashCode(this); |
| 3355 bool operator ==(Object other) => identical(this, other); | 3415 bool operator ==(Object other) => identical(this, other); |
| 3356 } | 3416 } |
| OLD | NEW |