| // 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 '../shared/lib.dart'; |
| |
| @pragma('dyn-module:entry-point') |
| Future<bool> dynamicModuleEntrypoint() async { |
| dynamic a1 = sharedA1; |
| |
| // Dynamic method, getter, and setter calls. |
| Expect.equals(a1.m4(14), 4); |
| a1.m1(); |
| a1.s3 = 'hello'; |
| Expect.equals(a1.g2, 'hello'); |
| Expect.equals(a1.m4(14), 4); |
| |
| // Dynamic use of fields. |
| Expect.equals(a1.f1, null); |
| a1.f1 = 2; |
| Expect.equals(a1.f1, 2); |
| Expect.equals(a1.f2, 3); |
| |
| if (const String.fromEnvironment('dynamic.modules.test.mode') == 'aot') { |
| // TODO(b/448095881): the tearoff operation currently fails in JIT |
| // Dynamic call via getter tearoff. |
| dynamic m4to = a1.m4; |
| Expect.equals(m4to(14), 4); |
| } |
| |
| // Ensure we don't accidentally call the target without a dynamic forwarder, |
| // otherwise parameter checks are omitted accidentally. |
| Expect.throws( |
| () => a1.m4('str-value'), |
| (e) => e is ArgumentError || e is TypeError, |
| 'missing error in dynamic call to host', |
| ); |
| |
| // Members defined in a dynamic module are dynamically-callable. |
| dynamic b = B(); |
| Expect.equals(b.m4(14), 13); |
| Expect.equals(b.m5(14), 16); |
| |
| // Members defined in a dynamic module also properly check their parameters. |
| Expect.throws( |
| () => b.m4('str-value'), |
| (e) => e is ArgumentError || e is TypeError, |
| 'missing error in dynamic call within dynamic module', |
| ); |
| Expect.throws( |
| () => b.m5('str-value'), |
| (e) => e is ArgumentError || e is TypeError, |
| 'missing error in dynamic call within dynamic module', |
| ); |
| |
| // A1.m6 was not exposed, even though m6 is an allowed selector. |
| Expect.throws( |
| () => a1.m6(), |
| (e) => e is NoSuchMethodError, |
| 'missing error in dynamic call to unexposed allowed method m6', |
| ); |
| |
| // A2.m1 was not exposed, but A1.m1 was, so the selector is allowed. |
| dynamic a2 = sharedA2; |
| Expect.throws( |
| () => a2.m1(), |
| (e) => e is NoSuchMethodError, |
| 'missing error in dynamic call to unexposed class C.m1', |
| ); |
| |
| // A1.m7 is exposed in other ways, but not dynamically-callable. |
| Expect.throws( |
| () => a1.m7(), |
| (e) => e is NoSuchMethodError, |
| 'missing error in dynamic call to callable but not dynamically-callable method m7', |
| ); |
| |
| dynamic a3 = sharedA4; |
| |
| // Interface class A3, where A3.m9 is exposed, but A3.m6 is not. |
| Expect.equals(a3.m9(1), 11); |
| Expect.throws( |
| () => a3.m6(), |
| (e) => e is NoSuchMethodError, |
| 'missing error in dynamic call to not dynamically-callable host method', |
| ); |
| |
| // Dynamic module class, base class only partially exposed. |
| dynamic b5 = B5(); |
| Expect.throws( |
| () => b5.m10(), // target is unexposed host |
| (e) => e is NoSuchMethodError, |
| 'missing error in dynamic call to not dynamically-callable host method', |
| ); |
| Expect.equals(b5.m11(), 4); // target in dynamic module |
| Expect.equals(b5.m12(), 1); // target is exposed in host |
| Expect.equals(b5.m13(), 4); // target in dynamic module |
| |
| sharedB6 = B6(); |
| sharedB7 = B7(); |
| |
| return true; |
| } |
| |
| class B { |
| // m4 matches an exposed selector name from the host. |
| int m4(int value) { |
| return value - 1; |
| } |
| |
| // m5 matches a selector name in the allowlist flag. |
| int m5(int value) { |
| return value + 2; |
| } |
| } |
| |
| class B5 extends A5 { |
| @override |
| int m11() => 4; |
| |
| @override |
| int m13() => 4; |
| } |
| |
| class B6 { |
| int m14() => 14; |
| } |
| |
| class B7 extends A7 { |
| @override |
| int m16() => 4; |
| |
| @override |
| int m18() => 4; |
| } |