[dart:js_interop] Change several operators back to JSBoolean

Related issues:
https://github.com/dart-lang/sdk/issues/55024
https://github.com/dart-lang/sdk/issues/55267

These operators were initially broken in 3.3 and were exposed
as returning JSBoolean but implemented as returning bool. They
were fixed to return bool in the public API, but we should
prefer to have them return JS types as they're likely to be used
in cases where implicit conversions are not useful.

CoreLibraryReviewExempt: Fixing type mismatch in backend-specific library.
Change-Id: I3b0e60550dcac78918f8399d11238dcfa34982cd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/359180
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Srujan Gaddam 2024-03-25 17:05:36 +00:00 committed by Commit Queue
parent 7c51a86267
commit 47f08fb143
5 changed files with 77 additions and 89 deletions

View file

@ -45,8 +45,9 @@ advantage of these improvements, set your package's
- Fixes an issue with several comparison operators in `JSAnyOperatorExtension`
that were declared to return `JSBoolean` but really returned `bool`. This led
to runtime errors when trying to use the return values. Their return types are
now `bool` for convenience. See issues [#55024] for more details.
to runtime errors when trying to use the return values. The implementation now
returns a `JSBoolean` to align with the interface. See issues [#55024] for
more details.
[#55024]: https://github.com/dart-lang/sdk/issues/55024

View file

@ -401,36 +401,40 @@ extension JSAnyOperatorExtension on JSAny? {
@patch
@pragma('dart2js:prefer-inline')
bool greaterThan(JSAny? any) => js_util.greaterThan(this, any);
JSBoolean greaterThan(JSAny? any) =>
js_util.greaterThan(this, any) as JSBoolean;
@patch
@pragma('dart2js:prefer-inline')
bool greaterThanOrEqualTo(JSAny? any) =>
js_util.greaterThanOrEqual(this, any);
JSBoolean greaterThanOrEqualTo(JSAny? any) =>
js_util.greaterThanOrEqual(this, any) as JSBoolean;
@patch
@pragma('dart2js:prefer-inline')
bool lessThan(JSAny? any) => js_util.lessThan(this, any);
JSBoolean lessThan(JSAny? any) => js_util.lessThan(this, any) as JSBoolean;
@patch
@pragma('dart2js:prefer-inline')
bool lessThanOrEqualTo(JSAny? any) => js_util.lessThanOrEqual(this, any);
JSBoolean lessThanOrEqualTo(JSAny? any) =>
js_util.lessThanOrEqual(this, any) as JSBoolean;
@patch
@pragma('dart2js:prefer-inline')
bool equals(JSAny? any) => js_util.equal(this, any);
JSBoolean equals(JSAny? any) => js_util.equal(this, any) as JSBoolean;
@patch
@pragma('dart2js:prefer-inline')
bool notEquals(JSAny? any) => js_util.notEqual(this, any);
JSBoolean notEquals(JSAny? any) => js_util.notEqual(this, any) as JSBoolean;
@patch
@pragma('dart2js:prefer-inline')
bool strictEquals(JSAny? any) => js_util.strictEqual(this, any);
JSBoolean strictEquals(JSAny? any) =>
js_util.strictEqual(this, any) as JSBoolean;
@patch
@pragma('dart2js:prefer-inline')
bool strictNotEquals(JSAny? any) => js_util.strictNotEqual(this, any);
JSBoolean strictNotEquals(JSAny? any) =>
js_util.strictNotEqual(this, any) as JSBoolean;
@patch
@pragma('dart2js:prefer-inline')

View file

@ -488,52 +488,44 @@ extension JSAnyOperatorExtension on JSAny? {
'(o, a) => o ** a', this.toExternRef, any.toExternRef));
@patch
bool greaterThan(JSAny? any) =>
JSBoolean greaterThan(JSAny? any) =>
_boxNonNullable<JSBoolean>(js_helper.JS<WasmExternRef?>(
'(o, a) => o > a', this.toExternRef, any.toExternRef))
.toDart;
'(o, a) => o > a', this.toExternRef, any.toExternRef));
@patch
bool greaterThanOrEqualTo(JSAny? any) =>
JSBoolean greaterThanOrEqualTo(JSAny? any) =>
_boxNonNullable<JSBoolean>(js_helper.JS<WasmExternRef?>(
'(o, a) => o >= a', this.toExternRef, any.toExternRef))
.toDart;
'(o, a) => o >= a', this.toExternRef, any.toExternRef));
@patch
bool lessThan(JSAny? any) =>
JSBoolean lessThan(JSAny? any) =>
_boxNonNullable<JSBoolean>(js_helper.JS<WasmExternRef?>(
'(o, a) => o < a', this.toExternRef, any.toExternRef))
.toDart;
'(o, a) => o < a', this.toExternRef, any.toExternRef));
@patch
bool lessThanOrEqualTo(JSAny? any) =>
JSBoolean lessThanOrEqualTo(JSAny? any) =>
_boxNonNullable<JSBoolean>(js_helper.JS<WasmExternRef?>(
'(o, a) => o <= a', this.toExternRef, any.toExternRef))
.toDart;
'(o, a) => o <= a', this.toExternRef, any.toExternRef));
@patch
bool equals(JSAny? any) =>
JSBoolean equals(JSAny? any) =>
_boxNonNullable<JSBoolean>(js_helper.JS<WasmExternRef?>(
'(o, a) => o == a', this.toExternRef, any.toExternRef))
.toDart;
'(o, a) => o == a', this.toExternRef, any.toExternRef));
@patch
bool notEquals(JSAny? any) =>
JSBoolean notEquals(JSAny? any) =>
_boxNonNullable<JSBoolean>(js_helper.JS<WasmExternRef?>(
'(o, a) => o != a', this.toExternRef, any.toExternRef))
.toDart;
'(o, a) => o != a', this.toExternRef, any.toExternRef));
@patch
bool strictEquals(JSAny? any) =>
JSBoolean strictEquals(JSAny? any) =>
_boxNonNullable<JSBoolean>(js_helper.JS<WasmExternRef?>(
'(o, a) => o === a', this.toExternRef, any.toExternRef))
.toDart;
'(o, a) => o === a', this.toExternRef, any.toExternRef));
@patch
bool strictNotEquals(JSAny? any) =>
JSBoolean strictNotEquals(JSAny? any) =>
_boxNonNullable<JSBoolean>(js_helper.JS<WasmExternRef?>(
'(o, a) => o !== a', this.toExternRef, any.toExternRef))
.toDart;
'(o, a) => o !== a', this.toExternRef, any.toExternRef));
@patch
JSNumber unsignedRightShift(JSAny? any) =>

