Skip to content

Commit 6156dd6

Browse files
alexmarkovCommit Queue
authored andcommitted
[dynamic modules] Respect dyn-module:language-impl:* pragmas during dynamic module validation
Dynamic module validation now accepts reference to classes and members from 'dart:*' libraries if they are annotated with dyn-module:language-impl:* pragmas. This matches the behavior of dynamic interface annotator. This is a follow-up to https://dart-review.googlesource.com/c/sdk/+/418663. Also, when checking for possible dynamic overrides in TFA, treat _enumToString from dart:core as a public name because front-end desugars enums into classes which override private _enumToString method in a different library. TEST=pkg/dynamic_modules/test/data/enum Fixes b/395992622 Change-Id: I45ed9855f806224a510fa4e56962607de3371eb0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/423820 Reviewed-by: Slava Egorov <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Reviewed-by: Nate Biggs <[email protected]> Commit-Queue: Alexander Markov <[email protected]>
1 parent edd14f3 commit 6156dd6

File tree

15 files changed

+151
-78
lines changed

15 files changed

+151
-78
lines changed

pkg/dart2wasm/lib/compile.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ Future<CompilationResult> compileToModule(
223223
moduleStrategy = DynamicMainModuleStrategy(
224224
component,
225225
coreTypes,
226-
target,
227226
File.fromUri(dynamicInterfaceUri).readAsStringSync(),
228227
options.dynamicInterfaceUri!);
229228
} else if (isDynamicModule) {

pkg/dart2wasm/lib/dynamic_modules.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import 'package:kernel/ast.dart';
66
import 'package:kernel/core_types.dart';
77
import 'package:kernel/library_index.dart';
8-
import 'package:kernel/target/targets.dart' show Target;
98
import 'package:vm/metadata/procedure_attributes.dart'
109
show ProcedureAttributesMetadata;
1110
import 'package:vm/transformations/dynamic_interface_annotator.dart'
@@ -102,7 +101,6 @@ class DynamicModuleOutputData extends ModuleOutputData {
102101
class DynamicMainModuleStrategy extends DefaultModuleStrategy with KernelNodes {
103102
@override
104103
final CoreTypes coreTypes;
105-
final Target target;
106104
@override
107105
final LibraryIndex index;
108106
final Uri dynamicInterfaceSpecificationBaseUri;
@@ -111,7 +109,6 @@ class DynamicMainModuleStrategy extends DefaultModuleStrategy with KernelNodes {
111109
DynamicMainModuleStrategy(
112110
super.component,
113111
this.coreTypes,
114-
this.target,
115112
this.dynamicInterfaceSpecification,
116113
this.dynamicInterfaceSpecificationBaseUri)
117114
: index = coreTypes.index;
@@ -120,7 +117,7 @@ class DynamicMainModuleStrategy extends DefaultModuleStrategy with KernelNodes {
120117
void prepareComponent() {
121118
// Annotate the kernel with info from dynamic interface.
122119
dynamic_interface_annotator.annotateComponent(dynamicInterfaceSpecification,
123-
dynamicInterfaceSpecificationBaseUri, component, coreTypes, target);
120+
dynamicInterfaceSpecificationBaseUri, component, coreTypes);
124121
_addImplicitPragmas();
125122
_addMetadataPragmas();
126123
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
# for details. All rights reserved. Use of this source code is governed by a
3+
# BSD-style license that can be found in the LICENSE file.
4+
5+
callable:
6+
# TODO(sigmund): This should be included by default
7+
- library: 'dart:core'
8+
class: 'Object'
9+
- library: 'dart:core'
10+
class: 'String'
11+
- library: 'dart:core'
12+
class: 'int'
13+
- library: 'dart:core'
14+
class: 'List'
15+
- library: 'dart:core'
16+
class: 'pragma'
17+
member: '_'
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import '../../common/testing.dart' as helper;
6+
import 'package:expect/expect.dart';
7+
8+
void main() async {
9+
final result = await helper.load('entry1.dart') as Enum;
10+
Expect.equals('e2', result.name);
11+
Expect.equals('Foo.e2', result.toString());
12+
helper.done();
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
enum Foo {
6+
e1,
7+
e2,
8+
e3;
9+
}
10+
11+
@pragma('dyn-module:entry-point')
12+
Object? dynamicModuleEntrypoint() => Foo.e2;

pkg/front_end/lib/src/api_prototype/dynamic_module_validator.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
library front_end.dynamic_module_validator;
66

77
export '../kernel/dynamic_module_validator.dart'
8-
show DynamicInterfaceSpecification;
8+
show DynamicInterfaceLanguageImplPragmas, DynamicInterfaceSpecification;

pkg/front_end/lib/src/kernel/dynamic_module_validator.dart

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:kernel/ast.dart';
66
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
7+
import 'package:kernel/core_types.dart' show CoreTypes;
78
import 'package:kernel/library_index.dart' show LibraryIndex;
89
import 'package:yaml/yaml.dart';
910
import '../source/source_loader.dart' show SourceLoader;
@@ -28,15 +29,18 @@ void validateDynamicModule(
2829
String dynamicInterfaceSpecification,
2930
Uri dynamicInterfaceSpecificationUri,
3031
Component component,
32+
CoreTypes coreTypes,
3133
ClassHierarchy hierarchy,
3234
List<Library> libraries,
3335
SourceLoader loader) {
3436
final DynamicInterfaceSpecification spec = new DynamicInterfaceSpecification(
3537
dynamicInterfaceSpecification,
3638
dynamicInterfaceSpecificationUri,
3739
component);
40+
final DynamicInterfaceLanguageImplPragmas languageImplPragmas =
41+
new DynamicInterfaceLanguageImplPragmas(coreTypes);
3842
final _DynamicModuleValidator validator = new _DynamicModuleValidator(
39-
spec, new Set.of(libraries), hierarchy, loader);
43+
spec, languageImplPragmas, new Set.of(libraries), hierarchy, loader);
4044
for (Library library in libraries) {
4145
library.accept(validator);
4246
}
@@ -183,17 +187,73 @@ class DynamicInterfaceSpecification {
183187
}
184188
}
185189

190+
/// Recognizes dyn-module:language-impl:* pragmas which can be used
191+
/// to annotate classes and members in core libraries to include
192+
/// them to the dynamic interface automatically.
193+
class DynamicInterfaceLanguageImplPragmas {
194+
static const String extendablePragmaName =
195+
"dyn-module:language-impl:extendable";
196+
static const String canBeOverriddenPragmaName =
197+
"dyn-module:language-impl:can-be-overridden";
198+
static const String callablePragmaName = "dyn-module:language-impl:callable";
199+
200+
final CoreTypes coreTypes;
201+
DynamicInterfaceLanguageImplPragmas(this.coreTypes);
202+
203+
bool isPlatformLibrary(Library library) => library.importUri.isScheme('dart');
204+
205+
bool isExtendable(Class node) =>
206+
isPlatformLibrary(node.enclosingLibrary) &&
207+
// Coverage-ignore(suite): Not run.
208+
isAnnotatedWith(node, extendablePragmaName);
209+
210+
bool canBeOverridden(Member node) =>
211+
isPlatformLibrary(node.enclosingLibrary) &&
212+
// Coverage-ignore(suite): Not run.
213+
isAnnotatedWith(node, canBeOverriddenPragmaName);
214+
215+
bool isCallable(TreeNode node) => switch (node) {
216+
Member() => isPlatformLibrary(node.enclosingLibrary) &&
217+
// Coverage-ignore(suite): Not run.
218+
(isAnnotatedWith(node, callablePragmaName) ||
219+
(!node.name.isPrivate &&
220+
node.enclosingClass != null &&
221+
isAnnotatedWith(node.enclosingClass!, callablePragmaName))),
222+
Class() => isPlatformLibrary(node.enclosingLibrary) &&
223+
isAnnotatedWith(node, callablePragmaName),
224+
_ => // Coverage-ignore(suite): Not run.
225+
throw 'Unexpected node ${node.runtimeType} $node'
226+
};
227+
228+
bool isAnnotatedWith(Annotatable node, String pragmaName) {
229+
for (Expression annotation in node.annotations) {
230+
if (annotation case ConstantExpression(:var constant)) {
231+
if (constant case InstanceConstant(:var classNode, :var fieldValues)
232+
when classNode == coreTypes.pragmaClass) {
233+
// Coverage-ignore-block(suite): Not run.
234+
if (fieldValues[coreTypes.pragmaName.fieldReference]
235+
case StringConstant(:var value) when value == pragmaName) {
236+
return true;
237+
}
238+
}
239+
}
240+
}
241+
return false;
242+
}
243+
}
244+
186245
class _DynamicModuleValidator extends RecursiveVisitor {
187246
final DynamicInterfaceSpecification spec;
247+
final DynamicInterfaceLanguageImplPragmas languageImplPragmas;
188248
final Set<Library> moduleLibraries;
189249
final ClassHierarchy hierarchy;
190250
final SourceLoader loader;
191251
final Set<Constant> _visitedConstants = new Set<Constant>.identity();
192252

193253
TreeNode? _enclosingTreeNode;
194254

195-
_DynamicModuleValidator(
196-
this.spec, this.moduleLibraries, this.hierarchy, this.loader) {
255+
_DynamicModuleValidator(this.spec, this.languageImplPragmas,
256+
this.moduleLibraries, this.hierarchy, this.loader) {
197257
_addLibraryExports(spec.callable);
198258
_addLibraryExports(spec.extendable);
199259
_addLibraryExports(spec.canBeOverridden);
@@ -493,7 +553,9 @@ class _DynamicModuleValidator extends RecursiveVisitor {
493553
if (target is Procedure) {
494554
target = _unwrapMixinStubs(target);
495555
}
496-
if (!_isFromDynamicModule(target) && !_isSpecified(target, spec.callable)) {
556+
if (!_isFromDynamicModule(target) &&
557+
!_isSpecified(target, spec.callable) &&
558+
!languageImplPragmas.isCallable(target)) {
497559
switch (target) {
498560
case Constructor():
499561
String name = target.enclosingClass.name;
@@ -534,7 +596,8 @@ class _DynamicModuleValidator extends RecursiveVisitor {
534596
void _verifyExtendable(Supertype base, Class node) {
535597
final Class baseClass = base.classNode;
536598
if (!_isFromDynamicModule(baseClass) &&
537-
!_isSpecified(baseClass, spec.extendable)) {
599+
!_isSpecified(baseClass, spec.extendable) &&
600+
!languageImplPragmas.isExtendable(baseClass)) {
538601
loader.addProblem(
539602
templateClassShouldBeListedAsExtendableInDynamicInterface
540603
.withArguments(baseClass.name),
@@ -578,7 +641,8 @@ class _DynamicModuleValidator extends RecursiveVisitor {
578641

579642
void _verifyOverride(Member ownMember, Member superMember) {
580643
if (!_isFromDynamicModule(superMember) &&
581-
!_isSpecified(superMember, spec.canBeOverridden)) {
644+
!_isSpecified(superMember, spec.canBeOverridden) &&
645+
!languageImplPragmas.canBeOverridden(superMember)) {
582646
loader.addProblem(
583647
templateMemberShouldBeListedAsCanBeOverriddenInDynamicInterface
584648
.withArguments(

pkg/front_end/lib/src/kernel/kernel_target.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,7 @@ class KernelTarget {
14841484
dynamicInterfaceSpecification,
14851485
dynamicInterfaceSpecificationUri,
14861486
component!,
1487+
loader.coreTypes,
14871488
loader.hierarchy,
14881489
loader.libraries,
14891490
loader);

pkg/front_end/test/coverage_suite_expected.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
710710
),
711711
// 100.0%.
712712
"package:front_end/src/kernel/dynamic_module_validator.dart": (
713-
hitCount: 322,
713+
hitCount: 358,
714714
missCount: 0,
715715
),
716716
// 100.0%.

pkg/front_end/test/spell_checking_list_code.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,7 @@ postfix
13221322
pound
13231323
pow
13241324
pragma
1325+
pragmas
13251326
pre
13261327
prebuild
13271328
prebuilt

0 commit comments

Comments
 (0)