diff --git a/sdk/lib/js_util/js_util.dart b/sdk/lib/js_util/js_util.dart index fcd668cbe0a..5dca72d6c7b 100644 --- a/sdk/lib/js_util/js_util.dart +++ b/sdk/lib/js_util/js_util.dart @@ -68,7 +68,8 @@ T newObject() => JS('=Object', '{}'); bool hasProperty(Object o, Object name) => JS('bool', '# in #', name, o); -T getProperty(Object o, Object name) => JS('Object|Null', '#[#]', o, name); +T getProperty(Object o, Object name) => + JS('Object|Null', '#[#]', o, name); // A CFE transformation may optimize calls to `setProperty`, when [value] is // statically known to be a non-function. @@ -88,40 +89,41 @@ T _setPropertyUnchecked(Object o, Object name, T? value) { // statically known to be non-functions. T callMethod(Object o, String method, List args) { assertInteropArgs(args); - return JS('Object|Null', '#[#].apply(#, #)', o, method, o, args); + return JS('Object|Null', '#[#].apply(#, #)', o, method, o, args); } /// Unchecked version for 0 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked0(Object o, String method) { - return JS('Object|Null', '#[#]()', o, method); + return JS('Object|Null', '#[#]()', o, method); } /// Unchecked version for 1 argument, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked1(Object o, String method, Object? arg1) { - return JS('Object|Null', '#[#](#)', o, method, arg1); + return JS('Object|Null', '#[#](#)', o, method, arg1); } /// Unchecked version for 2 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked2( Object o, String method, Object? arg1, Object? arg2) { - return JS('Object|Null', '#[#](#, #)', o, method, arg1, arg2); + return JS('Object|Null', '#[#](#, #)', o, method, arg1, arg2); } /// Unchecked version for 3 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked3( Object o, String method, Object? arg1, Object? arg2, Object? arg3) { - return JS('Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3); + return JS( + 'Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3); } /// Unchecked version for 4 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked4(Object o, String method, Object? arg1, Object? arg2, Object? arg3, Object? arg4) { - return JS( + return JS( 'Object|Null', '#[#](#, #, #, #)', o, method, arg1, arg2, arg3, arg4); } @@ -134,7 +136,7 @@ bool instanceof(Object? o, Object type) => T callConstructor(Object constr, List? arguments) { if (arguments == null) { - return JS('Object', 'new #()', constr); + return JS('Object', 'new #()', constr); } else { assertInteropArgs(arguments); } @@ -143,29 +145,30 @@ T callConstructor(Object constr, List? arguments) { int argumentCount = JS('int', '#.length', arguments); switch (argumentCount) { case 0: - return JS('Object', 'new #()', constr); + return JS('Object', 'new #()', constr); case 1: var arg0 = JS('', '#[0]', arguments); - return JS('Object', 'new #(#)', constr, arg0); + return JS('Object', 'new #(#)', constr, arg0); case 2: var arg0 = JS('', '#[0]', arguments); var arg1 = JS('', '#[1]', arguments); - return JS('Object', 'new #(#, #)', constr, arg0, arg1); + return JS('Object', 'new #(#, #)', constr, arg0, arg1); case 3: var arg0 = JS('', '#[0]', arguments); var arg1 = JS('', '#[1]', arguments); var arg2 = JS('', '#[2]', arguments); - return JS('Object', 'new #(#, #, #)', constr, arg0, arg1, arg2); + return JS( + 'Object', 'new #(#, #, #)', constr, arg0, arg1, arg2); case 4: var arg0 = JS('', '#[0]', arguments); var arg1 = JS('', '#[1]', arguments); var arg2 = JS('', '#[2]', arguments); var arg3 = JS('', '#[3]', arguments); - return JS( + return JS( 'Object', 'new #(#, #, #, #)', constr, arg0, arg1, arg2, arg3); } } @@ -183,7 +186,7 @@ T callConstructor(Object constr, List? arguments) { JS('String', 'String(#)', factoryFunction); // This could return an UnknownJavaScriptObject, or a native // object for which there is an interceptor - return JS('Object', 'new #()', factoryFunction); + return JS('Object', 'new #()', factoryFunction); // TODO(sra): Investigate: // @@ -196,33 +199,34 @@ T callConstructor(Object constr, List? arguments) { /// Unchecked version for 0 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callConstructorUnchecked0(Object constr) { - return JS('Object', 'new #()', constr); + return JS('Object', 'new #()', constr); } /// Unchecked version for 1 argument, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callConstructorUnchecked1(Object constr, Object? arg1) { - return JS('Object', 'new #(#)', constr, arg1); + return JS('Object', 'new #(#)', constr, arg1); } /// Unchecked version for 2 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callConstructorUnchecked2(Object constr, Object? arg1, Object? arg2) { - return JS('Object', 'new #(#, #)', constr, arg1, arg2); + return JS('Object', 'new #(#, #)', constr, arg1, arg2); } /// Unchecked version for 3 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callConstructorUnchecked3( Object constr, Object? arg1, Object? arg2, Object? arg3) { - return JS('Object', 'new #(#, #, #)', constr, arg1, arg2, arg3); + return JS('Object', 'new #(#, #, #)', constr, arg1, arg2, arg3); } /// Unchecked version for 4 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callConstructorUnchecked4( Object constr, Object? arg1, Object? arg2, Object? arg3, Object? arg4) { - return JS('Object', 'new #(#, #, #, #)', constr, arg1, arg2, arg3, arg4); + return JS( + 'Object', 'new #(#, #, #, #)', constr, arg1, arg2, arg3, arg4); } /// Exception for when the promise is rejected with a `null` or `undefined` diff --git a/tests/lib/js/js_util/implicit_downcast_test.dart b/tests/lib/js/js_util/implicit_downcast_test.dart new file mode 100644 index 00000000000..b0d201b1bd3 --- /dev/null +++ b/tests/lib/js/js_util/implicit_downcast_test.dart @@ -0,0 +1,124 @@ +// Copyright (c) 2022, 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 implicit downcasts in js_util. + +@JS() +library js_util_implicit_downcast_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 Foo { + external Foo(num a); + + external num get a; + external void set a(_); + external num bar(); +} + +bool isComplianceMode() { + var stuff = [1, 'string']; + var a = stuff[0]; + // Detect whether we are using --omit-implicit-checks. + try { + String s = a as dynamic; + return false; + } catch (e) { + // Ignore. + } + return true; +} + +main() { + eval(r""" + function Foo(a) { + this.a = a; + } + + Foo.prototype.bar = function() { + return this.a; + } + """); + + if (isComplianceMode()) { + complianceModeTest(); + } else { + omitImplicitChecksTest(); + } +} + +complianceModeTest() { + var f = Foo(42); + expect(js_util.getProperty(f, 'a'), equals(42)); + expect(() => js_util.getProperty(f, 'a'), throws); + + f.a = 5; + expect(js_util.callMethod(f, 'bar', []), equals(5)); + expect(() => js_util.callMethod(f, 'bar', []), throws); + + // Check optimized lowering of callMethod. + expect(() => js_util.callMethod(f, 'bar', [1]), throws); + expect(() => js_util.callMethod(f, 'bar', [1, 2]), throws); + expect(() => js_util.callMethod(f, 'bar', [1, 2, 3]), throws); + expect(() => js_util.callMethod(f, 'bar', [1, 2, 3, 4]), throws); + expect(() => js_util.callMethod(f, 'bar', [1, 2, 3, 4, 5]), throws); + + var f2 = Foo(7); + var fConstructor = js_util.getProperty(f, 'constructor'); + expect(js_util.callConstructor(fConstructor, [7]).a, equals(7)); + expect(() => js_util.callConstructor(fConstructor, [7]), throws); + + // Check optimized lowering of callConstructor. + expect(() => js_util.callConstructor(fConstructor, null), throws); + expect(() => js_util.callConstructor(fConstructor, []), throws); + expect(() => js_util.callConstructor(fConstructor, [1, 2]), throws); + expect(() => js_util.callConstructor(fConstructor, [1, 2, 3]), throws); + expect( + () => js_util.callConstructor(fConstructor, [1, 2, 3, 4]), throws); + expect(() => js_util.callConstructor(fConstructor, [1, 2, 3, 4, 5]), + throws); +} + +omitImplicitChecksTest() { + var f = Foo(42); + expect(js_util.getProperty(f, 'a'), equals(42)); + expect(js_util.getProperty(f, 'a'), equals(42)); + + f.a = 5; + expect(js_util.callMethod(f, 'bar', []), equals(5)); + expect(js_util.callMethod(f, 'bar', []), equals(5)); + + // Check optimized lowering of callMethod. + expect(js_util.callMethod(f, 'bar', [1]), equals(5)); + expect(js_util.callMethod(f, 'bar', [1, 2]), equals(5)); + expect(js_util.callMethod(f, 'bar', [1, 2, 3]), equals(5)); + expect(js_util.callMethod(f, 'bar', [1, 2, 3, 4]), equals(5)); + expect(js_util.callMethod(f, 'bar', [1, 2, 3, 4, 5]), equals(5)); + + var fConstructor = js_util.getProperty(f, 'constructor'); + expect(js_util.callConstructor(fConstructor, [7]).a, equals(7)); + expect( + (js_util.callConstructor(fConstructor, [7]) as Foo).a, equals(7)); + + // Check optimized lowering of callConstructor. + expect((js_util.callConstructor(fConstructor, null) as Foo).a, + equals(null)); + expect( + (js_util.callConstructor(fConstructor, []) as Foo).a, equals(null)); + expect((js_util.callConstructor(fConstructor, [1, 2]) as Foo).a, + equals(1)); + expect((js_util.callConstructor(fConstructor, [1, 2, 3]) as Foo).a, + equals(1)); + expect((js_util.callConstructor(fConstructor, [1, 2, 3, 4]) as Foo).a, + equals(1)); + expect( + (js_util.callConstructor(fConstructor, [1, 2, 3, 4, 5]) as Foo).a, + equals(1)); +} diff --git a/tests/lib/lib.status b/tests/lib/lib.status index 46bdedf6860..770c4d6f56a 100644 --- a/tests/lib/lib.status +++ b/tests/lib/lib.status @@ -45,6 +45,7 @@ js/external_extension_members_test: SkipByDesign # Issue 42085. CSP policy disal js/instanceof_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/is_check_and_as_cast_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/js_util/async_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code +js/js_util/implicit_downcast_test.dart: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/js_util/javascriptobject_extensions_test.dart: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/js_util/jsify_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/js_util/promise_reject_null_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code diff --git a/tests/lib_2/js/js_util/implicit_downcast_test.dart b/tests/lib_2/js/js_util/implicit_downcast_test.dart new file mode 100644 index 00000000000..b0d201b1bd3 --- /dev/null +++ b/tests/lib_2/js/js_util/implicit_downcast_test.dart @@ -0,0 +1,124 @@ +// Copyright (c) 2022, 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 implicit downcasts in js_util. + +@JS() +library js_util_implicit_downcast_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 Foo { + external Foo(num a); + + external num get a; + external void set a(_); + external num bar(); +} + +bool isComplianceMode() { + var stuff = [1, 'string']; + var a = stuff[0]; + // Detect whether we are using --omit-implicit-checks. + try { + String s = a as dynamic; + return false; + } catch (e) { + // Ignore. + } + return true; +} + +main() { + eval(r""" + function Foo(a) { + this.a = a; + } + + Foo.prototype.bar = function() { + return this.a; + } + """); + + if (isComplianceMode()) { + complianceModeTest(); + } else { + omitImplicitChecksTest(); + } +} + +complianceModeTest() { + var f = Foo(42); + expect(js_util.getProperty(f, 'a'), equals(42)); + expect(() => js_util.getProperty(f, 'a'), throws); + + f.a = 5; + expect(js_util.callMethod(f, 'bar', []), equals(5)); + expect(() => js_util.callMethod(f, 'bar', []), throws); + + // Check optimized lowering of callMethod. + expect(() => js_util.callMethod(f, 'bar', [1]), throws); + expect(() => js_util.callMethod(f, 'bar', [1, 2]), throws); + expect(() => js_util.callMethod(f, 'bar', [1, 2, 3]), throws); + expect(() => js_util.callMethod(f, 'bar', [1, 2, 3, 4]), throws); + expect(() => js_util.callMethod(f, 'bar', [1, 2, 3, 4, 5]), throws); + + var f2 = Foo(7); + var fConstructor = js_util.getProperty(f, 'constructor'); + expect(js_util.callConstructor(fConstructor, [7]).a, equals(7)); + expect(() => js_util.callConstructor(fConstructor, [7]), throws); + + // Check optimized lowering of callConstructor. + expect(() => js_util.callConstructor(fConstructor, null), throws); + expect(() => js_util.callConstructor(fConstructor, []), throws); + expect(() => js_util.callConstructor(fConstructor, [1, 2]), throws); + expect(() => js_util.callConstructor(fConstructor, [1, 2, 3]), throws); + expect( + () => js_util.callConstructor(fConstructor, [1, 2, 3, 4]), throws); + expect(() => js_util.callConstructor(fConstructor, [1, 2, 3, 4, 5]), + throws); +} + +omitImplicitChecksTest() { + var f = Foo(42); + expect(js_util.getProperty(f, 'a'), equals(42)); + expect(js_util.getProperty(f, 'a'), equals(42)); + + f.a = 5; + expect(js_util.callMethod(f, 'bar', []), equals(5)); + expect(js_util.callMethod(f, 'bar', []), equals(5)); + + // Check optimized lowering of callMethod. + expect(js_util.callMethod(f, 'bar', [1]), equals(5)); + expect(js_util.callMethod(f, 'bar', [1, 2]), equals(5)); + expect(js_util.callMethod(f, 'bar', [1, 2, 3]), equals(5)); + expect(js_util.callMethod(f, 'bar', [1, 2, 3, 4]), equals(5)); + expect(js_util.callMethod(f, 'bar', [1, 2, 3, 4, 5]), equals(5)); + + var fConstructor = js_util.getProperty(f, 'constructor'); + expect(js_util.callConstructor(fConstructor, [7]).a, equals(7)); + expect( + (js_util.callConstructor(fConstructor, [7]) as Foo).a, equals(7)); + + // Check optimized lowering of callConstructor. + expect((js_util.callConstructor(fConstructor, null) as Foo).a, + equals(null)); + expect( + (js_util.callConstructor(fConstructor, []) as Foo).a, equals(null)); + expect((js_util.callConstructor(fConstructor, [1, 2]) as Foo).a, + equals(1)); + expect((js_util.callConstructor(fConstructor, [1, 2, 3]) as Foo).a, + equals(1)); + expect((js_util.callConstructor(fConstructor, [1, 2, 3, 4]) as Foo).a, + equals(1)); + expect( + (js_util.callConstructor(fConstructor, [1, 2, 3, 4, 5]) as Foo).a, + equals(1)); +} diff --git a/tests/lib_2/lib_2.status b/tests/lib_2/lib_2.status index bef1d690958..d34ce69003d 100644 --- a/tests/lib_2/lib_2.status +++ b/tests/lib_2/lib_2.status @@ -45,6 +45,7 @@ js/external_extension_members_test: SkipByDesign # Issue 42085. CSP policy disal js/instanceof_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/is_check_and_as_cast_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/js_util/async_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code +js/js_util/implicit_downcast_test.dart: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/js_util/javascriptobject_extensions_test.dart: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/js_util/jsify_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code js/js_util/promise_reject_null_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code