acd16c6aa7
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 <iinozemtsev@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
171 lines
5.7 KiB
Dart
171 lines
5.7 KiB
Dart
// Copyright (c) 2025, 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:kernel/ast.dart';
|
|
import 'package:kernel/core_types.dart' show CoreTypes;
|
|
import 'package:kernel/target/targets.dart' show TargetFlags;
|
|
import 'package:vm/modular/target/vm.dart' show VmTarget;
|
|
import 'package:vm/transformations/pragma.dart';
|
|
|
|
import 'collector.dart';
|
|
|
|
bool _allowsCall(ParsedEntryPointPragma pragma) =>
|
|
pragma.type == PragmaEntryPointType.Default ||
|
|
pragma.type == PragmaEntryPointType.CallOnly;
|
|
|
|
bool _allowsGet(ParsedEntryPointPragma pragma) =>
|
|
pragma.type == PragmaEntryPointType.Default ||
|
|
pragma.type == PragmaEntryPointType.GetterOnly;
|
|
|
|
bool _allowsSet(ParsedEntryPointPragma pragma) =>
|
|
pragma.type == PragmaEntryPointType.Default ||
|
|
pragma.type == PragmaEntryPointType.SetterOnly;
|
|
|
|
EntryPointShimCollector visitLibrary(
|
|
Component component,
|
|
Library library, {
|
|
CoreTypes? coreTypes,
|
|
bool createUninitializedInstanceMethods = false,
|
|
bool errorOnUnhandledEntryPoints = false,
|
|
}) {
|
|
coreTypes ??= CoreTypes(component);
|
|
final collector = EntryPointShimCollector(
|
|
coreTypes,
|
|
errorOnUnhandledEntryPoints,
|
|
);
|
|
final visitor = EntryPointShimVisitor(coreTypes, library, collector);
|
|
component.accept(visitor);
|
|
return visitor.collector;
|
|
}
|
|
|
|
class EntryPointShimVisitor extends RecursiveVisitor {
|
|
final Library _library;
|
|
final EntryPointShimCollector _collector;
|
|
final bool _createUninitializedInstances;
|
|
final PragmaAnnotationParser _pragmaParser;
|
|
|
|
EntryPointShimVisitor(
|
|
CoreTypes coreTypes,
|
|
this._library,
|
|
this._collector, {
|
|
bool createUninitializedInstanceMethods = false,
|
|
}) : _createUninitializedInstances = createUninitializedInstanceMethods,
|
|
_pragmaParser = ConstantPragmaAnnotationParser(
|
|
coreTypes,
|
|
VmTarget(const TargetFlags()),
|
|
);
|
|
|
|
EntryPointShimCollector get collector => _collector;
|
|
|
|
ParsedEntryPointPragma? _entryPointAnnotation(Annotatable node) {
|
|
if ((node as dynamic).enclosingLibrary != _library) {
|
|
return null;
|
|
}
|
|
|
|
final pragmas = _pragmaParser
|
|
.parsedPragmas<ParsedEntryPointPragma>(node.annotations)
|
|
.where(
|
|
(p) =>
|
|
p.type != PragmaEntryPointType.Extendable &&
|
|
p.type != PragmaEntryPointType.CanBeOverridden,
|
|
);
|
|
if (pragmas.isEmpty) return null;
|
|
var pragma = pragmas.first;
|
|
if (pragma.type != PragmaEntryPointType.Default) {
|
|
for (final p in pragmas.skip(1)) {
|
|
if (p.type == PragmaEntryPointType.Default) {
|
|
pragma = p;
|
|
break;
|
|
}
|
|
if (p.type != pragma.type) {
|
|
throw "Incompatible non-default pragmas: ${pragma.type} and ${p.type}";
|
|
}
|
|
}
|
|
}
|
|
return pragma;
|
|
}
|
|
|
|
@override
|
|
void visitField(Field field) {
|
|
final pragma = _entryPointAnnotation(field);
|
|
if (pragma != null) {
|
|
assert(pragma.type != PragmaEntryPointType.CallOnly);
|
|
assert(!field.isFinal || pragma.type != PragmaEntryPointType.SetterOnly);
|
|
if (_allowsGet(pragma)) {
|
|
_collector.addAll(field.getterReference, {
|
|
EntryPointRole.getter,
|
|
// If the field's value has a Function type, then the value can be
|
|
// both retrieved and invoked using a single Dart_Invoke call.
|
|
if (field.getterType is FunctionType) EntryPointRole.call,
|
|
});
|
|
}
|
|
if (_allowsSet(pragma)) {
|
|
_collector.add(field.setterReference!, EntryPointRole.setter);
|
|
}
|
|
}
|
|
super.visitField(field);
|
|
}
|
|
|
|
@override
|
|
void visitConstructor(Constructor constructor) {
|
|
final pragma = _entryPointAnnotation(constructor);
|
|
if (pragma != null) {
|
|
assert(_allowsCall(pragma));
|
|
_collector.addAll(constructor.reference, {
|
|
// Dart_New
|
|
EntryPointRole.allocation,
|
|
// Dart_InvokeConstructor
|
|
if (_createUninitializedInstances) EntryPointRole.initialization,
|
|
});
|
|
}
|
|
super.visitConstructor(constructor);
|
|
}
|
|
|
|
@override
|
|
void visitProcedure(Procedure procedure) {
|
|
final pragma = _entryPointAnnotation(procedure);
|
|
if (pragma != null) {
|
|
if (procedure.isGetter) {
|
|
assert(_allowsGet(pragma));
|
|
_collector.addAll(procedure.reference, {
|
|
EntryPointRole.getter,
|
|
// If the returned value has a Function type, then the closure value
|
|
// can be both retrieved and invoked using a single Dart_Invoke call.
|
|
if (procedure.getterType is FunctionType) EntryPointRole.call,
|
|
});
|
|
} else if (procedure.isSetter) {
|
|
assert(_allowsSet(pragma));
|
|
// Treat the procedure the same as a field, since both are accessed via
|
|
// Dart_SetField.
|
|
_collector.add(procedure.reference, EntryPointRole.setter);
|
|
} else if (procedure.isFactory) {
|
|
assert(_allowsCall(pragma));
|
|
_collector.add(procedure.reference, EntryPointRole.call);
|
|
} else {
|
|
_collector.addAll(procedure.reference, {
|
|
if (_allowsCall(pragma)) EntryPointRole.call,
|
|
if (_allowsGet(pragma)) EntryPointRole.closure,
|
|
});
|
|
}
|
|
}
|
|
super.visitProcedure(procedure);
|
|
}
|
|
|
|
@override
|
|
void visitClass(Class cls) {
|
|
final pragma = _entryPointAnnotation(cls);
|
|
if (pragma != null) {
|
|
assert(pragma.type == PragmaEntryPointType.Default);
|
|
final canAllocate = !cls.isAbstract && _createUninitializedInstances;
|
|
_collector.addAll(cls.reference, {
|
|
EntryPointRole.class_,
|
|
EntryPointRole.nonNullableType,
|
|
EntryPointRole.nullableType,
|
|
if (canAllocate) EntryPointRole.allocation,
|
|
});
|
|
}
|
|
super.visitClass(cls);
|
|
}
|
|
}
|