dart2js: generate simple '==' in more cases
Change-Id: I9f8d674f8346b6c3ecf5232c369b3953053d261f
Reviewed-on: https://dart-review.googlesource.com/74964
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index ca08263..6608085 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -82,18 +82,36 @@
return node;
}
+ /// Returns the single JavaScript comparison (`==` or `===`) if that
+ /// implements `identical(left, right)`, or returns `null` if a more complex
+ /// expression is needed.
String simpleOp(HInstruction left, HInstruction right) {
- // Returns the single identity comparison (== or ===) or null if a more
- // complex expression is required.
AbstractValue leftType = left.instructionType;
AbstractValue rightType = right.instructionType;
if (_abstractValueDomain.canBeNull(leftType) &&
_abstractValueDomain.canBeNull(rightType)) {
- if (left.isConstantNull() ||
- right.isConstantNull() ||
- (left.isPrimitive(_abstractValueDomain) && leftType == rightType)) {
+ // Can't use `===` on Dart `null` since it is implemented by JavaScript
+ // `null` and `undefined`.
+ if (left.isConstantNull() || right.isConstantNull()) {
return '==';
}
+ if (_abstractValueDomain.isNumberOrNull(leftType) &&
+ _abstractValueDomain.isNumberOrNull(rightType)) {
+ return '==';
+ }
+ if (_abstractValueDomain.isStringOrNull(leftType) &&
+ _abstractValueDomain.isStringOrNull(rightType)) {
+ return '==';
+ }
+ if (_abstractValueDomain.isBooleanOrNull(leftType) &&
+ _abstractValueDomain.isBooleanOrNull(rightType)) {
+ return '==';
+ }
+
+ // TODO(34439): There are more cases that can compile to `==` without
+ // triggering a conversion in the JavaScript evaluation. `==` will work
+ // for most Dart objects, but we have to ensure neither side can be a
+ // JavaScript Number, String, Symbol or Boolean.
return null;
}
return '===';
diff --git a/tests/compiler/dart2js/codegen/equals_test.dart b/tests/compiler/dart2js/codegen/equals_test.dart
new file mode 100644
index 0000000..9395108
--- /dev/null
+++ b/tests/compiler/dart2js/codegen/equals_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library equals_test;
+
+import 'dart:async';
+import 'package:async_helper/async_helper.dart';
+import '../helpers/compiler_helper.dart';
+
+const String TEST1 = r"""
+foo(int a) {
+ return a == null;
+ // present: 'a == null'
+ // absent: 'eq'
+}
+""";
+
+const String TEST2 = r"""
+foo(int a) {
+ return a == 123;
+ // present: 'a === 123'
+ // absent: 'eq'
+}
+""";
+
+const String TEST3 = r"""
+foo(int a, int b) {
+ return a == b;
+ // present: 'a == b'
+ // absent: 'eq'
+ // absent: '==='
+}
+""";
+
+const String TEST4 = r"""
+foo(String a, String b) {
+ return a == b;
+ // present: 'a == b'
+ // absent: 'eq'
+ // absent: '==='
+}
+""";
+
+// Comparable includes String and int, so can't be compared with `a == b` since
+// that will convert an operand to make `2 =="2"` true.
+const String TEST5 = r"""
+foo(Comparable a, Comparable b) {
+ return a == b;
+ // present: 'a === b'
+ // present: 'a == null'
+ // present: 'b == null'
+ // absent: 'eq'
+ // absent: 'a == b'
+}
+""";
+
+const String TEST6 = r"""
+foo(dynamic a, dynamic b) {
+ return a == b;
+ // present: 'eq'
+ // absent: '=='
+}
+""";
+
+main() {
+ runTests() async {
+ Future check(String test) {
+ return compile(test, entry: 'foo', check: checkerForAbsentPresent(test));
+ }
+
+ await check(TEST1);
+ await check(TEST2);
+ await check(TEST3);
+ await check(TEST4);
+ await check(TEST5);
+ await check(TEST6);
+ }
+
+ asyncTest(() async {
+ print('--test from kernel------------------------------------------------');
+ await runTests();
+ });
+}