| // Copyright (c) 2024, 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. |
| |
| // Requirements=checked-implicit-downcasts |
| |
| import 'dart:js_interop'; |
| |
| import 'package:expect/expect.dart'; |
| import 'package:expect/variations.dart'; |
| |
| const isDDC = const bool.fromEnvironment('dart.library._ddc_only'); |
| const isDart2JS = const bool.fromEnvironment('dart.tool.dart2js'); |
| const soundNullSafety = !unsoundNullSafety; |
| |
| @JS('call') |
| external String _call(JSFunction f, JSArray<JSAny?> args); |
| |
| String call(JSFunction f, List<Object?> args) => |
| _call(f, args.map((e) => e?.jsify()).toList().toJS); |
| |
| @JS() |
| external void eval(String code); |
| |
| // Zero. |
| String zeroArgs() => '0'; |
| String zeroArgsThis([JSObject? this_]) => '0'; |
| |
| // One. |
| String oneRequired(String arg1) => arg1; |
| String oneOptional([String arg1 = 'default']) => '$arg1'; |
| String oneOptionalThis(JSObject? this_, [String arg1 = 'default']) => '$arg1'; |
| |
| // Two. |
| String twoRequired(String arg1, String? arg2) => '$arg1$arg2'; |
| String oneRequiredOneOptional(String arg1, [String? arg2 = 'default']) => |
| '$arg1$arg2'; |
| String twoOptional([String arg1 = 'default', String? arg2 = 'default']) => |
| '$arg1$arg2'; |
| String oneRequiredOneOptionalThis( |
| JSObject? this_, |
| String arg1, [ |
| String? arg2 = 'default', |
| ]) => '$arg1$arg2'; |
| |
| // Three. |
| String threeRequired(String arg1, String? arg2, String arg3) => |
| '$arg1$arg2$arg3'; |
| String twoRequiredOneOptional( |
| String arg1, |
| String? arg2, [ |
| String arg3 = 'default', |
| ]) => '$arg1$arg2$arg3'; |
| String oneRequiredTwoOptional( |
| String arg1, [ |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| ]) => '$arg1$arg2$arg3'; |
| String threeOptional([ |
| String arg1 = 'default', |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| ]) => '$arg1$arg2$arg3'; |
| String threeOptionalThis([ |
| JSObject? this_, |
| String arg1 = 'default', |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| ]) => '$arg1$arg2$arg3'; |
| |
| // Four. |
| String fourRequired(String arg1, String? arg2, String arg3, String arg4) => |
| '$arg1$arg2$arg3$arg4'; |
| String threeRequiredOneOptional( |
| String arg1, |
| String? arg2, |
| String arg3, [ |
| String arg4 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4'; |
| String twoRequiredTwoOptional( |
| String arg1, |
| String? arg2, [ |
| String arg3 = 'default', |
| String arg4 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4'; |
| String oneRequiredThreeOptional( |
| String arg1, [ |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| String arg4 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4'; |
| String fourOptional([ |
| String arg1 = 'default', |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| String arg4 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4'; |
| String oneRequiredThreeOptionalThis( |
| JSObject? this_, |
| String arg1, [ |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| String arg4 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4'; |
| |
| // Five. |
| String fiveRequired( |
| String arg1, |
| String? arg2, |
| String arg3, |
| String arg4, |
| String arg5, |
| ) => '$arg1$arg2$arg3$arg4$arg5'; |
| String fourRequiredOneOptional( |
| String arg1, |
| String? arg2, |
| String arg3, |
| String arg4, [ |
| String arg5 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5'; |
| String threeRequiredTwoOptional( |
| String arg1, |
| String? arg2, |
| String arg3, [ |
| String arg4 = 'default', |
| String arg5 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5'; |
| String twoRequiredThreeOptional( |
| String arg1, |
| String? arg2, [ |
| String arg3 = 'default', |
| String arg4 = 'default', |
| String arg5 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5'; |
| String oneRequiredFourOptional( |
| String arg1, [ |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| String arg4 = 'default', |
| String arg5 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5'; |
| String fiveOptional([ |
| String arg1 = 'default', |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| String arg4 = 'default', |
| String arg5 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5'; |
| String threeRequiredTwoOptionalThis( |
| JSObject? this_, |
| String arg1, |
| String? arg2, |
| String arg3, [ |
| String arg4 = 'default', |
| String arg5 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5'; |
| |
| // Six. |
| String sixRequired( |
| String arg1, |
| String? arg2, |
| String arg3, |
| String arg4, |
| String arg5, |
| String arg6, |
| ) => '$arg1$arg2$arg3$arg4$arg5$arg6'; |
| String fiveRequiredOneOptional( |
| String arg1, |
| String? arg2, |
| String arg3, |
| String arg4, |
| String arg5, [ |
| String arg6 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5$arg6'; |
| String fourRequiredTwoOptional( |
| String arg1, |
| String? arg2, |
| String arg3, |
| String arg4, [ |
| String arg5 = 'default', |
| String arg6 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5$arg6'; |
| String threeRequiredThreeOptional( |
| String arg1, |
| String? arg2, |
| String arg3, [ |
| String arg4 = 'default', |
| String arg5 = 'default', |
| String arg6 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5$arg6'; |
| String twoRequiredFourOptional( |
| String arg1, |
| String? arg2, [ |
| String arg3 = 'default', |
| String arg4 = 'default', |
| String arg5 = 'default', |
| String arg6 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5$arg6'; |
| String oneRequiredFiveOptional( |
| String arg1, [ |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| String arg4 = 'default', |
| String arg5 = 'default', |
| String arg6 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5$arg6'; |
| String sixOptional([ |
| String arg1 = 'default', |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| String arg4 = 'default', |
| String arg5 = 'default', |
| String arg6 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5$arg6'; |
| String sixOptionalThis([ |
| JSObject? this_, |
| String arg1 = 'default', |
| String? arg2 = 'default', |
| String arg3 = 'default', |
| String arg4 = 'default', |
| String arg5 = 'default', |
| String arg6 = 'default', |
| ]) => '$arg1$arg2$arg3$arg4$arg5$arg6'; |
| |
| void testZero() { |
| // Arity tests. |
| Expect.equals(call(zeroArgs.toJS, []), '0'); |
| Expect.equals(call(zeroArgs.toJS, ['extra']), '0'); |
| Expect.equals(call(zeroArgs.toJS, [1.0]), '0'); |
| Expect.equals(call(zeroArgsThis.toJSCaptureThis, []), '0'); |
| Expect.equals(call(zeroArgs.toJSCaptureThis, []), '0'); |
| |
| // Conversion round-trip test. |
| final tearOff = zeroArgs; |
| Expect.identical(tearOff, tearOff.toJS.toDart); |
| final tearOffThis = zeroArgsThis; |
| Expect.identical(tearOffThis, tearOffThis.toJSCaptureThis.toDart); |
| |
| // Avoid rewrapping test. |
| if (isDDC || isDart2JS) { |
| Expect.throwsArgumentError(() => (zeroArgs.toJS as String Function()).toJS); |
| Expect.throwsArgumentError( |
| () => (zeroArgsThis.toJSCaptureThis as String Function()).toJSCaptureThis, |
| ); |
| } |
| } |
| |
| void testOne() { |
| // Type tests. |
| Expect.throws(() => call(oneRequired.toJS, [0])); |
| Expect.throwsWhen(soundNullSafety, () => call(oneOptional.toJS, [null])); |
| Expect.throwsWhen( |
| soundNullSafety, |
| () => call(oneOptional.toJS, ['undefined']), |
| ); |
| Expect.throws(() => call(oneOptionalThis.toJSCaptureThis, [true])); |
| |
| // Arity tests. |
| Expect.throws(() => call(oneRequired.toJS, [])); |
| Expect.equals(call(oneRequired.toJS, ['a']), 'a'); |
| Expect.equals(call(oneRequired.toJS, ['a', 'extra']), 'a'); |
| Expect.equals(call(oneOptional.toJS, []), 'default'); |
| Expect.equals(call(oneOptional.toJS, ['a']), 'a'); |
| Expect.equals(call(oneOptional.toJS, ['a', 'extra']), 'a'); |
| Expect.equals(call(oneOptionalThis.toJSCaptureThis, ['a']), 'a'); |
| if (soundNullSafety) { |
| // `this` can be null or a JSObject depending on strict mode, which in turn |
| // depends on the compiler. To make this consistent, only run when sound |
| // null safety is enabled. |
| Expect.throws(() => call(oneRequired.toJSCaptureThis, [])); |
| } |
| |
| // Function subtyping tests. |
| Expect.equals(call((oneOptional as String Function()).toJS, []), 'default'); |
| // Throws away the additional args due to the static typing. |
| Expect.equals( |
| call((oneOptional as String Function()).toJS, ['a']), |
| 'default', |
| ); |
| Expect.equals( |
| call((oneOptionalThis as String Function(JSObject?)).toJSCaptureThis, [ |
| 'a', |
| ]), |
| 'default', |
| ); |
| |
| // Conversion round-trip test. |
| final tearOff = oneRequired; |
| Expect.identical(tearOff, tearOff.toJS.toDart); |
| final tearOffThis = oneOptionalThis; |
| Expect.identical(tearOffThis, tearOffThis.toJSCaptureThis.toDart); |
| |
| // Avoid rewrapping test. |
| if (isDDC || isDart2JS) { |
| Expect.throwsArgumentError( |
| () => (oneOptional.toJS as String Function()).toJS, |
| ); |
| Expect.throwsArgumentError( |
| () => (oneOptionalThis.toJSCaptureThis as String Function(JSObject?)) |
| .toJSCaptureThis, |
| ); |
| } |
| } |
| |
| void testTwo() { |
| // Type tests. |
| Expect.throws(() => call(twoOptional.toJS, [false, 'b'])); |
| Expect.throws(() => call(twoOptional.toJS, ['a', 1.0])); |
| Expect.throwsWhen( |
| soundNullSafety, |
| () => call(oneRequiredOneOptional.toJS, ['undefined', 'b']), |
| ); |
| Expect.throws(() => call(oneRequiredOneOptional.toJS, ['a', true])); |
| Expect.throws(() => call(twoRequired.toJS, [0, 'b'])); |
| Expect.throws(() => call(twoRequired.toJS, ['a', 0])); |
| Expect.throws(() => call(oneRequiredOneOptional.toJSCaptureThis, [0])); |
| |
| // Arity tests. |
| Expect.throws(() => call(twoRequired.toJS, [])); |
| Expect.throws(() => call(twoRequired.toJS, ['a'])); |
| Expect.equals(call(twoRequired.toJS, ['a', 'b']), 'ab'); |
| Expect.equals(call(twoRequired.toJS, ['a', 'b', 'extra']), 'ab'); |
| Expect.throws(() => call(oneRequiredOneOptional.toJS, [])); |
| Expect.equals(call(oneRequiredOneOptional.toJS, ['a']), 'adefault'); |
| Expect.equals(call(oneRequiredOneOptional.toJS, ['a', 'b']), 'ab'); |
| Expect.equals(call(oneRequiredOneOptional.toJS, ['a', 'b', 'extra']), 'ab'); |
| Expect.equals(call(twoOptional.toJS, []), 'defaultdefault'); |
| Expect.equals(call(twoOptional.toJS, ['a']), 'adefault'); |
| Expect.equals(call(twoOptional.toJS, ['a', 'b']), 'ab'); |
| Expect.equals(call(twoOptional.toJS, ['a', 'b', 'extra']), 'ab'); |
| Expect.equals( |
| call(oneRequiredOneOptionalThis.toJSCaptureThis, ['a', 'b', 'extra']), |
| 'ab', |
| ); |
| Expect.equals( |
| call(oneRequiredOneOptionalThis.toJSCaptureThis, ['a']), |
| 'adefault', |
| ); |
| |
| // Function subtyping tests. |
| // TODO(55881): dart2wasm's type conversions are based on the static type, |
| // whereas DDC and dart2js only do type checks based on the runtime type. We |
| // can't replicate dart2Wasm's behavior in DDC and dart2js as it would require |
| // a new Dart trampoline for every function, so there's a discrepancy when we |
| // use a static type with different parameter types. |
| var closure = () => |
| call((twoRequired as String Function(String, String)).toJS, ['a', null]); |
| if (isDDC || isDart2JS) { |
| Expect.equals(closure(), 'anull'); |
| } else { |
| Expect.throws(closure); |
| } |
| Expect.throws( |
| () => call((oneRequiredOneOptional as String Function(String)).toJS, []), |
| ); |
| Expect.equals( |
| call((oneRequiredOneOptional as String Function(String)).toJS, ['a']), |
| 'adefault', |
| ); |
| Expect.equals( |
| call((oneRequiredOneOptional as String Function(String)).toJS, ['a', 0]), |
| 'adefault', |
| ); |
| Expect.equals( |
| call((twoOptional as String Function()).toJS, []), |
| 'defaultdefault', |
| ); |
| Expect.equals( |
| call((twoOptional as String Function()).toJS, ['a']), |
| 'defaultdefault', |
| ); |
| Expect.equals( |
| call((twoOptional as String Function([String])).toJS, []), |
| 'defaultdefault', |
| ); |
| Expect.equals( |
| call((twoOptional as String Function([String])).toJS, ['a']), |
| 'adefault', |
| ); |
| Expect.equals( |
| call((twoOptional as String Function([String])).toJS, ['a', false]), |
| 'adefault', |
| ); |
| Expect.equals( |
| call( |
| (oneRequiredOneOptionalThis as String Function(JSObject?, String)) |
| .toJSCaptureThis, |
| ['a', 'b'], |
| ), |
| 'adefault', |
| ); |
| |
| // `undefined` tests. |
| Expect.equals(call(twoRequired.toJS, ['a', 'undefined']), 'anull'); |
| // TODO(55884): DDC lowers function with defaults to use the JS default |
| // argument syntax, which means passing `undefined` results in DDC replacing |
| // it with the default, instead of keeping it as `undefined`. |
| Expect.equals( |
| call(oneRequiredOneOptional.toJS, ['a', 'undefined']), |
| isDDC ? 'adefault' : 'anull', |
| ); |
| Expect.equals( |
| call(twoOptional.toJS, ['a', 'undefined']), |
| isDDC ? 'adefault' : 'anull', |
| ); |
| |
| // Conversion round-trip test. |
| final tearOff = twoRequired; |
| Expect.identical(tearOff, tearOff.toJS.toDart); |
| final tearOffThis = oneRequiredOneOptionalThis; |
| Expect.identical(tearOffThis, tearOffThis.toJSCaptureThis.toDart); |
| |
| // Avoid rewrapping test. |
| if (isDDC || isDart2JS) { |
| Expect.throwsArgumentError( |
| () => (oneRequiredOneOptional.toJS as String Function(String)).toJS, |
| ); |
| Expect.throwsArgumentError( |
| () => |
| (oneRequiredOneOptionalThis.toJSCaptureThis |
| as String Function(JSObject?, String)) |
| .toJSCaptureThis, |
| ); |
| } |
| } |
| |
| // To avoid making this test unreadably long, the remaining tests choose a small |
| // subset of all possible tests for general validation. |
| void testThree() { |
| // Type tests. |
| Expect.throws(() => call(threeRequired.toJS, [0, 'b', 'c'])); |
| Expect.throws(() => call(oneRequiredTwoOptional.toJS, ['a', false])); |
| Expect.throws(() => call(threeOptionalThis.toJSCaptureThis, [true])); |
| |
| // Arity tests. |
| Expect.equals(call(twoRequiredOneOptional.toJS, ['a', 'b']), 'abdefault'); |
| Expect.throws(() => call(oneRequiredTwoOptional.toJS, [])); |
| Expect.equals( |
| call(threeOptionalThis.toJSCaptureThis, ['a', 'b']), |
| 'abdefault', |
| ); |
| |
| // Function subtyping tests. |
| var closure = () => call( |
| (twoRequiredOneOptional as String Function(String, String)).toJS, |
| ['a', null, 'c'], |
| ); |
| if (isDDC || isDart2JS) { |
| Expect.equals(closure(), 'anulldefault'); |
| } else { |
| Expect.throws(closure); |
| } |
| Expect.equals( |
| call((threeOptional as String Function([String])).toJS, ['a', 0, true]), |
| 'adefaultdefault', |
| ); |
| Expect.equals( |
| call( |
| (threeOptionalThis as String Function([JSObject?, String, String?])) |
| .toJSCaptureThis, |
| ['a', 'b', false], |
| ), |
| 'abdefault', |
| ); |
| |
| // `undefined` tests. |
| Expect.equals( |
| call(threeOptional.toJS, ['a', 'undefined']), |
| isDDC ? 'adefaultdefault' : 'anulldefault', |
| ); |
| |
| // Conversion round-trip test. |
| final tearOff = threeRequired; |
| Expect.identical(tearOff, tearOff.toJS.toDart); |
| final tearOffThis = threeOptionalThis; |
| Expect.identical(tearOffThis, tearOffThis.toJSCaptureThis.toDart); |
| |
| // Avoid rewrapping test. |
| if (isDDC || isDart2JS) { |
| Expect.throwsArgumentError( |
| () => (twoRequiredOneOptional.toJS as String Function(String, String?)) |
| .toJS, |
| ); |
| Expect.throwsArgumentError( |
| () => (threeOptionalThis.toJSCaptureThis as String Function(JSObject?)) |
| .toJSCaptureThis, |
| ); |
| } |
| } |
| |
| void testFour() { |
| // Type tests. |
| Expect.throws( |
| () => call(threeRequiredOneOptional.toJS, ['a', 'b', 'c', true]), |
| ); |
| Expect.throws(() => call(oneRequiredThreeOptional.toJS, [false])); |
| if (soundNullSafety) { |
| // `this` can be null or a JSObject depending on strict mode, which in turn |
| // depends on the compiler. To make this consistent, only run when sound |
| // null safety is enabled. |
| Expect.throws(() => call(oneRequiredThreeOptional.toJSCaptureThis, ['a'])); |
| } |
| |
| // Arity tests. |
| Expect.equals(call(fourRequired.toJS, ['a', 'b', 'c', 'd', false]), 'abcd'); |
| Expect.equals(call(fourOptional.toJS, ['a']), 'adefaultdefaultdefault'); |
| Expect.equals( |
| call(oneRequiredThreeOptionalThis.toJSCaptureThis, ['a', 'b']), |
| 'abdefaultdefault', |
| ); |
| |
| // Function subtyping tests. |
| final closure = () => call( |
| (threeRequiredOneOptional as String Function(String, String, String)).toJS, |
| ['a', null, 'c'], |
| ); |
| if (isDDC || isDart2JS) { |
| Expect.equals(closure(), 'anullcdefault'); |
| } else { |
| Expect.throws(closure); |
| } |
| Expect.equals( |
| call( |
| (twoRequiredTwoOptional as String Function(String, String?, [String])) |
| .toJS, |
| ['a', null], |
| ), |
| 'anulldefaultdefault', |
| ); |
| Expect.equals( |
| call( |
| (oneRequiredThreeOptionalThis as String Function(JSObject?, String)) |
| .toJSCaptureThis, |
| ['a', 'b'], |
| ), |
| 'adefaultdefaultdefault', |
| ); |
| |
| // `undefined` tests. |
| Expect.equals( |
| call(oneRequiredThreeOptional.toJS, ['a', 'undefined', 'c']), |
| isDDC ? 'adefaultcdefault' : 'anullcdefault', |
| ); |
| |
| // Conversion round-trip test. |
| final tearOff = fourRequired; |
| Expect.identical(tearOff, tearOff.toJS.toDart); |
| final tearOffThis = oneRequiredThreeOptionalThis; |
| Expect.identical(tearOffThis, tearOffThis.toJSCaptureThis.toDart); |
| |
| // Avoid rewrapping test. |
| if (isDDC || isDart2JS) { |
| Expect.throwsArgumentError( |
| () => (oneRequiredThreeOptional.toJS as String Function(String)).toJS, |
| ); |
| Expect.throwsArgumentError( |
| () => |
| (oneRequiredThreeOptionalThis.toJSCaptureThis |
| as String Function(JSObject?, String)) |
| .toJSCaptureThis, |
| ); |
| } |
| } |
| |
| void testFive() { |
| // Type tests. |
| Expect.throws(() => call(twoRequiredThreeOptional.toJS, ['a', 0])); |
| Expect.throws(() => call(fiveOptional.toJS, [false])); |
| Expect.throws( |
| () => call(threeRequiredTwoOptionalThis.toJSCaptureThis, ['a', 'b', 1.0]), |
| ); |
| |
| // Arity tests. |
| Expect.equals(call(fiveRequired.toJS, ['a', 'b', 'c', 'd', 'e', 0]), 'abcde'); |
| Expect.equals( |
| call(fourRequiredOneOptional.toJS, ['a', null, 'c', 'd']), |
| 'anullcddefault', |
| ); |
| Expect.equals( |
| call(threeRequiredTwoOptionalThis.toJSCaptureThis, ['a', 'b', 'c', 'd']), |
| 'abcddefault', |
| ); |
| |
| // Function subtyping tests. |
| final closure = () => call( |
| (threeRequiredTwoOptional |
| as String Function(String, String, String, [String])) |
| .toJS, |
| ['a', null, 'c'], |
| ); |
| if (isDDC || isDart2JS) { |
| Expect.equals(closure(), 'anullcdefaultdefault'); |
| } else { |
| Expect.throws(closure); |
| } |
| Expect.equals( |
| call( |
| (twoRequiredThreeOptional as String Function(String, String?, [String])) |
| .toJS, |
| ['a', null, 'c'], |
| ), |
| 'anullcdefaultdefault', |
| ); |
| Expect.equals( |
| call( |
| (threeRequiredTwoOptionalThis |
| as String Function(JSObject?, String, String?, String)) |
| .toJSCaptureThis, |
| ['a', 'b', 'c', 'd'], |
| ), |
| 'abcdefaultdefault', |
| ); |
| |
| // `undefined` tests. |
| Expect.equals( |
| call(oneRequiredFourOptional.toJS, ['a', 'undefined', 'c', 'd', 'e']), |
| isDDC ? 'adefaultcde' : 'anullcde', |
| ); |
| |
| // Conversion round-trip test. |
| final tearOff = fiveRequired; |
| Expect.identical(tearOff, tearOff.toJS.toDart); |
| final tearOffThis = threeRequiredTwoOptionalThis; |
| Expect.identical(tearOffThis, tearOffThis.toJSCaptureThis.toDart); |
| |
| // Avoid rewrapping test. |
| if (isDDC || isDart2JS) { |
| Expect.throwsArgumentError( |
| () => (twoRequiredThreeOptional.toJS as String Function(String, String?)) |
| .toJS, |
| ); |
| Expect.throwsArgumentError( |
| () => |
| (threeRequiredTwoOptionalThis.toJSCaptureThis |
| as String Function(JSObject?, String, String?, String)) |
| .toJSCaptureThis, |
| ); |
| } |
| } |
| |
| // DDC and dart2js should use either a `dcall` or `Function.apply` for this. |
| void testSix() { |
| // Type tests. |
| Expect.throws(() => call(sixRequired.toJS, ['a', 'b', 0.0, 'd', 'e', 'f'])); |
| Expect.throws(() => call(threeRequiredThreeOptional.toJS, ['undefined'])); |
| Expect.throwsWhen( |
| soundNullSafety, |
| () => call(sixOptionalThis.toJSCaptureThis, [null]), |
| ); |
| |
| // Arity tests. |
| // Verify that we appropriately truncate arguments even though we don't have |
| // a special lowering for six arguments in DDC and dart2js. |
| Expect.equals( |
| call(fourRequiredTwoOptional.toJS, ['a', 'b', 'c', 'd', 'e', 'f', 0]), |
| 'abcdef', |
| ); |
| Expect.throws(() => call(twoRequiredFourOptional.toJS, [])); |
| Expect.equals( |
| call(sixOptionalThis.toJSCaptureThis, ['a', 'b', 'c', 'd', 'e', 'f', 0]), |
| 'abcdef', |
| ); |
| |
| // Function subtyping tests. |
| var closure = () => call( |
| (fiveRequiredOneOptional |
| as String Function(String, String, String, String, String)) |
| .toJS, |
| ['a', null, 'c', 'd', 'e', 'f'], |
| ); |
| if (isDDC || isDart2JS) { |
| Expect.equals(closure(), 'anullcdedefault'); |
| } else { |
| Expect.throws(closure); |
| } |
| Expect.equals( |
| call((oneRequiredFiveOptional as String Function(String, [String?])).toJS, [ |
| 'a', |
| 'b', |
| 0, |
| 0.0, |
| false, |
| ]), |
| 'abdefaultdefaultdefaultdefault', |
| ); |
| Expect.equals( |
| call((sixOptionalThis as String Function()).toJSCaptureThis, [ |
| true, |
| 0, |
| 0.0, |
| ]), |
| 'defaultdefaultdefaultdefaultdefaultdefault', |
| ); |
| |
| // `undefined` tests. |
| Expect.equals( |
| call(sixOptional.toJS, ['a', 'undefined', 'c', 'd', 'e']), |
| isDDC ? 'adefaultcdedefault' : 'anullcdedefault', |
| ); |
| |
| // Conversion round-trip test. |
| final tearOff = sixRequired; |
| Expect.identical(tearOff, tearOff.toJS.toDart); |
| final tearOffThis = sixOptionalThis; |
| Expect.identical(tearOffThis, tearOffThis.toJSCaptureThis.toDart); |
| |
| // Avoid rewrapping test. |
| if (isDDC || isDart2JS) { |
| Expect.throwsArgumentError( |
| () => (sixOptional.toJS as String Function()).toJS, |
| ); |
| Expect.throwsArgumentError( |
| () => (sixOptionalThis.toJSCaptureThis as String Function(JSObject?)) |
| .toJSCaptureThis, |
| ); |
| } |
| } |
| |
| void main() { |
| eval(''' |
| self.call = function(f, args) { |
| var convert = function(arg) { |
| return arg == 'undefined' ? undefined : arg; |
| }; |
| return f.apply(null, args.map((e) => convert(e))); |
| }; |
| '''); |
| testZero(); |
| testOne(); |
| testTwo(); |
| testThree(); |
| testFour(); |
| testFive(); |
| testSix(); |
| } |