Index: lib/src/compiler/code_generator.dart |
diff --git a/lib/src/compiler/code_generator.dart b/lib/src/compiler/code_generator.dart |
index d6e8a5d91e68e0603021bd950b6397c45b103203..0ae7e0a0561aa998bf21d9ab1241cc788f629520 100644 |
--- a/lib/src/compiler/code_generator.dart |
+++ b/lib/src/compiler/code_generator.dart |
@@ -2810,21 +2810,61 @@ class CodeGenerator extends GeneralizingAstVisitor |
// these values are assumed to be non-null (determined by the checker) |
// TODO(jmesserly): it would be nice to just inline the method from core, |
// instead of special cases here. |
- if (op.type == TokenType.TILDE_SLASH) { |
- // `a ~/ b` is equivalent to `(a / b).truncate()` |
- var div = AstBuilder.binaryExpression(left, '/', right) |
- ..staticType = node.staticType; |
- return _emitSend(div, 'truncate', []); |
- } else { |
- // TODO(vsm): When do Dart ops not map to JS? |
- code = '# $op #'; |
+ JS.Expression binary(String code) { |
+ return js.call(code, [notNull(left), notNull(right)]); |
+ } |
+ |
+ JS.Expression bitwise(String code) { |
+ return _coerceBitOperationResultToUnsigned(node, binary(code)); |
+ } |
+ |
+ switch (op.type) { |
+ case TokenType.TILDE_SLASH: |
Jennifer Messerly
2016/04/28 20:08:52
this looks great, love the refactoring!
|
+ // `a ~/ b` is equivalent to `(a / b).truncate()` |
+ var div = AstBuilder.binaryExpression(left, '/', right) |
+ ..staticType = node.staticType; |
+ return _emitSend(div, 'truncate', []); |
+ |
+ case TokenType.PERCENT: |
+ // TODO(sra): We can generate `a % b + 0` if both are non-negative |
+ // (the `+ 0` is to coerce -0.0 to 0). |
+ return _emitSend(left, op.lexeme, [right]); |
+ |
+ case TokenType.AMPERSAND: |
+ return bitwise('# & #'); |
+ |
+ case TokenType.BAR: |
+ return bitwise('# | #'); |
+ |
+ case TokenType.CARET: |
+ return bitwise('# ^ #'); |
+ |
+ case TokenType.GT_GT: |
+ // TODO(sra): Detect when JS shift does the right thing. |
+ return _emitSend(left, op.lexeme, [right]); |
+ |
+ case TokenType.LT_LT: |
+ // TODO(sra): Detect when JS shift does the right thing. |
+ return _emitSend(left, op.lexeme, [right]); |
+ |
+ default: |
+ // TODO(vsm): When do Dart ops not map to JS? |
+ return binary('# $op #'); |
} |
- return js.call(code, [notNull(left), notNull(right)]); |
} |
return _emitSend(left, op.lexeme, [right]); |
} |
+ JS.Expression _coerceBitOperationResultToUnsigned( |
+ Expression node, JS.Expression operation) { |
+ // Bit operations are coerced to values on [0, 2^32). The coercion changes |
Jennifer Messerly
2016/04/28 20:08:52
move this to be a doc comment?
|
+ // the interpretation of the the 32-bit value from signed to unsigned. Most |
+ // JavaScript operations interpret their operands as signed and generate |
+ // signed results. |
+ return js.call('# >>> 0', operation); |
+ } |
+ |
/// If the type [t] is [int] or [double], or a type parameter |
/// bounded by [int], [double] or [num] returns [num]. |
/// Otherwise returns [t]. |
@@ -3001,9 +3041,17 @@ class CodeGenerator extends GeneralizingAstVisitor |
var dispatchType = getStaticType(expr); |
if (unaryOperationIsPrimitive(dispatchType)) { |
+ if (op.lexeme == '~') { |
+ if (_isNumberInJS(dispatchType)) { |
+ JS.Expression jsExpr = js.call('~#', notNull(expr)); |
+ return _coerceBitOperationResultToUnsigned(node, jsExpr); |
+ } |
+ return _emitSend(expr, op.lexeme[0], []); |
+ } |
if (!isNullable(expr)) { |
return js.call('$op#', _visit(expr)); |
- } else if (op.lexeme == '++' || op.lexeme == '--') { |
+ } |
+ if (op.lexeme == '++' || op.lexeme == '--') { |
// We need a null check, so the increment must be expanded out. |
var vars = <JS.MetaLetVariable, JS.Expression>{}; |
var x = _bindLeftHandSide(vars, expr, context: expr); |
@@ -3014,9 +3062,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
..staticType = getStaticType(expr); |
return new JS.MetaLet(vars, [_emitSet(x, increment)]); |
- } else { |
- return js.call('$op#', notNull(expr)); |
} |
+ return js.call('$op#', notNull(expr)); |
} |
if (op.lexeme == '++' || op.lexeme == '--') { |