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 |