Files
sdk/tests/web/wasm/dynamic_calls_test.dart
Martin Kustermann 2e2f8ae7b3 [dart2wasm] Make normal dynamic calls avoid wasm array creations
Currently a dynamic call will call a helper which will create
3 array objects (for type, positional and named arguments).

It will then pass those wasm arrays to type checker methods.
Those will then get values out of the array, type check and call
the target.

We change this now such that in the normal case (**) we avoid
the array creations. Instead we make a dynamic forwarder function
per target and call shape.

We also outline the array creation when creating `Invocation`
objects. So for simple caller shapes - such as `clone()` the
creation of an `Invocation` object is a call without arguments
instead of various array creations.

Issue https://github.com/dart-lang/sdk/issues/62640

(**) If the dynamic call could be call-via-field we still create
those arrays, as we use them for closure type checking.

Change-Id: Ia8f3f3cd95f650bd8706a15ed68a43db4de80a6e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/487020
Reviewed-by: Srujan Gaddam <srujzs@google.com>
2026-03-12 02:17:07 -07:00

103 lines
2.7 KiB
Dart

// Copyright (c) 2026, 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.
import 'package:expect/expect.dart';
import '' deferred as D;
void main() async {
await D.loadLibrary();
final list = D.getObjects();
final a = list[int.parse('0')];
final b = list[int.parse('1')];
final c = list[int.parse('2')];
final d = list[int.parse('3')];
// Successfull dynamic method/getter/setter call
Expect.equals(52, a.foo(10));
Expect.equals(42, a.fooGetter);
a.fooSetter = 100;
Expect.equals(100, a.fooGetter);
// Successfull dynamic tearoff + dynamic closure call.
final fooTearOff = a.foo;
Expect.equals(110, fooTearOff(10));
// Successful call via field.
Expect.equals(20, b.foo(10));
// Argument type check errors
Expect.throws<TypeError>(() => a.foo(''));
Expect.throws<TypeError>(() => a.fooSetter = '');
// User-defined noSuchMethod handler
Expect.equals(1, c.foo(1));
Expect.equals(2, c.bar(1, 2));
Expect.equals(3, c.baz);
c.buz = 2;
Expect.equals(4, D.cCounter);
// Default noSuchMethod handler
Expect.throwsNoSuchMethodError(() => d.foo(10));
Expect.throwsNoSuchMethodError(() => d.fooGetter);
Expect.throwsNoSuchMethodError(() => d.fooSetter = 10);
final e = list[int.parse('4')];
// Optional positional and type args
Expect.equals("$Object 10 1 2", e.foo(10));
Expect.equals("$Object 10 20 2", e.foo(10, 20));
Expect.equals("$Object 10 20 30", e.foo(10, 20, 30));
Expect.equals("$String hi 1 2", e.foo<String>("hi"));
Expect.equals("$String hi 20 30", e.foo<String>("hi", 20, 30));
// Optional named and type args
Expect.equals("$Object $dynamic 10 null 3", e.bar(10));
Expect.equals("$int $String 10 hi 3", e.bar<int, String>(10, y: "hi"));
Expect.equals(
"$int $String 10 hi 40",
e.bar<int, String>(10, y: "hi", z: 40),
);
Expect.equals("$Object $dynamic 10 null 40", e.bar(10, z: 40));
// NoSuchMethod for wrong shape
Expect.throwsNoSuchMethodError(() => e.foo());
Expect.throwsNoSuchMethodError(() => e.foo(1, 2, 3, 4));
Expect.throwsNoSuchMethodError(() => e.bar(10, unknown: 1));
}
List<dynamic> getObjects() => <dynamic>[
A(),
B((int x) => x * 2),
C(),
Object(),
OptionalArgs(),
];
class A {
int _value = 42;
int foo(int x) => x + _value;
int get fooGetter => _value;
set fooSetter(int x) => _value = x;
}
class B {
final Function foo;
B(this.foo);
}
int cCounter = 0;
class C {
@override
noSuchMethod(Invocation i) => ++cCounter;
}
class OptionalArgs {
String foo<T extends Object>(T x, [int y = 1, int z = 2]) => "$T $x $y $z";
String bar<T extends Object, U>(T x, {U? y, int z = 3}) => "$T $U $x $y $z";
}