2e2f8ae7b3
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>
103 lines
2.7 KiB
Dart
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";
|
|
}
|