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 ddc.src.codegen.js_codegen; | 5 library ddc.src.codegen.js_codegen; |
| 6 | 6 |
| 7 import 'dart:collection' show HashSet; | 7 import 'dart:collection' show HashSet; |
| 8 import 'dart:io' show Directory, File; | 8 import 'dart:io' show Directory, File; |
| 9 | 9 |
| 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 11 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 11 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
| 12 import 'package:analyzer/src/generated/constant.dart'; | 12 import 'package:analyzer/src/generated/constant.dart'; |
| 13 import 'package:analyzer/src/generated/element.dart'; | 13 import 'package:analyzer/src/generated/element.dart'; |
| 14 import 'package:analyzer/src/generated/scanner.dart' | 14 import 'package:analyzer/src/generated/scanner.dart' |
| 15 show StringToken, Token, TokenType; | 15 show StringToken, Token, TokenType; |
| 16 import 'package:source_maps/source_maps.dart' as srcmaps show Printer; | 16 import 'package:source_maps/source_maps.dart' as srcmaps show Printer; |
| 17 import 'package:source_maps/source_maps.dart' show SourceMapSpan; | 17 import 'package:source_maps/source_maps.dart' show SourceMapSpan; |
| 18 import 'package:source_span/source_span.dart' show SourceLocation; | 18 import 'package:source_span/source_span.dart' show SourceLocation; |
| 19 import 'package:path/path.dart' as path; | 19 import 'package:path/path.dart' as path; |
| 20 | 20 |
| 21 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; | |
| 22 | |
| 21 // TODO(jmesserly): import from its own package | 23 // TODO(jmesserly): import from its own package |
| 22 import 'package:dev_compiler/src/js/js_ast.dart' as JS; | 24 import 'package:dev_compiler/src/js/js_ast.dart' as JS; |
| 23 import 'package:dev_compiler/src/js/js_ast.dart' show js; | 25 import 'package:dev_compiler/src/js/js_ast.dart' show js; |
| 24 | 26 |
| 25 import 'package:dev_compiler/src/checker/rules.dart'; | 27 import 'package:dev_compiler/src/checker/rules.dart'; |
| 26 import 'package:dev_compiler/src/info.dart'; | 28 import 'package:dev_compiler/src/info.dart'; |
| 27 import 'package:dev_compiler/src/options.dart'; | 29 import 'package:dev_compiler/src/options.dart'; |
| 28 import 'package:dev_compiler/src/report.dart'; | 30 import 'package:dev_compiler/src/report.dart'; |
| 29 import 'package:dev_compiler/src/utils.dart'; | 31 import 'package:dev_compiler/src/utils.dart'; |
| 30 import 'code_generator.dart'; | 32 import 'code_generator.dart'; |
| (...skipping 660 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 691 } else { | 693 } else { |
| 692 result = new JS.VariableUse(name); | 694 result = new JS.VariableUse(name); |
| 693 } | 695 } |
| 694 | 696 |
| 695 if (typeArgs != null) { | 697 if (typeArgs != null) { |
| 696 result = js.call('#(#)', [result, typeArgs]); | 698 result = js.call('#(#)', [result, typeArgs]); |
| 697 } | 699 } |
| 698 return result; | 700 return result; |
| 699 } | 701 } |
| 700 | 702 |
| 703 JS.Node _emitDPutIfDynamic( | |
| 704 Expression target, SimpleIdentifier id, Expression rhs) { | |
| 705 if (rules.isDynamicTarget(target)) { | |
| 706 return js.call('dart.dput(#, #, #)', [ | |
| 707 _visit(target), | |
| 708 js.string(id.name, "'"), | |
| 709 _visit(rhs) | |
| 710 ]); | |
| 711 } else { | |
| 712 return null; | |
| 713 } | |
| 714 } | |
| 715 | |
| 701 @override | 716 @override |
| 702 JS.Node visitAssignmentExpression(AssignmentExpression node) { | 717 JS.Node visitAssignmentExpression(AssignmentExpression node) { |
| 703 var lhs = node.leftHandSide; | 718 var lhs = node.leftHandSide; |
| 704 var rhs = node.rightHandSide; | 719 var rhs = node.rightHandSide; |
| 720 return _emitAssignment(lhs, rhs, node.parent); | |
| 721 } | |
| 722 | |
| 723 JS.Node _emitAssignment(Expression lhs, Expression rhs, [AstNode parent]) { | |
| 705 if (lhs is IndexExpression) { | 724 if (lhs is IndexExpression) { |
| 706 String code; | 725 String code; |
| 707 var target = _getTarget(lhs); | 726 var target = _getTarget(lhs); |
| 708 if (rules.isDynamicTarget(target)) { | 727 if (rules.isDynamicTarget(target)) { |
| 709 code = 'dart.dsetindex(#, #, #)'; | 728 code = 'dart.dsetindex(#, #, #)'; |
| 710 } else { | 729 } else { |
| 711 code = '#.set(#, #)'; | 730 code = '#.set(#, #)'; |
| 712 } | 731 } |
| 713 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); | 732 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); |
| 714 } | 733 } |
| 715 | 734 |
| 716 if (lhs is PropertyAccess) { | 735 if (lhs is PropertyAccess) { |
| 717 var target = _getTarget(lhs); | 736 var result = _emitDPutIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); |
| 718 if (rules.isDynamicTarget(target)) { | 737 if (result != null) return result; |
| 719 return js.call('dart.dput(#, #, #)', [ | 738 } else if (lhs is PrefixedIdentifier) { |
| 720 _visit(target), | 739 // TODO(vsm): Is this the right code if the prefix is a library? |
|
Jennifer Messerly
2015/03/04 16:03:51
ah, I think the PrefixedIdentifier branch makes se
| |
| 721 js.string(lhs.propertyName.name, "'"), | 740 var result = _emitDPutIfDynamic(lhs.prefix, lhs.identifier, rhs); |
| 722 _visit(rhs) | 741 if (result != null) return result; |
| 723 ]); | |
| 724 } | |
| 725 } | 742 } |
| 726 | 743 |
| 727 if (node.parent is ExpressionStatement && | 744 if (parent is ExpressionStatement && |
| 728 rhs is CascadeExpression && | 745 rhs is CascadeExpression && |
| 729 _isStateless(lhs, rhs)) { | 746 _isStateless(lhs, rhs)) { |
| 730 // Special case: cascade assignment to a variable in a statement. | 747 // Special case: cascade assignment to a variable in a statement. |
| 731 // We can reuse the variable to desugar it: | 748 // We can reuse the variable to desugar it: |
| 732 // result = []..length = length; | 749 // result = []..length = length; |
| 733 // becomes: | 750 // becomes: |
| 734 // result = []; | 751 // result = []; |
| 735 // result.length = length; | 752 // result.length = length; |
| 736 var savedCascadeTemp = _cascadeTarget; | 753 var savedCascadeTemp = _cascadeTarget; |
| 737 _cascadeTarget = lhs; | 754 _cascadeTarget = lhs; |
| (...skipping 424 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1162 ]); | 1179 ]); |
| 1163 } else { | 1180 } else { |
| 1164 // Generic static-dispatch, user-defined operator code path. | 1181 // Generic static-dispatch, user-defined operator code path. |
| 1165 return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); | 1182 return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); |
| 1166 } | 1183 } |
| 1167 } | 1184 } |
| 1168 } | 1185 } |
| 1169 | 1186 |
| 1170 bool _isNull(Expression expr) => expr is NullLiteral; | 1187 bool _isNull(Expression expr) => expr is NullLiteral; |
| 1171 | 1188 |
| 1189 JS.Expression _emitIncrement(Token op, Expression expr, {bool prefix}) { | |
| 1190 assert(prefix != null); | |
| 1191 | |
| 1192 // TODO(vsm): Avoid collisions. | |
| 1193 var tmp1 = '_t1'; | |
|
Jennifer Messerly
2015/03/04 16:03:51
so far we've been using `$` for compiler generated
vsm
2015/03/04 17:55:30
Done.
| |
| 1194 var tmp2 = '_t2'; | |
| 1195 | |
| 1196 // Read | |
| 1197 var read = _visit(expr); | |
| 1198 | |
| 1199 // Increment | |
| 1200 var one = AstBuilder.integerLiteral(1); | |
| 1201 var binary = AstBuilder.binaryExpression(expr, op.lexeme[0], one); | |
|
Jennifer Messerly
2015/03/04 16:03:51
hmmm. Should this be `id` instead of `expr` as the
vsm
2015/03/04 17:55:30
good catch! fixed.
| |
| 1202 var increment = _visit(binary); | |
|
Jennifer Messerly
2015/03/04 16:03:51
this could be directly `visitBinaryExpression` sin
vsm
2015/03/04 17:55:30
This ends up being a little complicated. I'm feed
| |
| 1203 | |
| 1204 // Write | |
| 1205 var id = | |
| 1206 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, tmp1, 0)); | |
|
Jennifer Messerly
2015/03/04 16:03:51
maybe a TODO: I think we had to do something simil
vsm
2015/03/04 17:55:30
I factored this out a bit.
| |
| 1207 id.staticElement = new LocalVariableElementImpl.forNode(id); | |
| 1208 id.staticType = expr.staticType; | |
| 1209 var write = _emitAssignment(expr, id); | |
| 1210 | |
| 1211 return js.call(''' | |
| 1212 (function () { | |
|
Jennifer Messerly
2015/03/04 16:03:51
I think you want an `=>` function here. Otherwise
| |
| 1213 var $tmp1 = #; | |
|
Jennifer Messerly
2015/03/04 16:03:51
one warning I got from Stephen was to make sure to
| |
| 1214 var $tmp2 = #; | |
| 1215 #; | |
| 1216 return ${prefix ? tmp2 : tmp1}; | |
|
Jennifer Messerly
2015/03/04 16:03:51
hmmm, I wonder if we can do better for prefix and
vsm
2015/03/04 17:55:30
Nice idea! I will try this. In the postfix, dyna
| |
| 1217 })() | |
| 1218 ''', [read, increment, write]); | |
| 1219 } | |
| 1220 | |
| 1172 @override | 1221 @override |
| 1173 JS.Expression visitPostfixExpression(PostfixExpression node) { | 1222 JS.Expression visitPostfixExpression(PostfixExpression node) { |
| 1174 var op = node.operator; | 1223 var op = node.operator; |
| 1175 var expr = node.operand; | 1224 var expr = node.operand; |
| 1176 | 1225 |
| 1177 var dispatchType = rules.getStaticType(expr); | 1226 var dispatchType = rules.getStaticType(expr); |
| 1178 if (unaryOperationIsPrimitive(dispatchType)) { | 1227 if (unaryOperationIsPrimitive(dispatchType)) { |
| 1179 // TODO(vsm): When do Dart ops not map to JS? | 1228 // TODO(vsm): When do Dart ops not map to JS? |
| 1180 return js.call('#$op', notNull(expr)); | 1229 return js.call('#$op', notNull(expr)); |
| 1181 } else { | 1230 } else { |
| 1182 // TODO(vsm): Figure out operator calling convention / dispatch. | 1231 assert(op.lexeme == '++' || op.lexeme == '--'); |
| 1183 return visitExpression(node); | 1232 var result = _emitIncrement(op, expr, prefix: false); |
| 1233 return (result != null) ? result : visitExpression(node); | |
| 1184 } | 1234 } |
| 1185 } | 1235 } |
| 1186 | 1236 |
| 1187 @override | 1237 @override |
| 1188 JS.Expression visitPrefixExpression(PrefixExpression node) { | 1238 JS.Expression visitPrefixExpression(PrefixExpression node) { |
| 1189 var op = node.operator; | 1239 var op = node.operator; |
| 1190 var expr = node.operand; | 1240 var expr = node.operand; |
| 1191 | 1241 |
| 1192 var dispatchType = rules.getStaticType(expr); | 1242 var dispatchType = rules.getStaticType(expr); |
| 1193 if (unaryOperationIsPrimitive(dispatchType)) { | 1243 if (unaryOperationIsPrimitive(dispatchType)) { |
| 1194 // TODO(vsm): When do Dart ops not map to JS? | 1244 // TODO(vsm): When do Dart ops not map to JS? |
| 1195 return js.call('$op#', notNull(expr)); | 1245 return js.call('$op#', notNull(expr)); |
| 1196 } else { | 1246 } else { |
| 1197 // TODO(vsm): Figure out operator calling convention / dispatch. | 1247 // Increment or decrement must be numerical |
| 1198 return visitExpression(node); | 1248 if (op.lexeme == '++' || op.lexeme == '--') { |
| 1249 var result = _emitIncrement(op, expr, prefix: true); | |
| 1250 return (result != null) ? result : visitExpression(node); | |
| 1251 } | |
| 1252 } | |
| 1253 // TODO(vsm): Statically invoke the operator if the type is known. | |
| 1254 // Fall back to dynamic dispatch. | |
| 1255 var opString = _jsMemberName(op.lexeme, unary: true); | |
| 1256 if (rules.isDynamicTarget(expr)) { | |
| 1257 // dynamic dispatch | |
| 1258 return js.call('dart.dunary(#, #)', [opString, _visit(expr)]); | |
| 1259 } else if (_isJSBuiltinType(dispatchType)) { | |
| 1260 return js.call( | |
| 1261 '#.#(#)', [_emitTypeName(dispatchType), opString, _visit(expr)]); | |
| 1262 } else { | |
| 1263 // Generic static-dispatch, user-defined operator code path. | |
| 1264 return js.call('#.#()', [_visit(expr), opString]); | |
| 1199 } | 1265 } |
| 1200 } | 1266 } |
| 1201 | 1267 |
| 1202 // Cascades can contain [IndexExpression], [MethodInvocation] and | 1268 // Cascades can contain [IndexExpression], [MethodInvocation] and |
| 1203 // [PropertyAccess]. The code generation for those is handled in their | 1269 // [PropertyAccess]. The code generation for those is handled in their |
| 1204 // respective visit methods. | 1270 // respective visit methods. |
| 1205 @override | 1271 @override |
| 1206 JS.Node visitCascadeExpression(CascadeExpression node) { | 1272 JS.Node visitCascadeExpression(CascadeExpression node) { |
| 1207 var savedCascadeTemp = _cascadeTarget; | 1273 var savedCascadeTemp = _cascadeTarget; |
| 1208 | 1274 |
| (...skipping 754 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1963 | 2029 |
| 1964 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2030 // TODO(jmesserly): in many cases marking the end will be unncessary. |
| 1965 printer.mark(_location(node.end)); | 2031 printer.mark(_location(node.end)); |
| 1966 } | 2032 } |
| 1967 | 2033 |
| 1968 String _getIdentifier(AstNode node) { | 2034 String _getIdentifier(AstNode node) { |
| 1969 if (node is SimpleIdentifier) return node.name; | 2035 if (node is SimpleIdentifier) return node.name; |
| 1970 return null; | 2036 return null; |
| 1971 } | 2037 } |
| 1972 } | 2038 } |
| OLD | NEW |