mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:19:49 +00:00
Support invoking JS operators through dart:js_util
.
Some JS functionality is only exposed through operators, such as implicit type conversions and BigInt arithmetic. Other than requiring some minor additions for the exponentiation (`**`) operator, supporting these through `dart:js_util` is trivial. Change-Id: I010674303e4f99b42d43b73095de69b8ddcdeb47 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/242680 Reviewed-by: Joshua Litt <joshualitt@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
parent
3458b041df
commit
c50157e099
|
@ -678,6 +678,11 @@ class SizeEstimator implements NodeVisitor {
|
|||
// We cannot remove parenthesis for "*" because of precision issues.
|
||||
rightPrecedenceRequirement = UNARY;
|
||||
break;
|
||||
case "**":
|
||||
leftPrecedenceRequirement = EXPONENTIATION;
|
||||
// We cannot remove parenthesis for "**" because of precision issues.
|
||||
rightPrecedenceRequirement = UNARY;
|
||||
break;
|
||||
default:
|
||||
throw UnsupportedError("Forgot operator: $op");
|
||||
}
|
||||
|
|
|
@ -258,4 +258,12 @@ void main() {
|
|||
bar() {
|
||||
}
|
||||
}""");
|
||||
|
||||
// Exponentiation operator.
|
||||
testExpression("a ** b");
|
||||
//* Parsed with incorrect association:
|
||||
testExpression("a ** b ** c", "(a ** b) ** c");
|
||||
testExpression("(a ** b) ** c", "(a ** b) ** c");
|
||||
testExpression("a ** (b ** c)", "a ** b ** c");
|
||||
testExpression("a **= b");
|
||||
}
|
||||
|
|
|
@ -609,6 +609,7 @@ class MiniJsParser {
|
|||
'<<=': 17,
|
||||
'>>=': 17,
|
||||
'>>>=': 17,
|
||||
'**=': 17,
|
||||
'=': 17,
|
||||
'||': 14,
|
||||
'&&': 13,
|
||||
|
@ -632,7 +633,8 @@ class MiniJsParser {
|
|||
'-': 6,
|
||||
'*': 5,
|
||||
'/': 5,
|
||||
'%': 5
|
||||
'%': 5,
|
||||
'**': 4,
|
||||
};
|
||||
static final UNARY_OPERATORS = {
|
||||
'++',
|
||||
|
|
|
@ -1291,6 +1291,8 @@ class Binary extends Expression {
|
|||
int get precedenceLevel {
|
||||
// TODO(floitsch): switch to constant map.
|
||||
switch (op) {
|
||||
case '**':
|
||||
return EXPONENTIATION;
|
||||
case '*':
|
||||
case '/':
|
||||
case '%':
|
||||
|
|
|
@ -28,7 +28,8 @@ const RELATIONAL = EQUALITY + 1;
|
|||
const SHIFT = RELATIONAL + 1;
|
||||
const ADDITIVE = SHIFT + 1;
|
||||
const MULTIPLICATIVE = ADDITIVE + 1;
|
||||
const UNARY = MULTIPLICATIVE + 1;
|
||||
const EXPONENTIATION = MULTIPLICATIVE + 1;
|
||||
const UNARY = EXPONENTIATION + 1;
|
||||
const LEFT_HAND_SIDE = UNARY + 1;
|
||||
const CALL = LEFT_HAND_SIDE;
|
||||
// We always emit `new` with parenthesis, so it uses ACCESS as its precedence.
|
||||
|
|
|
@ -856,6 +856,12 @@ class Printer implements NodeVisitor {
|
|||
// We cannot remove parenthesis for "*" because of precision issues.
|
||||
rightPrecedenceRequirement = UNARY;
|
||||
break;
|
||||
case '**':
|
||||
// 'a ** b ** c' parses as 'a ** (b ** c)', so the left must have higher
|
||||
// precedence.
|
||||
leftPrecedenceRequirement = UNARY;
|
||||
rightPrecedenceRequirement = EXPONENTIATION;
|
||||
break;
|
||||
default:
|
||||
leftPrecedenceRequirement = EXPRESSION;
|
||||
rightPrecedenceRequirement = EXPRESSION;
|
||||
|
|
|
@ -535,6 +535,7 @@ class MiniJsParser {
|
|||
'<<=': 17,
|
||||
'>>=': 17,
|
||||
'>>>=': 17,
|
||||
'**=': 17,
|
||||
'=': 17,
|
||||
'||': 14,
|
||||
'&&': 13,
|
||||
|
@ -558,7 +559,8 @@ class MiniJsParser {
|
|||
'-': 6,
|
||||
'*': 5,
|
||||
'/': 5,
|
||||
'%': 5
|
||||
'%': 5,
|
||||
'**': 4,
|
||||
};
|
||||
static final UNARY_OPERATORS = {
|
||||
'++',
|
||||
|
|
|
@ -1638,6 +1638,8 @@ class Binary extends Expression {
|
|||
int get precedenceLevel {
|
||||
// TODO(floitsch): switch to constant map.
|
||||
switch (op) {
|
||||
case '**':
|
||||
return EXPONENTIATION;
|
||||
case '*':
|
||||
case '/':
|
||||
case '%':
|
||||
|
|
|
@ -16,7 +16,8 @@ const RELATIONAL = EQUALITY + 1;
|
|||
const SHIFT = RELATIONAL + 1;
|
||||
const ADDITIVE = SHIFT + 1;
|
||||
const MULTIPLICATIVE = ADDITIVE + 1;
|
||||
const UNARY = MULTIPLICATIVE + 1;
|
||||
const EXPONENTIATION = MULTIPLICATIVE + 1;
|
||||
const UNARY = EXPONENTIATION + 1;
|
||||
const CALL = UNARY + 1;
|
||||
const LEFT_HAND_SIDE = CALL + 1;
|
||||
const PRIMARY = LEFT_HAND_SIDE + 1;
|
||||
|
|
|
@ -970,6 +970,12 @@ class Printer implements NodeVisitor<void> {
|
|||
// We cannot remove parenthesis for "*" because of precision issues.
|
||||
rightPrecedenceRequirement = UNARY;
|
||||
break;
|
||||
case '**':
|
||||
// 'a ** b ** c' parses as 'a ** (b ** c)', so the left must have higher
|
||||
// precedence.
|
||||
leftPrecedenceRequirement = UNARY;
|
||||
rightPrecedenceRequirement = EXPONENTIATION;
|
||||
break;
|
||||
default:
|
||||
leftPrecedenceRequirement = EXPRESSION;
|
||||
rightPrecedenceRequirement = EXPRESSION;
|
||||
|
|
|
@ -285,6 +285,90 @@ T _callConstructorUnchecked4<T>(
|
|||
'Object', 'new #(#, #, #, #)', constr, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
/// Perform JavaScript addition (`+`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
T add<T>(Object? first, Object? second) {
|
||||
return JS<dynamic>('Object', '# + #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript subtraction (`-`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
T subtract<T>(Object? first, Object? second) {
|
||||
return JS<dynamic>('Object', '# - #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript multiplication (`*`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
T multiply<T>(Object? first, Object? second) {
|
||||
return JS<dynamic>('Object', '# * #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript division (`/`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
T divide<T>(Object? first, Object? second) {
|
||||
return JS<dynamic>('Object', '# / #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript exponentiation (`**`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
T exponentiate<T>(Object? first, Object? second) {
|
||||
return JS<dynamic>('Object', '# ** #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript remainder (`%`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
T modulo<T>(Object? first, Object? second) {
|
||||
return JS<dynamic>('Object', '# % #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript equality comparison (`==`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
bool equal<T>(Object? first, Object? second) {
|
||||
return JS<bool>('bool', '# == #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript strict equality comparison (`===`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
bool strictEqual<T>(Object? first, Object? second) {
|
||||
return JS<bool>('bool', '# === #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript inequality comparison (`!=`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
bool notEqual<T>(Object? first, Object? second) {
|
||||
return JS<bool>('bool', '# != #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript strict inequality comparison (`!==`) on two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
bool strictNotEqual<T>(Object? first, Object? second) {
|
||||
return JS<bool>('bool', '# !== #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript greater than comparison (`>`) of two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
bool greaterThan<T>(Object? first, Object? second) {
|
||||
return JS<bool>('bool', '# > #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript greater than or equal comparison (`>=`) of two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
bool greaterThanOrEqual<T>(Object? first, Object? second) {
|
||||
return JS<bool>('bool', '# >= #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript less than comparison (`<`) of two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
bool lessThan<T>(Object? first, Object? second) {
|
||||
return JS<bool>('bool', '# < #', first, second);
|
||||
}
|
||||
|
||||
/// Perform JavaScript less than or equal comparison (`<=`) of two values.
|
||||
@pragma('dart2js:tryInline')
|
||||
bool lessThanOrEqual<T>(Object? first, Object? second) {
|
||||
return JS<bool>('bool', '# <= #', first, second);
|
||||
}
|
||||
|
||||
/// Exception for when the promise is rejected with a `null` or `undefined`
|
||||
/// value.
|
||||
///
|
||||
|
|
110
tests/lib/js/js_util/bigint_test.dart
Normal file
110
tests/lib/js/js_util/bigint_test.dart
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
// Tests invoking JS BigInt functionality through js_util. This interop
|
||||
// requires usage of the operator functions exposed through js_util.
|
||||
|
||||
@JS()
|
||||
library js_util_bigint_test;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart' as js_util;
|
||||
import 'package:expect/minitest.dart';
|
||||
|
||||
@JS('BigInt')
|
||||
external Object BigInt(Object value);
|
||||
|
||||
main() {
|
||||
group('bigint', () {
|
||||
test('addition', () {
|
||||
final one = BigInt('1');
|
||||
final two = BigInt('2');
|
||||
final three = BigInt('3');
|
||||
expect(js_util.strictEqual(js_util.add(one, two), three), isTrue);
|
||||
expect(js_util.strictEqual(js_util.add(one, one), three), isFalse);
|
||||
});
|
||||
|
||||
test('subtraction', () {
|
||||
final one = BigInt('1');
|
||||
final two = BigInt('2');
|
||||
final three = BigInt('3');
|
||||
expect(js_util.strictEqual(js_util.subtract(three, one), two), isTrue);
|
||||
expect(js_util.strictEqual(js_util.subtract(three, two), two), isFalse);
|
||||
});
|
||||
|
||||
test('multiplication', () {
|
||||
final two = BigInt('2');
|
||||
final four = BigInt('4');
|
||||
expect(js_util.strictEqual(js_util.multiply(two, two), four), isTrue);
|
||||
expect(js_util.strictEqual(js_util.multiply(two, four), four), isFalse);
|
||||
});
|
||||
|
||||
test('division', () {
|
||||
final two = BigInt('2');
|
||||
final four = BigInt('4');
|
||||
expect(js_util.strictEqual(js_util.divide(four, two), two), isTrue);
|
||||
expect(js_util.strictEqual(js_util.divide(four, four), two), isFalse);
|
||||
});
|
||||
|
||||
test('exponentiation', () {
|
||||
final two = BigInt('2');
|
||||
final three = BigInt('3');
|
||||
final nine = BigInt('9');
|
||||
expect(
|
||||
js_util.strictEqual(js_util.exponentiate(three, two), nine), isTrue);
|
||||
expect(js_util.strictEqual(js_util.exponentiate(three, three), nine),
|
||||
isFalse);
|
||||
});
|
||||
|
||||
test('exponentiation2', () {
|
||||
final two = BigInt('2');
|
||||
final three = BigInt('3');
|
||||
final five = BigInt('5');
|
||||
expect(
|
||||
js_util.add(
|
||||
'', js_util.exponentiate(js_util.exponentiate(five, three), two)),
|
||||
'15625');
|
||||
expect(
|
||||
js_util.add(
|
||||
'', js_util.exponentiate(five, js_util.exponentiate(three, two))),
|
||||
'1953125');
|
||||
});
|
||||
|
||||
test('modulo', () {
|
||||
final zero = BigInt('0');
|
||||
final three = BigInt('3');
|
||||
final nine = BigInt('9');
|
||||
expect(js_util.strictEqual(js_util.modulo(nine, three), zero), isTrue);
|
||||
expect(js_util.strictEqual(js_util.modulo(nine, three), nine), isFalse);
|
||||
});
|
||||
|
||||
test('equality', () {
|
||||
final one = BigInt('1');
|
||||
expect(js_util.equal(one, 1), isTrue);
|
||||
expect(js_util.strictEqual(one, 1), isFalse);
|
||||
expect(js_util.notEqual(one, 1), isFalse);
|
||||
expect(js_util.strictNotEqual(one, 1), isTrue);
|
||||
});
|
||||
|
||||
test('comparisons', () {
|
||||
final zero = BigInt('0');
|
||||
final one = BigInt('1');
|
||||
final otherOne = BigInt('1');
|
||||
expect(js_util.greaterThan(one, zero), isTrue);
|
||||
expect(js_util.greaterThan(one, 0), isTrue);
|
||||
expect(js_util.greaterThan(2, one), isTrue);
|
||||
expect(js_util.greaterThanOrEqual(one, otherOne), isTrue);
|
||||
expect(js_util.greaterThanOrEqual(one, 1), isTrue);
|
||||
expect(js_util.greaterThanOrEqual(one, 2), isFalse);
|
||||
|
||||
expect(js_util.lessThan(one, zero), isFalse);
|
||||
expect(js_util.lessThan(zero, one), isTrue);
|
||||
expect(js_util.lessThan(one, 2), isTrue);
|
||||
expect(js_util.lessThan(one, 0), isFalse);
|
||||
expect(js_util.lessThanOrEqual(one, otherOne), isTrue);
|
||||
expect(js_util.lessThanOrEqual(one, 1), isTrue);
|
||||
expect(js_util.lessThanOrEqual(2, one), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
110
tests/lib_2/js/js_util/bigint_test.dart
Normal file
110
tests/lib_2/js/js_util/bigint_test.dart
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
// Tests invoking JS BigInt functionality through js_util. This interop
|
||||
// requires usage of the operator functions exposed through js_util.
|
||||
|
||||
@JS()
|
||||
library js_util_bigint_test;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart' as js_util;
|
||||
import 'package:expect/minitest.dart';
|
||||
|
||||
@JS('BigInt')
|
||||
external Object BigInt(Object value);
|
||||
|
||||
main() {
|
||||
group('bigint', () {
|
||||
test('addition', () {
|
||||
final one = BigInt('1');
|
||||
final two = BigInt('2');
|
||||
final three = BigInt('3');
|
||||
expect(js_util.strictEqual(js_util.add(one, two), three), isTrue);
|
||||
expect(js_util.strictEqual(js_util.add(one, one), three), isFalse);
|
||||
});
|
||||
|
||||
test('subtraction', () {
|
||||
final one = BigInt('1');
|
||||
final two = BigInt('2');
|
||||
final three = BigInt('3');
|
||||
expect(js_util.strictEqual(js_util.subtract(three, one), two), isTrue);
|
||||
expect(js_util.strictEqual(js_util.subtract(three, two), two), isFalse);
|
||||
});
|
||||
|
||||
test('multiplication', () {
|
||||
final two = BigInt('2');
|
||||
final four = BigInt('4');
|
||||
expect(js_util.strictEqual(js_util.multiply(two, two), four), isTrue);
|
||||
expect(js_util.strictEqual(js_util.multiply(two, four), four), isFalse);
|
||||
});
|
||||
|
||||
test('division', () {
|
||||
final two = BigInt('2');
|
||||
final four = BigInt('4');
|
||||
expect(js_util.strictEqual(js_util.divide(four, two), two), isTrue);
|
||||
expect(js_util.strictEqual(js_util.divide(four, four), two), isFalse);
|
||||
});
|
||||
|
||||
test('exponentiation', () {
|
||||
final two = BigInt('2');
|
||||
final three = BigInt('3');
|
||||
final nine = BigInt('9');
|
||||
expect(
|
||||
js_util.strictEqual(js_util.exponentiate(three, two), nine), isTrue);
|
||||
expect(js_util.strictEqual(js_util.exponentiate(three, three), nine),
|
||||
isFalse);
|
||||
});
|
||||
|
||||
test('exponentiation2', () {
|
||||
final two = BigInt('2');
|
||||
final three = BigInt('3');
|
||||
final five = BigInt('5');
|
||||
expect(
|
||||
js_util.add(
|
||||
'', js_util.exponentiate(js_util.exponentiate(five, three), two)),
|
||||
'15625');
|
||||
expect(
|
||||
js_util.add(
|
||||
'', js_util.exponentiate(five, js_util.exponentiate(three, two))),
|
||||
'1953125');
|
||||
});
|
||||
|
||||
test('modulo', () {
|
||||
final zero = BigInt('0');
|
||||
final three = BigInt('3');
|
||||
final nine = BigInt('9');
|
||||
expect(js_util.strictEqual(js_util.modulo(nine, three), zero), isTrue);
|
||||
expect(js_util.strictEqual(js_util.modulo(nine, three), nine), isFalse);
|
||||
});
|
||||
|
||||
test('equality', () {
|
||||
final one = BigInt('1');
|
||||
expect(js_util.equal(one, 1), isTrue);
|
||||
expect(js_util.strictEqual(one, 1), isFalse);
|
||||
expect(js_util.notEqual(one, 1), isFalse);
|
||||
expect(js_util.strictNotEqual(one, 1), isTrue);
|
||||
});
|
||||
|
||||
test('comparisons', () {
|
||||
final zero = BigInt('0');
|
||||
final one = BigInt('1');
|
||||
final otherOne = BigInt('1');
|
||||
expect(js_util.greaterThan(one, zero), isTrue);
|
||||
expect(js_util.greaterThan(one, 0), isTrue);
|
||||
expect(js_util.greaterThan(2, one), isTrue);
|
||||
expect(js_util.greaterThanOrEqual(one, otherOne), isTrue);
|
||||
expect(js_util.greaterThanOrEqual(one, 1), isTrue);
|
||||
expect(js_util.greaterThanOrEqual(one, 2), isFalse);
|
||||
|
||||
expect(js_util.lessThan(one, zero), isFalse);
|
||||
expect(js_util.lessThan(zero, one), isTrue);
|
||||
expect(js_util.lessThan(one, 2), isTrue);
|
||||
expect(js_util.lessThan(one, 0), isFalse);
|
||||
expect(js_util.lessThanOrEqual(one, otherOne), isTrue);
|
||||
expect(js_util.lessThanOrEqual(one, 1), isTrue);
|
||||
expect(js_util.lessThanOrEqual(2, one), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue