From 56186c7e9619d5b9aab93511b48ae4373d2c843e Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Thu, 9 Apr 2026 09:22:14 -0700 Subject: [PATCH] [vm,dyn_modules] Fix dynamic calls from host app to a dynamic module Fix the following cases of incorrect optimizations: * In TFA, when anlyzing a dynamic call with unknown receiver type, do not assume that all possible targets can be computed at compile time (if there can be dynamically loaded classes). * In the AOT, disable optimizations for dynamic calls with unique selectors (if there can be dynamically loaded classes). TEST=pkg/dynamic_modules/test/data/dyn_call_from_host Change-Id: I39d620aae3c116de03d4a2a3fd61864d88c48c8e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/493960 Commit-Queue: Alexander Markov Reviewed-by: Sigmund Cherem Reviewed-by: Slava Egorov --- .../dyn_call_from_host/dynamic_interface.yaml | 6 ++++ .../test/data/dyn_call_from_host/main.dart | 34 +++++++++++++++++++ .../dyn_call_from_host/modules/entry1.dart | 19 +++++++++++ .../transformations/type_flow/analysis.dart | 9 ++++- .../vm/compiler/aot/aot_call_specializer.cc | 4 +++ 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 pkg/dynamic_modules/test/data/dyn_call_from_host/dynamic_interface.yaml create mode 100644 pkg/dynamic_modules/test/data/dyn_call_from_host/main.dart create mode 100644 pkg/dynamic_modules/test/data/dyn_call_from_host/modules/entry1.dart diff --git a/pkg/dynamic_modules/test/data/dyn_call_from_host/dynamic_interface.yaml b/pkg/dynamic_modules/test/data/dyn_call_from_host/dynamic_interface.yaml new file mode 100644 index 00000000000..67565b4ee3d --- /dev/null +++ b/pkg/dynamic_modules/test/data/dyn_call_from_host/dynamic_interface.yaml @@ -0,0 +1,6 @@ +# 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. + +callable: + - library: 'dart:core' diff --git a/pkg/dynamic_modules/test/data/dyn_call_from_host/main.dart b/pkg/dynamic_modules/test/data/dyn_call_from_host/main.dart new file mode 100644 index 00000000000..320bed7f249 --- /dev/null +++ b/pkg/dynamic_modules/test/data/dyn_call_from_host/main.dart @@ -0,0 +1,34 @@ +// 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 '../../common/testing.dart' as helper; +import 'package:expect/expect.dart'; + +class C1 { + int method1() => 1; + int method2() => 2; +} + +class C2 { + int method3() => 3; + int method4() => 4; +} + +final List escape = [C1(), C2()]; + +// Dynamic calls from the host app. +void main() async { + print(escape); + + final list = (await helper.load('entry1.dart')) as List; + dynamic o1 = list[0]; + Expect.equals('10', o1.method1()); + Expect.equals('20', o1.method2()); + + dynamic o2 = list[1]; + Expect.equals('30', o2.method3()); + Expect.equals('40', o2.method4()); + Expect.equals('50', o2.method5()); + helper.done(); +} diff --git a/pkg/dynamic_modules/test/data/dyn_call_from_host/modules/entry1.dart b/pkg/dynamic_modules/test/data/dyn_call_from_host/modules/entry1.dart new file mode 100644 index 00000000000..6eabfda1928 --- /dev/null +++ b/pkg/dynamic_modules/test/data/dyn_call_from_host/modules/entry1.dart @@ -0,0 +1,19 @@ +// 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. + +class D1 { + String method1() => '10'; + String method2() => '20'; +} + +class D2 { + String method3() => '30'; + String method4() => '40'; + String method5() => '50'; +} + +@pragma('dyn-module:entry-point') +Object? dynamicModuleEntrypoint() { + return [D1(), D2()]; +} diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart index 3ab2fbfd80b..d15667f0bca 100644 --- a/pkg/vm/lib/transformations/type_flow/analysis.dart +++ b/pkg/vm/lib/transformations/type_flow/analysis.dart @@ -701,6 +701,7 @@ final class _DispatchableInvocation extends _Invocation { assert(targets.isEmpty); + bool unknownTargets = false; if (receiver is ConcreteType) { _collectTargetsForConcreteType(receiver, targets, typeFlowAnalysis); } else if (receiver is SetType) { @@ -709,6 +710,12 @@ final class _DispatchableInvocation extends _Invocation { } } else if (receiver is AnyInstanceType) { _collectTargetsForSelector(targets, typeFlowAnalysis); + // Any class from a dynamic module may have unknown target for the + // dynamic call with AnyInstanceType receiver. + unknownTargets = typeFlowAnalysis + .hierarchyCache + ._objectTFClass + .hasDynamicallyExtendableSubtypes; } else { assert(receiver is EmptyType); } @@ -725,7 +732,7 @@ final class _DispatchableInvocation extends _Invocation { ); } - return true; + return !unknownTargets; } void _collectTargetsForNull( diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc index 6f3f550905e..79d106b52ab 100644 --- a/runtime/vm/compiler/aot/aot_call_specializer.cc +++ b/runtime/vm/compiler/aot/aot_call_specializer.cc @@ -54,6 +54,10 @@ DEFINE_FLAG(int, static void GetUniqueDynamicTarget(IsolateGroup* isolate_group, const String& fname, Object* function) { + if (isolate_group->has_dynamically_extendable_classes()) { + *function = Object::null(); + return; + } UniqueFunctionsMap functions_map( isolate_group->object_store()->unique_dynamic_targets()); ASSERT(fname.IsSymbol());