Skip to content

Commit acd16c6

Browse files
sstricklCommit Queue
authored andcommitted
[pkg/vm] Add tool for generating DartEngine shims from entry points.
This is an initial cut of a tool for automatically creating shims for C/C++ programs using the entry point annotations in Dart code. The tool has two required arguments: * the .dill file containing the kernel representation of the Dart code * the base path of the header and implementation files to create With a base path of 'dir/name', the header file is created as 'dir/name.h' and the implementation file is created as 'dir/name.cc'. In addition, 'dir/name.h' is used as a basis for creating a #ifndef/#define/#endif header guard around the header contents. The created shims are specific to a single package, either * a user-specified package, provided via '-p'/'--package', or * the package of the main method If the user does not specify a package and there is no main method in the .dill file, the tool fails. Each shim takes the following arguments in order when applicable: * the isolate in which to perform the requested operation, * the instantiated type of the generic class (for invoking, setting, or getting constructors, static methods, and static fields) * the instance (for invoking, getting, or setting instance methods and fields) * the type arguments (for retrieving nullable or non-nullable instantiated types of a generic class) * the arguments (for invoking, setting, or getting constructors, methods, and fields) The generated shims: * cache the package library, types for non-generic classes, and types for generic classes instantiated with default type arguments. The cached persistent handles are cleared if any of the methods are called with a different isolate from the one used to populate the cache. * automatically handle conversions between C int64_t <=> Dart int and C double <=> Dart double. By default, shims are not created for allocation or initializing uninitialized instances. To create such shims, use the '-u' command line argument. Currently, shims are not created for methods that take optional or named arguments. To report an error if a shim cannot be created for any entry points, use the '-e' command line argument. TEST=tests/standalone/embedder_samples_test Change-Id: Ibdf3b52d900ba98038528178485f295c5868ac9d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/410500 Reviewed-by: Ivan Inozemtsev <[email protected]> Commit-Queue: Tess Strickland <[email protected]>
1 parent 4aa7c8b commit acd16c6

File tree

11 files changed

+1758
-138
lines changed

11 files changed

+1758
-138
lines changed

pkg/vm/lib/embedder/collector.dart

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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 'package:kernel/ast.dart';
6+
import 'package:kernel/core_types.dart' show CoreTypes;
7+
8+
import 'shims.dart';
9+
10+
enum EntryPointRole {
11+
// Dart_New (constructors) / Dart_Allocate (classes)
12+
allocation,
13+
// Dart_Invoke (invocable methods as well as invocable (function-valued)
14+
// fields and getters)
15+
call,
16+
// Dart_GetField (non-getter methods)
17+
closure,
18+
// Dart_GetClass (default type arguments for parameterized types)
19+
class_,
20+
// Dart_GetField (getter methods and fields)
21+
getter,
22+
// Dart_InvokeConstructor
23+
initialization,
24+
// Dart_SetField (setter methods and fields)
25+
setter,
26+
// Dart_NonNullableType
27+
nonNullableType,
28+
// Dart_NullableType
29+
nullableType,
30+
}
31+
32+
class EntryPointShimCollector {
33+
final CoreTypes _coreTypes;
34+
final bool _errorOnUnhandledEntryPoints;
35+
final roles = <Reference, Set<EntryPointFunctionShim>>{};
36+
37+
EntryPointShimCollector(this._coreTypes, this._errorOnUnhandledEntryPoints);
38+
39+
bool _hasUnhandledFeatures(NamedNode node, FunctionType type) {
40+
if (type.namedParameters.isNotEmpty) {
41+
if (_errorOnUnhandledEntryPoints) {
42+
throw ArgumentError("$node: named parameters are not handled");
43+
}
44+
return true;
45+
}
46+
if (type.positionalParameters.length !=
47+
type.requiredPositionalParameterCount) {
48+
if (_errorOnUnhandledEntryPoints) {
49+
throw ArgumentError("$node: optional parameters are not handled");
50+
}
51+
return true;
52+
}
53+
if (type.typeParameters.isNotEmpty) {
54+
if (_errorOnUnhandledEntryPoints) {
55+
throw ArgumentError("$node: generic methods are not handled");
56+
}
57+
return true;
58+
}
59+
return false;
60+
}
61+
62+
void add(Reference reference, EntryPointRole role) {
63+
final node = reference.node;
64+
late final EntryPointFunctionShim shim;
65+
switch (role) {
66+
case EntryPointRole.class_:
67+
shim = EntryPointClassShim.fromClass(node as Class, _coreTypes);
68+
break;
69+
case EntryPointRole.nonNullableType:
70+
shim = EntryPointNonNullableTypeShim.fromClass(
71+
node as Class,
72+
_coreTypes,
73+
);
74+
break;
75+
case EntryPointRole.nullableType:
76+
shim = EntryPointNullableTypeShim.fromClass(node as Class, _coreTypes);
77+
break;
78+
case EntryPointRole.allocation:
79+
if (node is Constructor) {
80+
if (_hasUnhandledFeatures(
81+
node,
82+
EntryPointNewShim.functionType(node),
83+
)) {
84+
return;
85+
}
86+
shim = EntryPointNewShim.fromConstructor(node, _coreTypes);
87+
} else {
88+
shim = EntryPointAllocationShim.fromClass(node as Class, _coreTypes);
89+
}
90+
break;
91+
case EntryPointRole.call:
92+
final member = node as Member;
93+
if (_hasUnhandledFeatures(
94+
member,
95+
EntryPointCallShim.functionType(member),
96+
)) {
97+
return;
98+
}
99+
shim = EntryPointCallShim.fromMember(member, _coreTypes);
100+
break;
101+
case EntryPointRole.closure:
102+
shim = EntryPointClosureShim.fromProcedure(
103+
node as Procedure,
104+
_coreTypes,
105+
);
106+
break;
107+
case EntryPointRole.getter:
108+
shim = EntryPointGetterShim.fromMember(node as Member, _coreTypes);
109+
break;
110+
case EntryPointRole.initialization:
111+
final c = node as Constructor;
112+
if (_hasUnhandledFeatures(
113+
c,
114+
EntryPointInitializationShim.functionType(c),
115+
)) {
116+
return;
117+
}
118+
shim = EntryPointInitializationShim.fromConstructor(c, _coreTypes);
119+
break;
120+
case EntryPointRole.setter:
121+
shim = EntryPointSetterShim.fromMember(node as Member, _coreTypes);
122+
break;
123+
}
124+
roles.putIfAbsent(reference, () => {}).add(shim);
125+
}
126+
127+
void addAll(Reference reference, Iterable<EntryPointRole> it) =>
128+
it.forEach((role) => add(reference, role));
129+
130+
Iterable<Reference> get keys => roles.keys;
131+
Iterable<MapEntry<Reference, Set<EntryPointFunctionShim>>> get entries =>
132+
roles.entries;
133+
Set<EntryPointFunctionShim>? operator [](Reference reference) =>
134+
roles[reference];
135+
bool containsKey(Reference reference) => roles.containsKey(reference);
136+
}

0 commit comments

Comments
 (0)