View file

@ -1005,8 +1005,7 @@ extension StringToJSString on String {
/// external operator int [](int key);
/// ```
///
/// Operators that always return number or boolean values use the Dart type
/// instead of a JS type for convenience as the conversions are inexpensive.
/// All operators in this extension shall accept and return only JS types.
// TODO(srujzs): Add more as needed. For now, we just expose the ones needed to
// migrate from `dart:js_util`.
extension JSAnyOperatorExtension on JSAny? {
@ -1033,28 +1032,28 @@ extension JSAnyOperatorExtension on JSAny? {
// Comparison operators.
/// The result of <code>[this] > [any]</code> in JavaScript.
external bool greaterThan(JSAny? any);
external JSBoolean greaterThan(JSAny? any);
/// The result of <code>[this] >= [any]</code> in JavaScript.
external bool greaterThanOrEqualTo(JSAny? any);
external JSBoolean greaterThanOrEqualTo(JSAny? any);
/// The result of <code>[this] < [any]</code> in JavaScript.
external bool lessThan(JSAny? any);
external JSBoolean lessThan(JSAny? any);
/// The result of <code>[this] <= [any]</code> in JavaScript.
external bool lessThanOrEqualTo(JSAny? any);
external JSBoolean lessThanOrEqualTo(JSAny? any);
/// The result of <code>[this] == [any]</code> in JavaScript.
external bool equals(JSAny? any);
external JSBoolean equals(JSAny? any);
/// The result of <code>[this] != [any]</code> in JavaScript.
external bool notEquals(JSAny? any);
external JSBoolean notEquals(JSAny? any);
/// The result of <code>[this] === [any]</code> in JavaScript.
external bool strictEquals(JSAny? any);
external JSBoolean strictEquals(JSAny? any);
/// The result of <code>[this] !== [any]</code> in JavaScript.
external bool strictNotEquals(JSAny? any);
external JSBoolean strictNotEquals(JSAny? any);
// Bitwise operators.
@ -1071,9 +1070,11 @@ extension JSAnyOperatorExtension on JSAny? {
external JSAny? or(JSAny? any);
/// The result of <code>![this]</code> in JavaScript.
// TODO(srujzs): Change this to JSBoolean to be consistent.
external bool get not;
/// The result of <code>!![this]</code> in JavaScript.
// TODO(srujzs): Change this to JSBoolean to be consistent.
external bool get isTruthy;
}

View file

@ -57,16 +57,6 @@ extension on int {
JSBigInt get toBigInt => BigInt(this.toString());
}
// Use a helper for `Expect.isTrue` and `Expect.isFalse` as those methods don't
// differentiate between `JSBoolean` and `bool`.
void expectTrue(bool value) {
Expect.isTrue(value);
}
void expectFalse(bool value) {
Expect.isFalse(value);
}
int toInt(JSAny any) => (any as JSNumber).toDartInt;
void dartJsInteropOperatorsTest() {
@ -86,39 +76,39 @@ void dartJsInteropOperatorsTest() {
final t = true.toJS;
final f = false.toJS;
// Equality attempts to coerce, whereas strict equality does not.
expectTrue(t.equals(1.toJS));
expectFalse(t.notEquals(1.toJS));
expectFalse(t.strictEquals(1.toJS));
expectTrue(t.strictNotEquals(1.toJS));
expectFalse((t.and(f) as JSBoolean).toDart);
expectTrue((t.or(f) as JSBoolean).toDart);
expectFalse(t.not);
expectTrue(t.isTruthy);
expectFalse(i10.lessThan(i10));
expectTrue(i10.lessThanOrEqualTo(i10));
expectFalse(i10.greaterThan(i10));
expectTrue(i10.greaterThanOrEqualTo(i10));
Expect.isTrue(t.equals(1.toJS).toDart);
Expect.isFalse(t.notEquals(1.toJS).toDart);
Expect.isFalse(t.strictEquals(1.toJS).toDart);
Expect.isTrue(t.strictNotEquals(1.toJS).toDart);
Expect.isFalse((t.and(f) as JSBoolean).toDart);
Expect.isTrue((t.or(f) as JSBoolean).toDart);
Expect.isFalse(t.not);
Expect.isTrue(t.isTruthy);
Expect.isFalse(i10.lessThan(i10).toDart);
Expect.isTrue(i10.lessThanOrEqualTo(i10).toDart);
Expect.isFalse(i10.greaterThan(i10).toDart);
Expect.isTrue(i10.greaterThanOrEqualTo(i10).toDart);
// Nulls.
expect(toInt(null.add(null)), 0);
expect(toInt(null.subtract(null)), 0);
expect(toInt(null.multiply(null)), 0);
expectTrue(isNaN(null.divide(null)));
expectTrue(isNaN(null.modulo(null)));
Expect.isTrue(isNaN(null.divide(null)));
Expect.isTrue(isNaN(null.modulo(null)));
expect(toInt(null.exponentiate(null)), 1);
expect(toInt(null.unsignedRightShift(null)), 0);
expectTrue(null.equals(null));
expectFalse(null.notEquals(null));
expectTrue(null.strictEquals(null));
expectFalse(null.strictNotEquals(null));
Expect.isTrue(null.equals(null).toDart);
Expect.isFalse(null.notEquals(null).toDart);
Expect.isTrue(null.strictEquals(null).toDart);
Expect.isFalse(null.strictNotEquals(null).toDart);
expect(null.and(null), null);
expect(null.or(null), null);
expectTrue(null.not);
expectFalse(null.isTruthy);
expectFalse(null.lessThan(null));
expectTrue(null.lessThanOrEqualTo(null));
expectFalse(null.greaterThan(null));
expectTrue(null.greaterThanOrEqualTo(null));
Expect.isTrue(null.not);
Expect.isFalse(null.isTruthy);
Expect.isFalse(null.lessThan(null).toDart);
Expect.isTrue(null.lessThanOrEqualTo(null).toDart);
Expect.isFalse(null.greaterThan(null).toDart);
Expect.isTrue(null.greaterThanOrEqualTo(null).toDart);
// Different types.
final b10 = 10.toBigInt;
@ -132,18 +122,18 @@ void dartJsInteropOperatorsTest() {
// returns a number.
expect(toInt(t.unsignedRightShift(f)), 1);
final b1 = 1.toBigInt;
expectTrue(b1.equals(t));
expectFalse(b1.notEquals(t));
expectFalse(b1.strictEquals(t));
expectTrue(b1.strictNotEquals(t));
Expect.isTrue(b1.equals(t).toDart);
Expect.isFalse(b1.notEquals(t).toDart);
Expect.isFalse(b1.strictEquals(t).toDart);
Expect.isTrue(b1.strictNotEquals(t).toDart);
expect(b10.and(b1), b1);
expect(b10.or(b1), b10);
expectFalse(b10.not);
expectTrue(b10.isTruthy);
expectFalse(b10.lessThan(b10));
expectTrue(b10.lessThanOrEqualTo(b10));
expectFalse(b10.greaterThan(b10));
expectTrue(b10.greaterThanOrEqualTo(b10));
Expect.isFalse(b10.not);
Expect.isTrue(b10.isTruthy);
Expect.isFalse(b10.lessThan(b10).toDart);
Expect.isTrue(b10.lessThanOrEqualTo(b10).toDart);
Expect.isFalse(b10.greaterThan(b10).toDart);
Expect.isTrue(b10.greaterThanOrEqualTo(b10).toDart);
}
void main() {