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 1303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1314 if (needsTagging) { | 1314 if (needsTagging) { |
1315 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) | 1315 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) |
1316 .toStatement()); | 1316 .toStatement()); |
1317 } | 1317 } |
1318 | 1318 |
1319 if (isPublic(name)) _addExport(name); | 1319 if (isPublic(name)) _addExport(name); |
1320 return _statement(body); | 1320 return _statement(body); |
1321 } | 1321 } |
1322 | 1322 |
1323 bool _isInlineJSFunction(FunctionExpression functionExpression) { | 1323 bool _isInlineJSFunction(FunctionExpression functionExpression) { |
1324 bool isJsInvocation(Expression expr) => | |
1325 expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); | |
1326 | |
1327 var body = functionExpression.body; | 1324 var body = functionExpression.body; |
1328 if (body is ExpressionFunctionBody) { | 1325 if (body is ExpressionFunctionBody) { |
1329 return isJsInvocation(body.expression); | 1326 return _isJSInvocation(body.expression); |
1330 } else if (body is BlockFunctionBody) { | 1327 } else if (body is BlockFunctionBody) { |
1331 if (body.block.statements.length == 1) { | 1328 if (body.block.statements.length == 1) { |
1332 var stat = body.block.statements.single; | 1329 var stat = body.block.statements.single; |
1333 if (stat is ReturnStatement) { | 1330 if (stat is ReturnStatement) { |
1334 return isJsInvocation(stat.expression); | 1331 return _isJSInvocation(stat.expression); |
1335 } | 1332 } |
1336 } | 1333 } |
1337 } | 1334 } |
1338 return false; | 1335 return false; |
1339 } | 1336 } |
1340 | 1337 |
| 1338 bool _isJSInvocation(Expression expr) => |
| 1339 expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); |
| 1340 |
1341 // Simplify `(args) => ((x, y) => { ... })(x, y)` to `(args) => { ... }`. | 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 | 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 | 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). | 1344 // body (which only works if we don't do weird renamings of Dart params). |
1345 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) { | 1345 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) { |
1346 String getIdent(JS.Node node) => node is JS.Identifier ? node.name : null; | 1346 String getIdent(JS.Node node) => node is JS.Identifier ? node.name : null; |
1347 List<String> getIdents(List params) => | 1347 List<String> getIdents(List params) => |
1348 params.map(getIdent).toList(growable: false); | 1348 params.map(getIdent).toList(growable: false); |
1349 | 1349 |
1350 if (fn.body is JS.Block && fn.body.statements.length == 1) { | 1350 if (fn.body is JS.Block && fn.body.statements.length == 1) { |
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1737 return _maybeQualifiedName(element); | 1737 return _maybeQualifiedName(element); |
1738 } | 1738 } |
1739 | 1739 |
1740 JS.Expression _maybeQualifiedName(Element e, [String name]) { | 1740 JS.Expression _maybeQualifiedName(Element e, [String name]) { |
1741 var libName = _libraryName(e.library); | 1741 var libName = _libraryName(e.library); |
1742 var nameExpr = _propertyName(name ?? e.name); | 1742 var nameExpr = _propertyName(name ?? e.name); |
1743 | 1743 |
1744 // Always qualify: | 1744 // Always qualify: |
1745 // * mutable top-level fields | 1745 // * mutable top-level fields |
1746 // * elements from other libraries | 1746 // * elements from other libraries |
1747 bool mutableTopLevel = e is TopLevelVariableElement && !e.isConst; | 1747 bool mutableTopLevel = e is TopLevelVariableElement && |
| 1748 !e.isConst && |
| 1749 !_isFinalJSDecl(e.computeNode()); |
1748 bool fromAnotherLibrary = e.library != currentLibrary; | 1750 bool fromAnotherLibrary = e.library != currentLibrary; |
1749 if (mutableTopLevel || fromAnotherLibrary) { | 1751 if (mutableTopLevel || fromAnotherLibrary) { |
1750 return new JS.PropertyAccess(libName, nameExpr); | 1752 return new JS.PropertyAccess(libName, nameExpr); |
1751 } | 1753 } |
1752 | 1754 |
1753 var id = new JS.MaybeQualifiedId(libName, nameExpr); | 1755 var id = new JS.MaybeQualifiedId(libName, nameExpr); |
1754 _qualifiedIds.add(new Tuple2(e, id)); | 1756 _qualifiedIds.add(new Tuple2(e, id)); |
1755 return id; | 1757 return id; |
1756 } | 1758 } |
1757 | 1759 |
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2083 } | 2085 } |
2084 | 2086 |
2085 @override | 2087 @override |
2086 visitVariableDeclaration(VariableDeclaration node) { | 2088 visitVariableDeclaration(VariableDeclaration node) { |
2087 if (node.element is PropertyInducingElement) return _emitStaticField(node); | 2089 if (node.element is PropertyInducingElement) return _emitStaticField(node); |
2088 | 2090 |
2089 var name = new JS.Identifier(node.name.name); | 2091 var name = new JS.Identifier(node.name.name); |
2090 return new JS.VariableInitialization(name, _visitInitializer(node)); | 2092 return new JS.VariableInitialization(name, _visitInitializer(node)); |
2091 } | 2093 } |
2092 | 2094 |
| 2095 bool _isFinalJSDecl(AstNode field) => field is VariableDeclaration && |
| 2096 field.isFinal && |
| 2097 _isJSInvocation(field.initializer); |
| 2098 |
2093 /// Emits a static or top-level field. | 2099 /// Emits a static or top-level field. |
2094 JS.Statement _emitStaticField(VariableDeclaration field) { | 2100 JS.Statement _emitStaticField(VariableDeclaration field) { |
2095 PropertyInducingElement element = field.element; | 2101 PropertyInducingElement element = field.element; |
2096 assert(element.isStatic); | 2102 assert(element.isStatic); |
2097 | 2103 |
2098 bool eagerInit; | 2104 bool eagerInit; |
2099 JS.Expression jsInit; | 2105 JS.Expression jsInit; |
2100 if (field.isConst || _constField.isFieldInitConstant(field)) { | 2106 if (field.isConst || _constField.isFieldInitConstant(field)) { |
2101 // If the field is constant, try and generate it at the top level. | 2107 // If the field is constant, try and generate it at the top level. |
2102 _loader.startTopLevel(element); | 2108 _loader.startTopLevel(element); |
2103 jsInit = _visitInitializer(field); | 2109 jsInit = _visitInitializer(field); |
2104 _loader.finishTopLevel(element); | 2110 _loader.finishTopLevel(element); |
2105 eagerInit = _loader.isLoaded(element); | 2111 eagerInit = _loader.isLoaded(element); |
2106 } else { | 2112 } else { |
2107 jsInit = _visitInitializer(field); | 2113 jsInit = _visitInitializer(field); |
2108 eagerInit = false; | 2114 eagerInit = false; |
2109 } | 2115 } |
2110 | 2116 |
| 2117 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile |
| 2118 // runtime helpers. |
| 2119 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); |
| 2120 if (isJSTopLevel) eagerInit = true; |
| 2121 |
2111 var fieldName = field.name.name; | 2122 var fieldName = field.name.name; |
2112 if (field.isConst && eagerInit && element is TopLevelVariableElement) { | 2123 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || |
| 2124 isJSTopLevel) { |
2113 // constant fields don't change, so we can generate them as `let` | 2125 // constant fields don't change, so we can generate them as `let` |
2114 // but add them to the module's exports. However, make sure we generate | 2126 // but add them to the module's exports. However, make sure we generate |
2115 // anything they depend on first. | 2127 // anything they depend on first. |
2116 | 2128 |
2117 if (isPublic(fieldName)) _addExport(fieldName); | 2129 if (isPublic(fieldName)) _addExport(fieldName); |
2118 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; | 2130 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; |
2119 return annotateVariable( | 2131 return annotateVariable( |
2120 js.statement( | 2132 js.statement( |
2121 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]), | 2133 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]), |
2122 field.element); | 2134 field.element); |
(...skipping 1294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3417 | 3429 |
3418 /// A special kind of element created by the compiler, signifying a temporary | 3430 /// A special kind of element created by the compiler, signifying a temporary |
3419 /// variable. These objects use instance equality, and should be shared | 3431 /// variable. These objects use instance equality, and should be shared |
3420 /// everywhere in the tree where they are treated as the same variable. | 3432 /// everywhere in the tree where they are treated as the same variable. |
3421 class TemporaryVariableElement extends LocalVariableElementImpl { | 3433 class TemporaryVariableElement extends LocalVariableElementImpl { |
3422 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3434 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3423 | 3435 |
3424 int get hashCode => identityHashCode(this); | 3436 int get hashCode => identityHashCode(this); |
3425 bool operator ==(Object other) => identical(this, other); | 3437 bool operator ==(Object other) => identical(this, other); |
3426 } | 3438 } |
OLD | NEW |