fix #34450, implement boolean bitwise operators in dartdevc
These will be added in https://dart-review.googlesource.com/c/sdk/+/74664.
Change-Id: I9712d8f9df72e686dd49e0b5198aa37e17815eb5
Reviewed-on: https://dart-review.googlesource.com/71228
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Vijay Menon <vsm@google.com>
Commit-Queue: Jenny Messerly <jmesserly@google.com>
diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
index 9545296..ef0d000 100644
--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
@@ -4635,10 +4635,12 @@
if (jsTypeRep.binaryOperationIsPrimitive(leftType, rightType) ||
leftType == types.stringType && op.type == TokenType.PLUS) {
- // special cases where we inline the operation
- // 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.
+ // Inline operations on primitive types where possible.
+ // TODO(jmesserly): inline these from dart:core instead of hardcoding
+ // the implementation details here.
+
+ /// Emits an inlined binary operation using the JS [code], adding null
+ /// checks if needed to ensure we throw the appropriate error.
JS.Expression binary(String code) {
return js.call(code, [notNull(left), notNull(right)])
..sourceInformation = _getLocation(node.operator.offset);
@@ -4648,6 +4650,16 @@
return _coerceBitOperationResultToUnsigned(node, binary(code));
}
+ /// Similar to [binary] but applies a boolean conversion to the right
+ /// operand, to match the boolean bitwise operators in dart:core.
+ ///
+ /// Short circuiting operators should not be used in [code], because the
+ /// null checks for both operands must happen unconditionally.
+ JS.Expression bitwiseBool(String code) {
+ return js.call(code, [notNull(left), _visitTest(right)])
+ ..sourceInformation = _getLocation(node.operator.offset);
+ }
+
switch (op.type) {
case TokenType.TILDE_SLASH:
// `a ~/ b` is equivalent to `(a / b).truncate()`
@@ -4662,13 +4674,19 @@
return operatorCall();
case TokenType.AMPERSAND:
- return bitwise('# & #');
+ return jsTypeRep.isBoolean(leftType)
+ ? bitwiseBool('!!(# & #)')
+ : bitwise('# & #');
case TokenType.BAR:
- return bitwise('# | #');
+ return jsTypeRep.isBoolean(leftType)
+ ? bitwiseBool('!!(# | #)')
+ : bitwise('# | #');
case TokenType.CARET:
- return bitwise('# ^ #');
+ return jsTypeRep.isBoolean(leftType)
+ ? bitwiseBool('# !== #')
+ : bitwise('# ^ #');
case TokenType.GT_GT:
int shiftCount = _asIntInRange(right, 0, 31);
diff --git a/pkg/dev_compiler/lib/src/compiler/js_typerep.dart b/pkg/dev_compiler/lib/src/compiler/js_typerep.dart
index dda2d29..a7bd241 100644
--- a/pkg/dev_compiler/lib/src/compiler/js_typerep.dart
+++ b/pkg/dev_compiler/lib/src/compiler/js_typerep.dart
@@ -89,6 +89,8 @@
bool isNumber(DartType type) => typeFor(type) is JSNumber;
+ bool isBoolean(DartType type) => typeFor(type) is JSBoolean;
+
/// Is this type known to be represented as Object or Null in JS.
bool isObjectOrNull(DartType t) {
var rep = typeFor(t);
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index b23e36f..ff4086c 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -4043,10 +4043,12 @@
if (_typeRep.binaryOperationIsPrimitive(leftType, rightType) ||
leftType == types.stringType && op == '+') {
- // special cases where we inline the operation
- // 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.
+ // Inline operations on primitive types where possible.
+ // TODO(jmesserly): inline these from dart:core instead of hardcoding
+ // the implementation details here.
+
+ /// Emits an inlined binary operation using the JS [code], adding null
+ /// checks if needed to ensure we throw the appropriate error.
JS.Expression binary(String code) {
return js.call(code, [notNull(left), notNull(right)]);
}
@@ -4055,6 +4057,15 @@
return _coerceBitOperationResultToUnsigned(node, binary(code));
}
+ /// Similar to [binary] but applies a boolean conversion to the right
+ /// operand, to match the boolean bitwise operators in dart:core.
+ ///
+ /// Short circuiting operators should not be used in [code], because the
+ /// null checks for both operands must happen unconditionally.
+ JS.Expression bitwiseBool(String code) {
+ return js.call(code, [notNull(left), _visitTest(right)]);
+ }
+
switch (op) {
case '~/':
// `a ~/ b` is equivalent to `(a / b).truncate()`
@@ -4070,13 +4081,19 @@
return _emitOperatorCall(left, target, op, [right]);
case '&':
- return bitwise('# & #');
+ return _typeRep.isBoolean(leftType)
+ ? bitwiseBool('!!(# & #)')
+ : bitwise('# & #');
case '|':
- return bitwise('# | #');
+ return _typeRep.isBoolean(leftType)
+ ? bitwiseBool('!!(# | #)')
+ : bitwise('# | #');
case '^':
- return bitwise('# ^ #');
+ return _typeRep.isBoolean(leftType)
+ ? bitwiseBool('# !== #')
+ : bitwise('# ^ #');
case '>>':
int shiftCount = _asIntInRange(right, 0, 31);