Version 2.14.0-263.0.dev
Merge commit '63ac437e9934c6422c96e5195201e8968bc8dfc6' into 'dev'
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index 4b41e12..b29d418 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -12,6 +12,8 @@
/// 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 @@
];
final Iterable<Procedure> _allowedInteropJsUtilTargets;
final Procedure _allowInteropTarget;
+ final Procedure _listEmptyFactory;
final CoreTypes _coreTypes;
final StatefulStaticTypeContext _staticTypeContext;
@@ -34,6 +37,12 @@
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 @@
_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 @@
/// 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 @@
/// 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 @@
..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 @@
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;
diff --git a/sdk/lib/js_util/js_util.dart b/sdk/lib/js_util/js_util.dart
index 867fd7f..ee3dd0f 100644
--- a/sdk/lib/js_util/js_util.dart
+++ b/sdk/lib/js_util/js_util.dart
@@ -68,13 +68,12 @@
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 @@
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
diff --git a/tests/lib/js/js_util/properties_implicit_checks_test.dart b/tests/lib/js/js_util/properties_implicit_checks_test.dart
new file mode 100644
index 0000000..90a8349
--- /dev/null
+++ b/tests/lib/js/js_util/properties_implicit_checks_test.dart
@@ -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);
+}
diff --git a/tests/lib/js/js_util/properties_test.dart b/tests/lib/js/js_util/properties_test.dart
index a9db3d2..326de20 100644
--- a/tests/lib/js/js_util/properties_test.dart
+++ b/tests/lib/js/js_util/properties_test.dart
@@ -53,6 +53,9 @@
class DartClass {
int x = 3;
int getX() => x;
+
+ static staticFunction() => 'static';
+ static const staticConstList = [1];
}
class GenericDartClass<T> {
@@ -70,6 +73,18 @@
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 @@
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 @@
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 @@
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 @@
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', () {
diff --git a/tests/lib/lib_dart2js.status b/tests/lib/lib_dart2js.status
index 7478db8..b1d2d3b 100644
--- a/tests/lib/lib_dart2js.status
+++ b/tests/lib/lib_dart2js.status
@@ -26,6 +26,9 @@
[ $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
diff --git a/tests/lib_2/js/js_util/properties_implicit_checks_test.dart b/tests/lib_2/js/js_util/properties_implicit_checks_test.dart
new file mode 100644
index 0000000..c3e141e
--- /dev/null
+++ b/tests/lib_2/js/js_util/properties_implicit_checks_test.dart
@@ -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);
+}
diff --git a/tests/lib_2/js/js_util/properties_test.dart b/tests/lib_2/js/js_util/properties_test.dart
index 511780f..8479414 100644
--- a/tests/lib_2/js/js_util/properties_test.dart
+++ b/tests/lib_2/js/js_util/properties_test.dart
@@ -55,6 +55,9 @@
class DartClass {
int x = 3;
int getX() => x;
+
+ static staticFunction() => 'static';
+ static const staticConstList = [1];
}
class GenericDartClass<T> {
@@ -72,6 +75,18 @@
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 @@
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 @@
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 @@
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 @@
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', () {
diff --git a/tests/lib_2/lib_2_dart2js.status b/tests/lib_2/lib_2_dart2js.status
index 2db76f5..1f09063 100644
--- a/tests/lib_2/lib_2_dart2js.status
+++ b/tests/lib_2/lib_2_dart2js.status
@@ -24,6 +24,9 @@
[ $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
diff --git a/tools/VERSION b/tools/VERSION
index e796ecf..dcd145e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 262
+PRERELEASE 263
PRERELEASE_PATCH 0
\ No newline at end of file