Chromium Code Reviews| Index: lib/src/codegen/js_codegen.dart |
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
| index cadeb9090c0f1e429e6bb906ea58913be5bbd7f2..d0981c32aadec08e504e97dc801530608dad4b04 100644 |
| --- a/lib/src/codegen/js_codegen.dart |
| +++ b/lib/src/codegen/js_codegen.dart |
| @@ -698,10 +698,27 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| return result; |
| } |
| + JS.Node _emitDPutIfDynamic( |
| + Expression target, SimpleIdentifier id, Expression rhs) { |
| + if (rules.isDynamicTarget(target)) { |
| + return js.call('dart.dput(#, #, #)', [ |
| + _visit(target), |
| + js.string(id.name, "'"), |
| + _visit(rhs) |
| + ]); |
| + } else { |
| + return null; |
| + } |
| + } |
| + |
| @override |
| JS.Node visitAssignmentExpression(AssignmentExpression node) { |
| var lhs = node.leftHandSide; |
| var rhs = node.rightHandSide; |
| + return _emitAssignment(lhs, rhs, node.parent); |
| + } |
| + |
| + JS.Node _emitAssignment(Expression lhs, Expression rhs, [AstNode parent]) { |
| if (lhs is IndexExpression) { |
| String code; |
| var target = _getTarget(lhs); |
| @@ -714,17 +731,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| } |
| if (lhs is PropertyAccess) { |
| - var target = _getTarget(lhs); |
| - if (rules.isDynamicTarget(target)) { |
| - return js.call('dart.dput(#, #, #)', [ |
| - _visit(target), |
| - js.string(lhs.propertyName.name, "'"), |
| - _visit(rhs) |
| - ]); |
| - } |
| + var result = _emitDPutIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); |
| + if (result != null) return result; |
| + } else if (lhs is PrefixedIdentifier) { |
| + // 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
|
| + var result = _emitDPutIfDynamic(lhs.prefix, lhs.identifier, rhs); |
| + if (result != null) return result; |
| } |
| - if (node.parent is ExpressionStatement && |
| + if (parent is ExpressionStatement && |
| rhs is CascadeExpression && |
| _isStateless(lhs, rhs)) { |
| // Special case: cascade assignment to a variable in a statement. |
| @@ -1169,6 +1184,35 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| bool _isNull(Expression expr) => expr is NullLiteral; |
| + JS.Expression _emitIncrement(Token op, Expression expr, {bool prefix}) { |
| + assert(prefix != null); |
| + |
| + // TODO(vsm): If the expression has a side effect, we need something different. |
|
Jennifer Messerly
2015/03/04 01:09:31
long line
vsm
2015/03/04 14:59:27
removed
|
| + 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
|
| + (expr is! SimpleIdentifier || |
| + (expr as SimpleIdentifier).staticElement is! LocalVariableElement)) { |
| + return null; |
| + } |
| + |
| + // TODO(vsm): Should we generate a runtime helper for this? |
| + var tmp = '_'; |
| + 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.
|
| + var id = |
| + new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, tmp, 0)); |
| + id.staticElement = new LocalVariableElementImpl.forNode(id); |
| + id.staticType = expr.staticType; |
| + var assignment = _emitAssignment(expr, id); |
| + return js.call(''' |
| +(function () { |
| + var $tmp = #; |
| + if (typeof($tmp) != 'number') throw 'number expected'; |
| + var result = $applied; |
| + #; |
| + return result; |
| +})() |
| +''', [expr.accept(this), assignment]); |
| + } |
| + |
| @override |
| JS.Expression visitPostfixExpression(PostfixExpression node) { |
| var op = node.operator; |
| @@ -1179,8 +1223,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| // TODO(vsm): When do Dart ops not map to JS? |
| return js.call('#$op', notNull(expr)); |
| } else { |
| - // TODO(vsm): Figure out operator calling convention / dispatch. |
| - return visitExpression(node); |
| + assert(op.lexeme == '++' || op.lexeme == '--'); |
| + var result = _emitIncrement(op, expr, prefix: false); |
| + return (result != null) ? result : visitExpression(node); |
| } |
| } |
| @@ -1194,8 +1239,24 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| // TODO(vsm): When do Dart ops not map to JS? |
| return js.call('$op#', notNull(expr)); |
| } else { |
| - // TODO(vsm): Figure out operator calling convention / dispatch. |
| - return visitExpression(node); |
| + // Increment or decrement must be numerical |
| + if (op.lexeme == '++' || op.lexeme == '--') { |
| + var result = _emitIncrement(op, expr, prefix: true); |
| + return (result != null) ? result : visitExpression(node); |
| + } |
| + } |
| + // TODO(vsm): Statically invoke the operator if the type is known. |
| + // Fall back to dynamic dispatch. |
| + var opString = _jsMemberName(op.lexeme, unary: true); |
| + if (rules.isDynamicTarget(expr)) { |
| + // dynamic dispatch |
| + return js.call('dart.dunary(#, #)', [opString, _visit(expr)]); |
| + } else if (_isJSBuiltinType(dispatchType)) { |
| + return js.call( |
| + '#.#(#)', [_emitTypeName(dispatchType), opString, _visit(expr)]); |
| + } else { |
| + // Generic static-dispatch, user-defined operator code path. |
| + return js.call('#.#()', [_visit(expr), opString]); |
| } |
| } |