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; |
| (...skipping 680 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 691 } else { | 691 } else { |
| 692 result = new JS.VariableUse(name); | 692 result = new JS.VariableUse(name); |
| 693 } | 693 } |
| 694 | 694 |
| 695 if (typeArgs != null) { | 695 if (typeArgs != null) { |
| 696 result = js.call('#(#)', [result, typeArgs]); | 696 result = js.call('#(#)', [result, typeArgs]); |
| 697 } | 697 } |
| 698 return result; | 698 return result; |
| 699 } | 699 } |
| 700 | 700 |
| 701 JS.Node _emitDPutIfDynamic( | |
| 702 Expression target, SimpleIdentifier id, Expression rhs) { | |
| 703 if (rules.isDynamicTarget(target)) { | |
| 704 return js.call('dart.dput(#, #, #)', [ | |
| 705 _visit(target), | |
| 706 js.string(id.name, "'"), | |
| 707 _visit(rhs) | |
| 708 ]); | |
| 709 } else { | |
| 710 return null; | |
| 711 } | |
| 712 } | |
| 713 | |
| 701 @override | 714 @override |
| 702 JS.Node visitAssignmentExpression(AssignmentExpression node) { | 715 JS.Node visitAssignmentExpression(AssignmentExpression node) { |
| 703 var lhs = node.leftHandSide; | 716 var lhs = node.leftHandSide; |
| 704 var rhs = node.rightHandSide; | 717 var rhs = node.rightHandSide; |
| 718 return _emitAssignment(lhs, rhs, node.parent); | |
| 719 } | |
| 720 | |
| 721 JS.Node _emitAssignment(Expression lhs, Expression rhs, [AstNode parent]) { | |
| 705 if (lhs is IndexExpression) { | 722 if (lhs is IndexExpression) { |
| 706 String code; | 723 String code; |
| 707 var target = _getTarget(lhs); | 724 var target = _getTarget(lhs); |
| 708 if (rules.isDynamicTarget(target)) { | 725 if (rules.isDynamicTarget(target)) { |
| 709 code = 'dart.dsetindex(#, #, #)'; | 726 code = 'dart.dsetindex(#, #, #)'; |
| 710 } else { | 727 } else { |
| 711 code = '#.set(#, #)'; | 728 code = '#.set(#, #)'; |
| 712 } | 729 } |
| 713 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); | 730 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); |
| 714 } | 731 } |
| 715 | 732 |
| 716 if (lhs is PropertyAccess) { | 733 if (lhs is PropertyAccess) { |
| 717 var target = _getTarget(lhs); | 734 var result = _emitDPutIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); |
| 718 if (rules.isDynamicTarget(target)) { | 735 if (result != null) return result; |
| 719 return js.call('dart.dput(#, #, #)', [ | 736 } else if (lhs is PrefixedIdentifier) { |
| 720 _visit(target), | 737 // TODO(vsm): Is this the right code if the prefix is a library? |
|
Jennifer Messerly
2015/03/04 01:09:31
how can this happen? I don't think PrefixedIdentif
Siggi Cherem (dart-lang)
2015/03/04 03:56:07
My understanding is that PrefixedIdentifier is use
| |
| 721 js.string(lhs.propertyName.name, "'"), | 738 var result = _emitDPutIfDynamic(lhs.prefix, lhs.identifier, rhs); |
| 722 _visit(rhs) | 739 if (result != null) return result; |
| 723 ]); | |
| 724 } | |
| 725 } | 740 } |
| 726 | 741 |
| 727 if (node.parent is ExpressionStatement && | 742 if (parent is ExpressionStatement && |
| 728 rhs is CascadeExpression && | 743 rhs is CascadeExpression && |
| 729 _isStateless(lhs, rhs)) { | 744 _isStateless(lhs, rhs)) { |
| 730 // Special case: cascade assignment to a variable in a statement. | 745 // Special case: cascade assignment to a variable in a statement. |
| 731 // We can reuse the variable to desugar it: | 746 // We can reuse the variable to desugar it: |
| 732 // result = []..length = length; | 747 // result = []..length = length; |
| 733 // becomes: | 748 // becomes: |
| 734 // result = []; | 749 // result = []; |
| 735 // result.length = length; | 750 // result.length = length; |
| 736 var savedCascadeTemp = _cascadeTarget; | 751 var savedCascadeTemp = _cascadeTarget; |
| 737 _cascadeTarget = lhs; | 752 _cascadeTarget = lhs; |
| (...skipping 424 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1162 ]); | 1177 ]); |
| 1163 } else { | 1178 } else { |
| 1164 // Generic static-dispatch, user-defined operator code path. | 1179 // Generic static-dispatch, user-defined operator code path. |
| 1165 return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); | 1180 return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); |
| 1166 } | 1181 } |
| 1167 } | 1182 } |
| 1168 } | 1183 } |
| 1169 | 1184 |
| 1170 bool _isNull(Expression expr) => expr is NullLiteral; | 1185 bool _isNull(Expression expr) => expr is NullLiteral; |
| 1171 | 1186 |
| 1187 JS.Expression _emitIncrement(Token op, Expression expr, {bool prefix}) { | |
| 1188 assert(prefix != null); | |
| 1189 | |
| 1190 // TODO(vsm): If the expression has a side effect, we need something differe nt. | |
|
Jennifer Messerly
2015/03/04 01:09:31
long line
vsm
2015/03/04 14:59:27
removed
| |
| 1191 if (!_isStateless(expr) && | |
|
vsm
2015/03/04 00:54:21
Actually, I think this is unnecessary...
Jennifer Messerly
2015/03/04 01:09:31
right, your temps are handling this?
consider `fo
vsm
2015/03/04 14:59:27
right
| |
| 1192 (expr is! SimpleIdentifier || | |
| 1193 (expr as SimpleIdentifier).staticElement is! LocalVariableElement)) { | |
| 1194 return null; | |
| 1195 } | |
| 1196 | |
| 1197 // TODO(vsm): Should we generate a runtime helper for this? | |
| 1198 var tmp = '_'; | |
| 1199 var applied = prefix ? '$tmp$op' : '$op$tmp'; | |
|
vsm
2015/03/04 00:54:21
This might not be a number. I'll change this to c
Jennifer Messerly
2015/03/04 01:09:31
yeah. ideally it could "desugar" to `expr += 1`, a
vsm
2015/03/04 14:59:27
mapping to x = x + 1 for now, but yeah.
| |
| 1200 var id = | |
| 1201 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, tmp, 0)); | |
| 1202 id.staticElement = new LocalVariableElementImpl.forNode(id); | |
| 1203 id.staticType = expr.staticType; | |
| 1204 var assignment = _emitAssignment(expr, id); | |
| 1205 return js.call(''' | |
| 1206 (function () { | |
| 1207 var $tmp = #; | |
| 1208 if (typeof($tmp) != 'number') throw 'number expected'; | |
| 1209 var result = $applied; | |
| 1210 #; | |
| 1211 return result; | |
| 1212 })() | |
| 1213 ''', [expr.accept(this), assignment]); | |
| 1214 } | |
| 1215 | |
| 1172 @override | 1216 @override |
| 1173 JS.Expression visitPostfixExpression(PostfixExpression node) { | 1217 JS.Expression visitPostfixExpression(PostfixExpression node) { |
| 1174 var op = node.operator; | 1218 var op = node.operator; |
| 1175 var expr = node.operand; | 1219 var expr = node.operand; |
| 1176 | 1220 |
| 1177 var dispatchType = rules.getStaticType(expr); | 1221 var dispatchType = rules.getStaticType(expr); |
| 1178 if (unaryOperationIsPrimitive(dispatchType)) { | 1222 if (unaryOperationIsPrimitive(dispatchType)) { |
| 1179 // TODO(vsm): When do Dart ops not map to JS? | 1223 // TODO(vsm): When do Dart ops not map to JS? |
| 1180 return js.call('#$op', notNull(expr)); | 1224 return js.call('#$op', notNull(expr)); |
| 1181 } else { | 1225 } else { |
| 1182 // TODO(vsm): Figure out operator calling convention / dispatch. | 1226 assert(op.lexeme == '++' || op.lexeme == '--'); |
| 1183 return visitExpression(node); | 1227 var result = _emitIncrement(op, expr, prefix: false); |
| 1228 return (result != null) ? result : visitExpression(node); | |
| 1184 } | 1229 } |
| 1185 } | 1230 } |
| 1186 | 1231 |
| 1187 @override | 1232 @override |
| 1188 JS.Expression visitPrefixExpression(PrefixExpression node) { | 1233 JS.Expression visitPrefixExpression(PrefixExpression node) { |
| 1189 var op = node.operator; | 1234 var op = node.operator; |
| 1190 var expr = node.operand; | 1235 var expr = node.operand; |
| 1191 | 1236 |
| 1192 var dispatchType = rules.getStaticType(expr); | 1237 var dispatchType = rules.getStaticType(expr); |
| 1193 if (unaryOperationIsPrimitive(dispatchType)) { | 1238 if (unaryOperationIsPrimitive(dispatchType)) { |
| 1194 // TODO(vsm): When do Dart ops not map to JS? | 1239 // TODO(vsm): When do Dart ops not map to JS? |
| 1195 return js.call('$op#', notNull(expr)); | 1240 return js.call('$op#', notNull(expr)); |
| 1196 } else { | 1241 } else { |
| 1197 // TODO(vsm): Figure out operator calling convention / dispatch. | 1242 // Increment or decrement must be numerical |
| 1198 return visitExpression(node); | 1243 if (op.lexeme == '++' || op.lexeme == '--') { |
| 1244 var result = _emitIncrement(op, expr, prefix: true); | |
| 1245 return (result != null) ? result : visitExpression(node); | |
| 1246 } | |
| 1247 } | |
| 1248 // TODO(vsm): Statically invoke the operator if the type is known. | |
| 1249 // Fall back to dynamic dispatch. | |
| 1250 var opString = _jsMemberName(op.lexeme, unary: true); | |
| 1251 if (rules.isDynamicTarget(expr)) { | |
| 1252 // dynamic dispatch | |
| 1253 return js.call('dart.dunary(#, #)', [opString, _visit(expr)]); | |
| 1254 } else if (_isJSBuiltinType(dispatchType)) { | |
| 1255 return js.call( | |
| 1256 '#.#(#)', [_emitTypeName(dispatchType), opString, _visit(expr)]); | |
| 1257 } else { | |
| 1258 // Generic static-dispatch, user-defined operator code path. | |
| 1259 return js.call('#.#()', [_visit(expr), opString]); | |
| 1199 } | 1260 } |
| 1200 } | 1261 } |
| 1201 | 1262 |
| 1202 // Cascades can contain [IndexExpression], [MethodInvocation] and | 1263 // Cascades can contain [IndexExpression], [MethodInvocation] and |
| 1203 // [PropertyAccess]. The code generation for those is handled in their | 1264 // [PropertyAccess]. The code generation for those is handled in their |
| 1204 // respective visit methods. | 1265 // respective visit methods. |
| 1205 @override | 1266 @override |
| 1206 JS.Node visitCascadeExpression(CascadeExpression node) { | 1267 JS.Node visitCascadeExpression(CascadeExpression node) { |
| 1207 var savedCascadeTemp = _cascadeTarget; | 1268 var savedCascadeTemp = _cascadeTarget; |
| 1208 | 1269 |
| (...skipping 754 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1963 | 2024 |
| 1964 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2025 // TODO(jmesserly): in many cases marking the end will be unncessary. |
| 1965 printer.mark(_location(node.end)); | 2026 printer.mark(_location(node.end)); |
| 1966 } | 2027 } |
| 1967 | 2028 |
| 1968 String _getIdentifier(AstNode node) { | 2029 String _getIdentifier(AstNode node) { |
| 1969 if (node is SimpleIdentifier) return node.name; | 2030 if (node is SimpleIdentifier) return node.name; |
| 1970 return null; | 2031 return null; |
| 1971 } | 2032 } |
| 1972 } | 2033 } |
| OLD | NEW |