mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:01:42 +00:00
Optimize js_util callMethod calls for 0-4 arguments.
Change completes initial set of js_util optimizations. Some usages of `getProperty`, `setProperty`, and `callMethod` with <= 4 arguments will have unnecessary checks removed by the compilers. Change-Id: I94d5402ca9a64ad2818fd6d6a5f7f114f87348c5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/204562 Commit-Queue: Riley Porter <rileyporter@google.com> Reviewed-by: Srujan Gaddam <srujzs@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
parent
c019575d48
commit
63ac437e99
|
@ -12,6 +12,8 @@ import 'package:kernel/kernel.dart';
|
|||
/// emits the code as a JavaScript code fragment.
|
||||
class JsUtilOptimizer extends Transformer {
|
||||
final Procedure _jsTarget;
|
||||
final Procedure _callMethodTarget;
|
||||
final List<Procedure> _callMethodUncheckedTargets;
|
||||
final Procedure _getPropertyTarget;
|
||||
final Procedure _setPropertyTarget;
|
||||
final Procedure _setPropertyUncheckedTarget;
|
||||
|
@ -27,6 +29,7 @@ class JsUtilOptimizer extends Transformer {
|
|||
];
|
||||
final Iterable<Procedure> _allowedInteropJsUtilTargets;
|
||||
final Procedure _allowInteropTarget;
|
||||
final Procedure _listEmptyFactory;
|
||||
|
||||
final CoreTypes _coreTypes;
|
||||
final StatefulStaticTypeContext _staticTypeContext;
|
||||
|
@ -34,6 +37,12 @@ class JsUtilOptimizer extends Transformer {
|
|||
JsUtilOptimizer(this._coreTypes, ClassHierarchy hierarchy)
|
||||
: _jsTarget =
|
||||
_coreTypes.index.getTopLevelMember('dart:_foreign_helper', 'JS'),
|
||||
_callMethodTarget =
|
||||
_coreTypes.index.getTopLevelMember('dart:js_util', 'callMethod'),
|
||||
_callMethodUncheckedTargets = List<Procedure>.generate(
|
||||
5,
|
||||
(i) => _coreTypes.index
|
||||
.getTopLevelMember('dart:js_util', '_callMethodUnchecked$i')),
|
||||
_getPropertyTarget =
|
||||
_coreTypes.index.getTopLevelMember('dart:js_util', 'getProperty'),
|
||||
_setPropertyTarget =
|
||||
|
@ -45,6 +54,8 @@ class JsUtilOptimizer extends Transformer {
|
|||
_allowedInteropJsUtilTargets = _allowedInteropJsUtilMembers.map(
|
||||
(member) =>
|
||||
_coreTypes.index.getTopLevelMember('dart:js_util', member)),
|
||||
_listEmptyFactory =
|
||||
_coreTypes.index.getMember('dart:core', 'List', 'empty'),
|
||||
_staticTypeContext = StatefulStaticTypeContext.stacked(
|
||||
TypeEnvironment(_coreTypes, hierarchy)) {}
|
||||
|
||||
|
@ -69,12 +80,16 @@ class JsUtilOptimizer extends Transformer {
|
|||
/// Lowers `getProperty` for any argument type straight to JS fragment call.
|
||||
/// Lowers `setProperty` to `_setPropertyUnchecked` for values that are
|
||||
/// not Function type and guaranteed to be interop allowed.
|
||||
/// Lowers `callMethod` to `_callMethodUncheckedN` when the number of given
|
||||
/// arguments is 0-4 and all arguments are guaranteed to be interop allowed.
|
||||
@override
|
||||
visitStaticInvocation(StaticInvocation node) {
|
||||
if (node.target == _getPropertyTarget) {
|
||||
node = _lowerGetProperty(node);
|
||||
} else if (node.target == _setPropertyTarget) {
|
||||
node = _lowerSetProperty(node);
|
||||
} else if (node.target == _callMethodTarget) {
|
||||
node = _lowerCallMethod(node);
|
||||
}
|
||||
node.transformChildren(this);
|
||||
return node;
|
||||
|
@ -104,6 +119,7 @@ class JsUtilOptimizer extends Transformer {
|
|||
|
||||
/// Lowers the given js_util `setProperty` call to `_setPropertyUnchecked`
|
||||
/// when the additional validation checks in `setProperty` can be elided.
|
||||
///
|
||||
/// Removing the checks allows further inlining by the compilers.
|
||||
StaticInvocation _lowerSetProperty(StaticInvocation node) {
|
||||
Arguments arguments = node.arguments;
|
||||
|
@ -119,6 +135,85 @@ class JsUtilOptimizer extends Transformer {
|
|||
..fileOffset = node.fileOffset;
|
||||
}
|
||||
|
||||
/// Lowers the given js_util `callMethod` call to `_callMethodUncheckedN`
|
||||
/// when the additional validation checks on the arguments can be elided.
|
||||
///
|
||||
/// Calls will be lowered when using a List literal or constant list with 0-4
|
||||
/// elements for the `callMethod` arguments, or the `List.empty()` factory.
|
||||
/// Removing the checks allows further inlining by the compilers.
|
||||
StaticInvocation _lowerCallMethod(StaticInvocation node) {
|
||||
Arguments arguments = node.arguments;
|
||||
assert(arguments.types.isEmpty);
|
||||
assert(arguments.positional.length == 3);
|
||||
assert(arguments.named.isEmpty);
|
||||
|
||||
// Lower List.empty factory call.
|
||||
var argumentsList = arguments.positional.last;
|
||||
if (argumentsList is StaticInvocation &&
|
||||
argumentsList.target == _listEmptyFactory) {
|
||||
return _createNewCallMethodNode([], arguments, node.fileOffset);
|
||||
}
|
||||
|
||||
// Lower other kinds of Lists.
|
||||
var callMethodArguments;
|
||||
var entryType;
|
||||
if (argumentsList is ListLiteral) {
|
||||
if (argumentsList.expressions.length >=
|
||||
_callMethodUncheckedTargets.length) {
|
||||
return node;
|
||||
}
|
||||
callMethodArguments = argumentsList.expressions;
|
||||
entryType = argumentsList.typeArgument;
|
||||
} else if (argumentsList is ConstantExpression &&
|
||||
argumentsList.constant is ListConstant) {
|
||||
var argumentsListConstant = argumentsList.constant as ListConstant;
|
||||
if (argumentsListConstant.entries.length >=
|
||||
_callMethodUncheckedTargets.length) {
|
||||
return node;
|
||||
}
|
||||
callMethodArguments = argumentsListConstant.entries
|
||||
.map((constant) => ConstantExpression(
|
||||
constant, constant.getType(_staticTypeContext)))
|
||||
.toList();
|
||||
entryType = argumentsListConstant.typeArgument;
|
||||
} else {
|
||||
// Skip lowering any other type of List.
|
||||
return node;
|
||||
}
|
||||
|
||||
// Check the overall List entry type, then verify each argument if needed.
|
||||
if (!_allowedInteropType(entryType)) {
|
||||
for (var argument in callMethodArguments) {
|
||||
if (!_allowedInterop(argument)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _createNewCallMethodNode(
|
||||
callMethodArguments, arguments, node.fileOffset);
|
||||
}
|
||||
|
||||
/// Creates a new StaticInvocation node for `_callMethodUncheckedN` with the
|
||||
/// given 0-4 arguments.
|
||||
StaticInvocation _createNewCallMethodNode(
|
||||
List<Expression> callMethodArguments,
|
||||
Arguments arguments,
|
||||
int nodeFileOffset) {
|
||||
assert(callMethodArguments.length <= 4);
|
||||
return StaticInvocation(
|
||||
_callMethodUncheckedTargets[callMethodArguments.length],
|
||||
Arguments(
|
||||
[
|
||||
arguments.positional[0],
|
||||
arguments.positional[1],
|
||||
...callMethodArguments
|
||||
],
|
||||
types: [],
|
||||
)..fileOffset = arguments.fileOffset)
|
||||
..fileOffset = nodeFileOffset;
|
||||
}
|
||||
|
||||
/// Returns whether the given Expression is guaranteed to be allowed to
|
||||
/// interop with JS.
|
||||
///
|
||||
|
@ -138,7 +233,12 @@ class JsUtilOptimizer extends Transformer {
|
|||
if (_allowedInteropJsUtilTargets.contains(node.target)) return true;
|
||||
}
|
||||
|
||||
var type = node.getStaticType(_staticTypeContext);
|
||||
return _allowedInteropType(node.getStaticType(_staticTypeContext));
|
||||
}
|
||||
|
||||
/// Returns whether the given DartType is guaranteed to be not a function
|
||||
/// and therefore allowed to interop with JS.
|
||||
bool _allowedInteropType(DartType type) {
|
||||
if (type is InterfaceType) {
|
||||
return type.classNode != _coreTypes.functionClass &&
|
||||
type.classNode != _coreTypes.objectClass;
|
||||
|
|
|
@ -68,13 +68,12 @@ dynamic newObject() => JS('=Object', '{}');
|
|||
|
||||
bool hasProperty(Object o, Object name) => JS('bool', '# in #', name, o);
|
||||
|
||||
// All usage optimized away in a CFE transformation. Changes here will not
|
||||
// affect the generated JS.
|
||||
// A CFE transformation will optimize all calls to `getProperty`.
|
||||
dynamic getProperty(Object o, Object name) =>
|
||||
JS('Object|Null', '#[#]', o, name);
|
||||
|
||||
// Some usage optimized away in a CFE transformation. If given value is a
|
||||
// function, changes here will not affect the generated JS.
|
||||
// A CFE transformation may optimize calls to `setProperty`, when [value] is
|
||||
// statically known to be a non-function.
|
||||
dynamic setProperty(Object o, Object name, Object? value) {
|
||||
assertInterop(value);
|
||||
return JS('', '#[#]=#', o, name, value);
|
||||
|
@ -86,11 +85,48 @@ dynamic _setPropertyUnchecked(Object o, Object name, Object? value) {
|
|||
return JS('', '#[#]=#', o, name, value);
|
||||
}
|
||||
|
||||
// A CFE transformation may optimize calls to `callMethod` when [args] is a
|
||||
// a list literal or const list containing at most 4 values, all of which are
|
||||
// statically known to be non-functions.
|
||||
dynamic callMethod(Object o, String method, List<Object?> args) {
|
||||
assertInteropArgs(args);
|
||||
return JS('Object|Null', '#[#].apply(#, #)', o, method, o, args);
|
||||
}
|
||||
|
||||
/// Unchecked version for 0 arguments, only used in a CFE transformation.
|
||||
@pragma('dart2js:tryInline')
|
||||
dynamic _callMethodUnchecked0(Object o, String method) {
|
||||
return JS('Object|Null', '#[#]()', o, method);
|
||||
}
|
||||
|
||||
/// Unchecked version for 1 argument, only used in a CFE transformation.
|
||||
@pragma('dart2js:tryInline')
|
||||
dynamic _callMethodUnchecked1(Object o, String method, Object? arg1) {
|
||||
return JS('Object|Null', '#[#](#)', o, method, arg1);
|
||||
}
|
||||
|
||||
/// Unchecked version for 2 arguments, only used in a CFE transformation.
|
||||
@pragma('dart2js:tryInline')
|
||||
dynamic _callMethodUnchecked2(
|
||||
Object o, String method, Object? arg1, Object? arg2) {
|
||||
return JS('Object|Null', '#[#](#, #)', o, method, arg1, arg2);
|
||||
}
|
||||
|
||||
/// Unchecked version for 3 arguments, only used in a CFE transformation.
|
||||
@pragma('dart2js:tryInline')
|
||||
dynamic _callMethodUnchecked3(
|
||||
Object o, String method, Object? arg1, Object? arg2, Object? arg3) {
|
||||
return JS('Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
/// Unchecked version for 4 arguments, only used in a CFE transformation.
|
||||
@pragma('dart2js:tryInline')
|
||||
dynamic _callMethodUnchecked4(Object o, String method, Object? arg1,
|
||||
Object? arg2, Object? arg3, Object? arg4) {
|
||||
return JS(
|
||||
'Object|Null', '#[#](#, #, #, #)', o, method, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
/// Check whether [o] is an instance of [type].
|
||||
///
|
||||
/// The value in [type] is expected to be a JS-interop object that
|
||||
|
|
37
tests/lib/js/js_util/properties_implicit_checks_test.dart
Normal file
37
tests/lib/js/js_util/properties_implicit_checks_test.dart
Normal file
|
@ -0,0 +1,37 @@
|
|||
// 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 the functionality of object properties with the js_util library that
|
||||
// involve implicit type checks.
|
||||
|
||||
@JS()
|
||||
library js_util_properties_implicit_checks_test;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart' as js_util;
|
||||
import 'package:expect/minitest.dart';
|
||||
|
||||
@JS()
|
||||
external void eval(String code);
|
||||
|
||||
@JS()
|
||||
class CallMethodTest {
|
||||
external CallMethodTest();
|
||||
|
||||
external one(a);
|
||||
}
|
||||
|
||||
main() {
|
||||
eval(r"""
|
||||
function CallMethodTest() {}
|
||||
|
||||
CallMethodTest.prototype.one = function(a) {
|
||||
return 'one';
|
||||
}
|
||||
""");
|
||||
|
||||
var o = CallMethodTest();
|
||||
expect(() => js_util.callMethod(o, 'one', <String>[5 as dynamic]), throws);
|
||||
expect(() => js_util.callMethod(o, 'one', <int>['foo' as dynamic]), throws);
|
||||
}
|
|
@ -53,6 +53,9 @@ class ExampleTypedLiteral {
|
|||
class DartClass {
|
||||
int x = 3;
|
||||
int getX() => x;
|
||||
|
||||
static staticFunction() => 'static';
|
||||
static const staticConstList = [1];
|
||||
}
|
||||
|
||||
class GenericDartClass<T> {
|
||||
|
@ -70,6 +73,18 @@ String _getBarWithSideEffect() {
|
|||
return 'bar';
|
||||
}
|
||||
|
||||
@JS()
|
||||
class CallMethodTest {
|
||||
external CallMethodTest();
|
||||
|
||||
external zero();
|
||||
external one(a);
|
||||
external two(a, b);
|
||||
external three(a, b, c);
|
||||
external four(a, b, c, d);
|
||||
external five(a, b, c, d, e);
|
||||
}
|
||||
|
||||
main() {
|
||||
eval(r"""
|
||||
function Foo(a) {
|
||||
|
@ -121,6 +136,27 @@ main() {
|
|||
Foo.prototype.callFn = function(fn) {
|
||||
return fn();
|
||||
}
|
||||
|
||||
function CallMethodTest() {}
|
||||
|
||||
CallMethodTest.prototype.zero = function() {
|
||||
return 'zero';
|
||||
}
|
||||
CallMethodTest.prototype.one = function(a) {
|
||||
return 'one';
|
||||
}
|
||||
CallMethodTest.prototype.two = function(a, b) {
|
||||
return 'two';
|
||||
}
|
||||
CallMethodTest.prototype.three = function(a, b, c) {
|
||||
return 'three';
|
||||
}
|
||||
CallMethodTest.prototype.four = function(a, b, c, d) {
|
||||
return 'four';
|
||||
}
|
||||
CallMethodTest.prototype.five = function(a, b, c, d, e) {
|
||||
return 'five';
|
||||
}
|
||||
""");
|
||||
|
||||
group('newObject', () {
|
||||
|
@ -324,6 +360,8 @@ main() {
|
|||
return 'Inline';
|
||||
}));
|
||||
expect(js_util.callMethod(f, 'bar', []), equals('Inline'));
|
||||
js_util.setProperty(f, 'bar', allowInterop(DartClass.staticFunction));
|
||||
expect(js_util.callMethod(f, 'bar', []), equals('static'));
|
||||
|
||||
// Set property to a JS function.
|
||||
js_util.setProperty(f, 'bar', allowInterop(jsFunction));
|
||||
|
@ -403,16 +441,20 @@ main() {
|
|||
expect(js_util.callMethod(f, 'sumFn', [2, 3]), equals(5));
|
||||
expect(js_util.callMethod(f, 'getA', [f]), equals(42));
|
||||
expect(js_util.callMethod(f, 'callFn', [allowInterop(jsFunction)]),
|
||||
equals("JS Function"));
|
||||
equals('JS Function'));
|
||||
expect(js_util.callMethod(f, 'callFn', [allowInterop(dartFunction)]),
|
||||
equals("Dart Function"));
|
||||
equals('Dart Function'));
|
||||
expect(
|
||||
js_util.callMethod(f, 'callFn', [
|
||||
allowInterop(() {
|
||||
return "inline";
|
||||
return 'inline';
|
||||
})
|
||||
]),
|
||||
equals("inline"));
|
||||
equals('inline'));
|
||||
expect(
|
||||
js_util.callMethod(
|
||||
f, 'callFn', [allowInterop(DartClass.staticFunction)]),
|
||||
equals('static'));
|
||||
|
||||
// Using a variable for the method name.
|
||||
String methodName = 'bar';
|
||||
|
@ -420,6 +462,70 @@ main() {
|
|||
String bar = _getBarWithSideEffect();
|
||||
expect(js_util.callMethod(f, bar, []), equals(42));
|
||||
});
|
||||
|
||||
test('callMethod with List edge cases', () {
|
||||
var o = CallMethodTest();
|
||||
|
||||
expect(js_util.callMethod(o, 'zero', List.empty()), equals('zero'));
|
||||
expect(js_util.callMethod(o, 'zero', List<int>.empty()), equals('zero'));
|
||||
|
||||
expect(
|
||||
js_util.callMethod(o, 'two', List<int>.filled(2, 0)), equals('two'));
|
||||
expect(js_util.callMethod(o, 'three', List<int>.generate(3, (i) => i)),
|
||||
equals('three'));
|
||||
|
||||
Iterable<String> iterableStrings = <String>['foo', 'bar'];
|
||||
expect(js_util.callMethod(o, 'two', List.of(iterableStrings)),
|
||||
equals('two'));
|
||||
|
||||
const l1 = [1, 2];
|
||||
const l2 = [3, 4];
|
||||
expect(js_util.callMethod(o, 'four', List.from(l1)..addAll(l2)),
|
||||
equals('four'));
|
||||
expect(js_util.callMethod(o, 'four', l1 + l2), equals('four'));
|
||||
expect(js_util.callMethod(o, 'four', List.unmodifiable([1, 2, 3, 4])),
|
||||
equals('four'));
|
||||
|
||||
var setElements = {1, 2};
|
||||
expect(js_util.callMethod(o, 'two', setElements.toList()), equals('two'));
|
||||
|
||||
var spreadList = [1, 2, 3];
|
||||
expect(js_util.callMethod(o, 'four', [1, ...spreadList]), equals('four'));
|
||||
});
|
||||
|
||||
test('edge cases for lowering to _callMethodUncheckedN', () {
|
||||
var o = CallMethodTest();
|
||||
|
||||
expect(js_util.callMethod(o, 'zero', []), equals('zero'));
|
||||
expect(js_util.callMethod(o, 'one', [1]), equals('one'));
|
||||
expect(js_util.callMethod(o, 'four', [1, 2, 3, 4]), equals('four'));
|
||||
expect(js_util.callMethod(o, 'five', [1, 2, 3, 4, 5]), equals('five'));
|
||||
|
||||
// List with a type declaration, short circuits element checking
|
||||
expect(js_util.callMethod(o, 'two', <int>[1, 2]), equals('two'));
|
||||
|
||||
// List as a variable instead of a List Literal or constant
|
||||
var list = [1, 2];
|
||||
expect(js_util.callMethod(o, 'two', list), equals('two'));
|
||||
|
||||
// Mixed types of elements to check in the given list.
|
||||
var x = 4;
|
||||
var str = 'cat';
|
||||
var b = false;
|
||||
var evens = [2, 4, 6];
|
||||
expect(js_util.callMethod(o, 'four', [x, str, b, evens]), equals('four'));
|
||||
var obj = Object();
|
||||
expect(js_util.callMethod(o, 'one', [obj]), equals('one'));
|
||||
var nullElement = null;
|
||||
expect(js_util.callMethod(o, 'one', [nullElement]), equals('one'));
|
||||
|
||||
// const lists.
|
||||
expect(js_util.callMethod(o, 'one', const [3]), equals('one'));
|
||||
const constList = [10, 20, 30];
|
||||
expect(js_util.callMethod(o, 'three', constList), equals('three'));
|
||||
expect(js_util.callMethod(o, 'one', DartClass.staticConstList),
|
||||
equals('one'));
|
||||
});
|
||||
});
|
||||
|
||||
group('instanceof', () {
|
||||
|
|
|
@ -26,6 +26,9 @@ wasm/*: SkipByDesign # dart:wasm not currently supported on web.
|
|||
[ $compiler != dart2js ]
|
||||
async/dart2js_uncaught_error_test: Skip # JS-integration only test
|
||||
|
||||
[ $builder_tag == dart2js_production && $compiler == dart2js ]
|
||||
js/js_util/properties_implicit_checks_test: SkipByDesign # No implicit checks in production mode
|
||||
|
||||
[ $compiler == dart2js && $runtime == chrome ]
|
||||
async/slow_consumer2_test: SkipSlow # Times out. Issue 22050
|
||||
convert/streamed_conversion_json_utf8_decode_test: SkipSlow # Times out. Issue 22050
|
||||
|
|
39
tests/lib_2/js/js_util/properties_implicit_checks_test.dart
Normal file
39
tests/lib_2/js/js_util/properties_implicit_checks_test.dart
Normal file
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
// Tests the functionality of object properties with the js_util library that
|
||||
// involve implicit type checks.
|
||||
|
||||
@JS()
|
||||
library js_util_properties_implicit_checks_test;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart' as js_util;
|
||||
import 'package:expect/minitest.dart';
|
||||
|
||||
@JS()
|
||||
external void eval(String code);
|
||||
|
||||
@JS()
|
||||
class CallMethodTest {
|
||||
external CallMethodTest();
|
||||
|
||||
external one(a);
|
||||
}
|
||||
|
||||
main() {
|
||||
eval(r"""
|
||||
function CallMethodTest() {}
|
||||
|
||||
CallMethodTest.prototype.one = function(a) {
|
||||
return 'one';
|
||||
}
|
||||
""");
|
||||
|
||||
var o = CallMethodTest();
|
||||
expect(() => js_util.callMethod(o, 'one', <String>[5 as dynamic]), throws);
|
||||
expect(() => js_util.callMethod(o, 'one', <int>['foo' as dynamic]), throws);
|
||||
}
|
|
@ -55,6 +55,9 @@ class ExampleTypedLiteral {
|
|||
class DartClass {
|
||||
int x = 3;
|
||||
int getX() => x;
|
||||
|
||||
static staticFunction() => 'static';
|
||||
static const staticConstList = [1];
|
||||
}
|
||||
|
||||
class GenericDartClass<T> {
|
||||
|
@ -72,6 +75,18 @@ String _getBarWithSideEffect() {
|
|||
return 'bar';
|
||||
}
|
||||
|
||||
@JS()
|
||||
class CallMethodTest {
|
||||
external CallMethodTest();
|
||||
|
||||
external zero();
|
||||
external one(a);
|
||||
external two(a, b);
|
||||
external three(a, b, c);
|
||||
external four(a, b, c, d);
|
||||
external five(a, b, c, d, e);
|
||||
}
|
||||
|
||||
main() {
|
||||
eval(r"""
|
||||
function Foo(a) {
|
||||
|
@ -123,6 +138,27 @@ main() {
|
|||
Foo.prototype.callFn = function(fn) {
|
||||
return fn();
|
||||
}
|
||||
|
||||
function CallMethodTest() {}
|
||||
|
||||
CallMethodTest.prototype.zero = function() {
|
||||
return 'zero';
|
||||
}
|
||||
CallMethodTest.prototype.one = function(a) {
|
||||
return 'one';
|
||||
}
|
||||
CallMethodTest.prototype.two = function(a, b) {
|
||||
return 'two';
|
||||
}
|
||||
CallMethodTest.prototype.three = function(a, b, c) {
|
||||
return 'three';
|
||||
}
|
||||
CallMethodTest.prototype.four = function(a, b, c, d) {
|
||||
return 'four';
|
||||
}
|
||||
CallMethodTest.prototype.five = function(a, b, c, d, e) {
|
||||
return 'five';
|
||||
}
|
||||
""");
|
||||
|
||||
group('newObject', () {
|
||||
|
@ -326,6 +362,8 @@ main() {
|
|||
return 'Inline';
|
||||
}));
|
||||
expect(js_util.callMethod(f, 'bar', []), equals('Inline'));
|
||||
js_util.setProperty(f, 'bar', allowInterop(DartClass.staticFunction));
|
||||
expect(js_util.callMethod(f, 'bar', []), equals('static'));
|
||||
|
||||
// Set property to a JS function.
|
||||
js_util.setProperty(f, 'bar', allowInterop(jsFunction));
|
||||
|
@ -405,16 +443,20 @@ main() {
|
|||
expect(js_util.callMethod(f, 'sumFn', [2, 3]), equals(5));
|
||||
expect(js_util.callMethod(f, 'getA', [f]), equals(42));
|
||||
expect(js_util.callMethod(f, 'callFn', [allowInterop(jsFunction)]),
|
||||
equals("JS Function"));
|
||||
equals('JS Function'));
|
||||
expect(js_util.callMethod(f, 'callFn', [allowInterop(dartFunction)]),
|
||||
equals("Dart Function"));
|
||||
equals('Dart Function'));
|
||||
expect(
|
||||
js_util.callMethod(f, 'callFn', [
|
||||
allowInterop(() {
|
||||
return "inline";
|
||||
return 'inline';
|
||||
})
|
||||
]),
|
||||
equals("inline"));
|
||||
equals('inline'));
|
||||
expect(
|
||||
js_util.callMethod(
|
||||
f, 'callFn', [allowInterop(DartClass.staticFunction)]),
|
||||
equals('static'));
|
||||
|
||||
// Using a variable for the method name.
|
||||
String methodName = 'bar';
|
||||
|
@ -422,6 +464,72 @@ main() {
|
|||
String bar = _getBarWithSideEffect();
|
||||
expect(js_util.callMethod(f, bar, []), equals(42));
|
||||
});
|
||||
|
||||
test('callMethod with List edge cases', () {
|
||||
var o = CallMethodTest();
|
||||
|
||||
expect(js_util.callMethod(o, 'zero', List()), equals('zero'));
|
||||
expect(js_util.callMethod(o, 'zero', List<int>()), equals('zero'));
|
||||
expect(js_util.callMethod(o, 'zero', List.empty()), equals('zero'));
|
||||
expect(js_util.callMethod(o, 'zero', List<int>.empty()), equals('zero'));
|
||||
|
||||
expect(
|
||||
js_util.callMethod(o, 'two', List<int>.filled(2, 0)), equals('two'));
|
||||
expect(js_util.callMethod(o, 'three', List<int>.generate(3, (i) => i)),
|
||||
equals('three'));
|
||||
|
||||
Iterable<String> iterableStrings = <String>['foo', 'bar'];
|
||||
expect(js_util.callMethod(o, 'two', List.of(iterableStrings)),
|
||||
equals('two'));
|
||||
|
||||
const l1 = [1, 2];
|
||||
const l2 = [3, 4];
|
||||
expect(js_util.callMethod(o, 'four', List.from(l1)..addAll(l2)),
|
||||
equals('four'));
|
||||
expect(js_util.callMethod(o, 'four', l1 + l2), equals('four'));
|
||||
expect(js_util.callMethod(o, 'four', List.unmodifiable([1, 2, 3, 4])),
|
||||
equals('four'));
|
||||
|
||||
var setElements = {1, 2};
|
||||
expect(js_util.callMethod(o, 'two', setElements.toList()), equals('two'));
|
||||
|
||||
var spreadList = [1, 2, 3];
|
||||
expect(js_util.callMethod(o, 'four', [1, ...spreadList]), equals('four'));
|
||||
});
|
||||
|
||||
test('edge cases for lowering to _callMethodUncheckedN', () {
|
||||
var o = CallMethodTest();
|
||||
|
||||
expect(js_util.callMethod(o, 'zero', []), equals('zero'));
|
||||
expect(js_util.callMethod(o, 'one', [1]), equals('one'));
|
||||
expect(js_util.callMethod(o, 'four', [1, 2, 3, 4]), equals('four'));
|
||||
expect(js_util.callMethod(o, 'five', [1, 2, 3, 4, 5]), equals('five'));
|
||||
|
||||
// List with a type declaration, short circuits element checking
|
||||
expect(js_util.callMethod(o, 'two', <int>[1, 2]), equals('two'));
|
||||
|
||||
// List as a variable instead of a List Literal or constant
|
||||
var list = [1, 2];
|
||||
expect(js_util.callMethod(o, 'two', list), equals('two'));
|
||||
|
||||
// Mixed types of elements to check in the given list.
|
||||
var x = 4;
|
||||
var str = 'cat';
|
||||
var b = false;
|
||||
var evens = [2, 4, 6];
|
||||
expect(js_util.callMethod(o, 'four', [x, str, b, evens]), equals('four'));
|
||||
var obj = Object();
|
||||
expect(js_util.callMethod(o, 'one', [obj]), equals('one'));
|
||||
var nullElement = null;
|
||||
expect(js_util.callMethod(o, 'one', [nullElement]), equals('one'));
|
||||
|
||||
// const lists.
|
||||
expect(js_util.callMethod(o, 'one', const [3]), equals('one'));
|
||||
const constList = [10, 20, 30];
|
||||
expect(js_util.callMethod(o, 'three', constList), equals('three'));
|
||||
expect(js_util.callMethod(o, 'one', DartClass.staticConstList),
|
||||
equals('one'));
|
||||
});
|
||||
});
|
||||
|
||||
group('instanceof', () {
|
||||
|
|
|
@ -24,6 +24,9 @@ wasm/*: SkipByDesign # dart:wasm not currently supported on web.
|
|||
[ $compiler != dart2js ]
|
||||
async/dart2js_uncaught_error_test: Skip # JS-integration only test
|
||||
|
||||
[ $builder_tag == dart2js_production && $compiler == dart2js ]
|
||||
js/js_util/properties_implicit_checks_test: SkipByDesign # No implicit checks in production mode
|
||||
|
||||
[ $compiler == dart2js && $runtime == chrome ]
|
||||
async/slow_consumer2_test: SkipSlow # Times out. Issue 22050
|
||||
convert/streamed_conversion_json_utf8_decode_test: SkipSlow # Times out. Issue 22050
|
||||
|
|
Loading…
Reference in a new issue