mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:09:49 +00:00
[vm/ffi] Support FFI in AOT (excluding callbacks).
Move generation of Function objects for native trampolines to the Precompiler, so they can be generated during AOT and tree-shaken if possible. Issue dartbug.com/35765 Change-Id: I0e69b7e0b22db73e3a40f2fe445660e57ddb6fa9 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-bare-linux-release-simarm64-try, vm-kernel-precomp-bare-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-dartkb-linux-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107407 Commit-Queue: Samir Jindel <sjindel@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
f0d6f119de
commit
fcc72ad83f
|
@ -76,20 +76,21 @@ class Command {
|
|||
}
|
||||
|
||||
static Command adbPrecompiled(
|
||||
String precompiledRunner,
|
||||
String buildPath,
|
||||
String processTest,
|
||||
String testDirectory,
|
||||
List<String> arguments,
|
||||
bool useBlobs,
|
||||
bool useElf) {
|
||||
return AdbPrecompilationCommand._(precompiledRunner, processTest,
|
||||
testDirectory, arguments, useBlobs, useElf);
|
||||
bool useElf,
|
||||
List<String> extraLibs) {
|
||||
return AdbPrecompilationCommand._(buildPath, processTest, testDirectory,
|
||||
arguments, useBlobs, useElf, extraLibs);
|
||||
}
|
||||
|
||||
static Command adbDartk(String precompiledRunner, String processTest,
|
||||
String script, List<String> arguments, List<String> extraLibraries) {
|
||||
static Command adbDartk(String buildPath, String processTest, String script,
|
||||
List<String> arguments, List<String> extraLibraries) {
|
||||
return AdbDartkCommand._(
|
||||
precompiledRunner, processTest, script, arguments, extraLibraries);
|
||||
buildPath, processTest, script, arguments, extraLibraries);
|
||||
}
|
||||
|
||||
static Command jsCommandLine(
|
||||
|
@ -587,54 +588,72 @@ class VmBatchCommand extends ProcessCommand implements VmCommand {
|
|||
}
|
||||
}
|
||||
|
||||
class AdbPrecompilationCommand extends Command {
|
||||
final String precompiledRunnerFilename;
|
||||
abstract class AdbCommand {
|
||||
String get buildPath;
|
||||
List<String> get extraLibraries;
|
||||
}
|
||||
|
||||
class AdbPrecompilationCommand extends Command implements AdbCommand {
|
||||
final String buildPath; // Path to the output directory of the build.
|
||||
final String processTestFilename;
|
||||
final String precompiledTestDirectory;
|
||||
final List<String> arguments;
|
||||
final bool useBlobs;
|
||||
final bool useElf;
|
||||
final List<String> extraLibraries;
|
||||
|
||||
AdbPrecompilationCommand._(
|
||||
this.precompiledRunnerFilename,
|
||||
this.buildPath,
|
||||
this.processTestFilename,
|
||||
this.precompiledTestDirectory,
|
||||
this.arguments,
|
||||
this.useBlobs,
|
||||
this.useElf,
|
||||
this.extraLibraries,
|
||||
{int index = 0})
|
||||
: super._("adb_precompilation", index: index);
|
||||
|
||||
AdbPrecompilationCommand indexedCopy(int index) => AdbPrecompilationCommand._(
|
||||
precompiledRunnerFilename,
|
||||
buildPath,
|
||||
processTestFilename,
|
||||
precompiledTestDirectory,
|
||||
arguments,
|
||||
useBlobs,
|
||||
useElf,
|
||||
extraLibraries,
|
||||
index: index);
|
||||
_buildHashCode(HashCodeBuilder builder) {
|
||||
super._buildHashCode(builder);
|
||||
builder.add(precompiledRunnerFilename);
|
||||
builder.add(buildPath);
|
||||
builder.add(precompiledTestDirectory);
|
||||
builder.add(arguments);
|
||||
builder.add(useBlobs);
|
||||
builder.add(useElf);
|
||||
extraLibraries.forEach(builder.add);
|
||||
}
|
||||
|
||||
static bool _listEquals(List<String> x, List<String> y) {
|
||||
if (x.length != y.length) return false;
|
||||
for (int i = 0; i < x.length; ++i) {
|
||||
if (x[i] != y[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _equal(AdbPrecompilationCommand other) =>
|
||||
super._equal(other) &&
|
||||
precompiledRunnerFilename == other.precompiledRunnerFilename &&
|
||||
buildPath == other.buildPath &&
|
||||
useBlobs == other.useBlobs &&
|
||||
useElf == other.useElf &&
|
||||
arguments == other.arguments &&
|
||||
precompiledTestDirectory == other.precompiledTestDirectory;
|
||||
precompiledTestDirectory == other.precompiledTestDirectory &&
|
||||
_listEquals(extraLibraries, other.extraLibraries);
|
||||
|
||||
String toString() => 'Steps to push precompiled runner and precompiled code '
|
||||
'to an attached device. Uses (and requires) adb.';
|
||||
}
|
||||
|
||||
class AdbDartkCommand extends Command {
|
||||
class AdbDartkCommand extends Command implements AdbCommand {
|
||||
final String buildPath;
|
||||
final String processTestFilename;
|
||||
final String kernelFile;
|
||||
|
|
|
@ -629,9 +629,23 @@ class CommandExecutorImpl implements CommandExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
List<_StepFunction> _pushLibraries(AdbCommand command, AdbDevice device,
|
||||
String deviceDir, String deviceTestDir) {
|
||||
final List<_StepFunction> steps = [];
|
||||
for (var lib in command.extraLibraries) {
|
||||
var libName = "lib${lib}.so";
|
||||
steps.add(() => device.runAdbCommand([
|
||||
'push',
|
||||
'${command.buildPath}/$libName',
|
||||
'$deviceTestDir/$libName'
|
||||
]));
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
||||
Future<CommandOutput> _runAdbPrecompilationCommand(
|
||||
AdbDevice device, AdbPrecompilationCommand command, int timeout) async {
|
||||
var runner = command.precompiledRunnerFilename;
|
||||
final String buildPath = command.buildPath;
|
||||
var processTest = command.processTestFilename;
|
||||
var testdir = command.precompiledTestDirectory;
|
||||
var arguments = command.arguments;
|
||||
|
@ -652,8 +666,8 @@ class CommandExecutorImpl implements CommandExecutor {
|
|||
|
||||
steps.add(() => device.runAdbShellCommand(['rm', '-Rf', deviceTestDir]));
|
||||
steps.add(() => device.runAdbShellCommand(['mkdir', '-p', deviceTestDir]));
|
||||
steps.add(() =>
|
||||
device.pushCachedData(runner, '$devicedir/dart_precompiled_runtime'));
|
||||
steps.add(() => device.pushCachedData('$buildPath/dart_precompiled_runtime',
|
||||
'$devicedir/dart_precompiled_runtime'));
|
||||
steps.add(
|
||||
() => device.pushCachedData(processTest, '$devicedir/process_test'));
|
||||
steps.add(() => device.runAdbShellCommand([
|
||||
|
@ -662,6 +676,8 @@ class CommandExecutorImpl implements CommandExecutor {
|
|||
'$devicedir/dart_precompiled_runtime $devicedir/process_test'
|
||||
]));
|
||||
|
||||
steps.addAll(_pushLibraries(command, device, devicedir, deviceTestDir));
|
||||
|
||||
for (var file in files) {
|
||||
steps.add(() => device
|
||||
.runAdbCommand(['push', '$testdir/$file', '$deviceTestDir/$file']));
|
||||
|
@ -669,7 +685,8 @@ class CommandExecutorImpl implements CommandExecutor {
|
|||
|
||||
steps.add(() => device.runAdbShellCommand(
|
||||
[
|
||||
'$devicedir/dart_precompiled_runtime',
|
||||
'export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$deviceTestDir;'
|
||||
'$devicedir/dart_precompiled_runtime',
|
||||
]..addAll(arguments),
|
||||
timeout: timeoutDuration));
|
||||
|
||||
|
@ -723,11 +740,7 @@ class CommandExecutorImpl implements CommandExecutor {
|
|||
steps.add(() => device
|
||||
.runAdbCommand(['push', hostKernelFile, '$deviceTestDir/out.dill']));
|
||||
|
||||
for (var lib in command.extraLibraries) {
|
||||
var libName = "lib${lib}.so";
|
||||
steps.add(() => device.runAdbCommand(
|
||||
['push', '${buildPath}/$libName', '$deviceTestDir/$libName']));
|
||||
}
|
||||
steps.addAll(_pushLibraries(command, device, devicedir, deviceTestDir));
|
||||
|
||||
steps.add(() => device.runAdbShellCommand(
|
||||
[
|
||||
|
|
|
@ -369,11 +369,10 @@ class DartPrecompiledAdbRuntimeConfiguration
|
|||
throw "dart_precompiled cannot run files of type '$type'.";
|
||||
}
|
||||
|
||||
String precompiledRunner = dartPrecompiledBinaryFileName;
|
||||
String processTest = processTestBinaryFileName;
|
||||
return [
|
||||
Command.adbPrecompiled(
|
||||
precompiledRunner, processTest, script, arguments, useBlobs, useElf)
|
||||
buildDir, processTest, script, arguments, useBlobs, useElf, extraLibs)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ const List<int> nativeTypeSizes = [
|
|||
/// _FfiUseSiteTransformer and _FfiDefinitionTransformer.
|
||||
class FfiTransformer extends Transformer {
|
||||
final TypeEnvironment env;
|
||||
final CoreTypes coreTypes;
|
||||
final LibraryIndex index;
|
||||
final ClassHierarchy hierarchy;
|
||||
final DiagnosticReporter diagnosticReporter;
|
||||
|
@ -107,16 +108,18 @@ class FfiTransformer extends Transformer {
|
|||
final Procedure storeMethod;
|
||||
final Procedure offsetByMethod;
|
||||
final Procedure asFunctionMethod;
|
||||
final Procedure asFunctionInternal;
|
||||
final Procedure lookupFunctionMethod;
|
||||
final Procedure fromFunctionMethod;
|
||||
final Field addressOfField;
|
||||
final Constructor structFromPointer;
|
||||
final Procedure libraryLookupMethod;
|
||||
|
||||
/// Classes corresponding to [NativeType], indexed by [NativeType].
|
||||
final List<Class> nativeTypesClasses;
|
||||
|
||||
FfiTransformer(
|
||||
this.index, CoreTypes coreTypes, this.hierarchy, this.diagnosticReporter)
|
||||
this.index, this.coreTypes, this.hierarchy, this.diagnosticReporter)
|
||||
: env = new TypeEnvironment(coreTypes, hierarchy),
|
||||
intClass = coreTypes.intClass,
|
||||
doubleClass = coreTypes.doubleClass,
|
||||
|
@ -133,10 +136,14 @@ class FfiTransformer extends Transformer {
|
|||
structFromPointer =
|
||||
index.getMember('dart:ffi', 'Struct', 'fromPointer'),
|
||||
asFunctionMethod = index.getMember('dart:ffi', 'Pointer', 'asFunction'),
|
||||
asFunctionInternal =
|
||||
index.getTopLevelMember('dart:ffi', '_asFunctionInternal'),
|
||||
lookupFunctionMethod =
|
||||
index.getMember('dart:ffi', 'DynamicLibrary', 'lookupFunction'),
|
||||
fromFunctionMethod =
|
||||
index.getMember('dart:ffi', 'Pointer', 'fromFunction'),
|
||||
libraryLookupMethod =
|
||||
index.getMember('dart:ffi', 'DynamicLibrary', 'lookup'),
|
||||
nativeTypesClasses = nativeTypeClassNames
|
||||
.map((name) => index.getClass('dart:ffi', name))
|
||||
.toList();
|
||||
|
|
|
@ -144,12 +144,48 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
|||
return node;
|
||||
}
|
||||
|
||||
// We need to replace calls to 'DynamicLibrary.lookupFunction' with explicit
|
||||
// Kernel, because we cannot have a generic call to 'asFunction' in its body.
|
||||
//
|
||||
// Below, in 'visitMethodInvocation', we ensure that the type arguments to
|
||||
// 'lookupFunction' are constants, so by inlining the call to 'asFunction' at
|
||||
// the call-site, we ensure that there are no generic calls to 'asFunction'.
|
||||
//
|
||||
// We will not detect dynamic invocations of 'asFunction' -- these are handled
|
||||
// by the stub in 'dynamic_library_patch.dart'. Dynamic invocations of
|
||||
// 'lookupFunction' (and 'asFunction') are not legal and throw a runtime
|
||||
// exception.
|
||||
Expression _replaceLookupFunction(MethodInvocation node) {
|
||||
// The generated code looks like:
|
||||
//
|
||||
// _asFunctionInternal<DS, NS>(lookup<NativeFunction<NS>>(symbolName))
|
||||
|
||||
final DartType nativeSignature = node.arguments.types[0];
|
||||
final DartType dartSignature = node.arguments.types[1];
|
||||
|
||||
final Arguments args = Arguments([
|
||||
node.arguments.positional.single
|
||||
], types: [
|
||||
InterfaceType(nativeFunctionClass, [nativeSignature])
|
||||
]);
|
||||
|
||||
final Expression lookupResult = MethodInvocation(
|
||||
node.receiver, Name("lookup"), args, libraryLookupMethod);
|
||||
|
||||
return StaticInvocation(asFunctionInternal,
|
||||
Arguments([lookupResult], types: [dartSignature, nativeSignature]));
|
||||
}
|
||||
|
||||
@override
|
||||
visitMethodInvocation(MethodInvocation node) {
|
||||
super.visitMethodInvocation(node);
|
||||
|
||||
Member target = node.interfaceTarget;
|
||||
try {
|
||||
// We will not detect dynamic invocations of 'asFunction' and
|
||||
// 'lookupFunction' -- these are handled by the 'asFunctionInternal' stub
|
||||
// in 'dynamic_library_patch.dart'. Dynamic invocations of 'asFunction'
|
||||
// and 'lookupFunction' are not legal and throw a runtime exception.
|
||||
if (target == lookupFunctionMethod) {
|
||||
DartType nativeType =
|
||||
InterfaceType(nativeFunctionClass, [node.arguments.types[0]]);
|
||||
|
@ -157,14 +193,8 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
|||
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
return _replaceLookupFunction(node);
|
||||
} else if (target == asFunctionMethod) {
|
||||
if (isFfiLibrary) {
|
||||
// Library code of dart:ffi uses asFunction to implement
|
||||
// lookupFunction. Since we treat lookupFunction as well, this call
|
||||
// can be generic and still support AOT.
|
||||
return node;
|
||||
}
|
||||
|
||||
DartType dartType = node.arguments.types[0];
|
||||
DartType pointerType = node.receiver.getStaticType(env);
|
||||
DartType nativeType = _pointerTypeGetTypeArg(pointerType);
|
||||
|
@ -172,6 +202,11 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
|||
_ensureNativeTypeValid(pointerType, node);
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
|
||||
final DartType nativeSignature =
|
||||
(nativeType as InterfaceType).typeArguments[0];
|
||||
return StaticInvocation(asFunctionInternal,
|
||||
Arguments([node.receiver], types: [dartType, nativeSignature]));
|
||||
} else if (target == loadMethod) {
|
||||
// TODO(dacoharkes): should load and store be generic?
|
||||
// https://github.com/dart-lang/sdk/issues/35902
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "vm/compiler/ffi.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
#include "vm/exceptions.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/log.h"
|
||||
#include "vm/native_arguments.h"
|
||||
#include "vm/native_entry.h"
|
||||
|
@ -31,11 +32,6 @@ static bool IsPointerType(const AbstractType& type) {
|
|||
return RawObject::IsFfiPointerClassId(type.type_class_id());
|
||||
}
|
||||
|
||||
static bool IsNativeFunction(const AbstractType& type_arg) {
|
||||
classid_t type_cid = type_arg.type_class_id();
|
||||
return RawObject::IsFfiTypeNativeFunctionClassId(type_cid);
|
||||
}
|
||||
|
||||
static void CheckSized(const AbstractType& type_arg) {
|
||||
const classid_t type_cid = type_arg.type_class_id();
|
||||
if (RawObject::IsFfiNativeTypeTypeClassId(type_cid) ||
|
||||
|
@ -52,7 +48,7 @@ static void CheckSized(const AbstractType& type_arg) {
|
|||
}
|
||||
}
|
||||
|
||||
enum class FfiVariance { kInvariant = 0, kCovariant = 1, kContravariant = 2 };
|
||||
enum class FfiVariance { kCovariant = 0, kContravariant = 1 };
|
||||
|
||||
// Checks that a dart type correspond to a [NativeType].
|
||||
// Because this is checked already in a kernel transformation, it does not throw
|
||||
|
@ -86,9 +82,7 @@ static bool DartAndCTypeCorrespond(const AbstractType& native_type,
|
|||
Heap::kNew);
|
||||
}
|
||||
if (RawObject::IsFfiPointerClassId(native_type_cid)) {
|
||||
return (variance == FfiVariance::kInvariant &&
|
||||
dart_type.Equals(native_type)) ||
|
||||
(variance == FfiVariance::kCovariant &&
|
||||
return (variance == FfiVariance::kCovariant &&
|
||||
dart_type.IsSubtypeOf(native_type, Heap::kNew)) ||
|
||||
(variance == FfiVariance::kContravariant &&
|
||||
native_type.IsSubtypeOf(dart_type, Heap::kNew)) ||
|
||||
|
@ -105,7 +99,8 @@ static bool DartAndCTypeCorrespond(const AbstractType& native_type,
|
|||
if (!nativefunction_type_arg.IsFunctionType()) {
|
||||
return false;
|
||||
}
|
||||
Function& dart_function = Function::Handle(((Type&)dart_type).signature());
|
||||
Function& dart_function =
|
||||
Function::Handle((Type::Cast(dart_type)).signature());
|
||||
if (dart_function.NumTypeParameters() != 0 ||
|
||||
dart_function.HasOptionalPositionalParameters() ||
|
||||
dart_function.HasOptionalNamedParameters()) {
|
||||
|
@ -434,91 +429,12 @@ DEFINE_NATIVE_ENTRY(Ffi_sizeOf, 1, 0) {
|
|||
return Integer::New(SizeOf(type_arg));
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): Cache the trampolines.
|
||||
// We can possibly address simultaniously with 'precaching' in AOT.
|
||||
static RawFunction* TrampolineFunction(const Function& dart_signature,
|
||||
const Function& c_signature) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
String& name =
|
||||
String::ZoneHandle(Symbols::New(Thread::Current(), "FfiTrampoline"));
|
||||
const Library& lib = Library::Handle(Library::FfiLibrary());
|
||||
const Class& owner_class = Class::Handle(lib.toplevel_class());
|
||||
Function& function =
|
||||
Function::Handle(zone, Function::New(name, RawFunction::kFfiTrampoline,
|
||||
/*is_static=*/true,
|
||||
/*is_const=*/false,
|
||||
/*is_abstract=*/false,
|
||||
/*is_external=*/false,
|
||||
/*is_native=*/false, owner_class,
|
||||
TokenPosition::kMinSource));
|
||||
function.set_is_debuggable(false);
|
||||
function.set_num_fixed_parameters(dart_signature.num_fixed_parameters());
|
||||
function.set_result_type(AbstractType::Handle(dart_signature.result_type()));
|
||||
function.set_parameter_types(Array::Handle(dart_signature.parameter_types()));
|
||||
|
||||
// The signature function won't have any names for the parameters. We need to
|
||||
// assign unique names for scope building and error messages.
|
||||
const intptr_t num_params = dart_signature.num_fixed_parameters();
|
||||
const Array& parameter_names = Array::Handle(Array::New(num_params));
|
||||
for (intptr_t i = 0; i < num_params; ++i) {
|
||||
if (i == 0) {
|
||||
name = Symbols::ClosureParameter().raw();
|
||||
} else {
|
||||
name = Symbols::NewFormatted(thread, ":ffiParam%" Pd, i);
|
||||
}
|
||||
parameter_names.SetAt(i, name);
|
||||
}
|
||||
function.set_parameter_names(parameter_names);
|
||||
function.SetFfiCSignature(c_signature);
|
||||
|
||||
return function.raw();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_asFunction, 1, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
AbstractType& pointer_type_arg =
|
||||
AbstractType::Handle(pointer.type_argument());
|
||||
ASSERT(IsNativeFunction(pointer_type_arg));
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
CheckDartAndCTypeCorrespond(pointer_type_arg, type_arg,
|
||||
FfiVariance::kInvariant);
|
||||
|
||||
Function& dart_signature = Function::Handle(Type::Cast(type_arg).signature());
|
||||
TypeArguments& nativefunction_type_args =
|
||||
TypeArguments::Handle(pointer_type_arg.arguments());
|
||||
AbstractType& nativefunction_type_arg =
|
||||
AbstractType::Handle(nativefunction_type_args.TypeAt(0));
|
||||
Function& c_signature =
|
||||
Function::Handle(Type::Cast(nativefunction_type_arg).signature());
|
||||
Function& function =
|
||||
Function::Handle(TrampolineFunction(dart_signature, c_signature));
|
||||
|
||||
// Set the c function pointer in the context of the closure rather than in
|
||||
// the function so that we can reuse the function for each c function with
|
||||
// the same signature.
|
||||
Context& context = Context::Handle(Context::New(1));
|
||||
context.SetAt(0,
|
||||
Integer::Handle(zone, Integer::New(pointer.NativeAddress())));
|
||||
|
||||
RawClosure* raw_closure =
|
||||
Closure::New(Object::null_type_arguments(), Object::null_type_arguments(),
|
||||
function, context, Heap::kOld);
|
||||
|
||||
return raw_closure;
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER) && \
|
||||
!defined(TARGET_ARCH_DBC)
|
||||
// Generates assembly to trampoline from native code into Dart.
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER)
|
||||
static uword CompileNativeCallback(const Function& c_signature,
|
||||
const Function& dart_target,
|
||||
const Instance& exceptional_return) {
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
// https://github.com/dart-lang/sdk/issues/35774
|
||||
// FFI is supported, but callbacks are not.
|
||||
Exceptions::ThrowUnsupportedError(
|
||||
"FFI callbacks are not yet supported on DBC.");
|
||||
#else
|
||||
Thread* const thread = Thread::Current();
|
||||
const int32_t callback_id = thread->AllocateFfiCallbackId();
|
||||
|
||||
|
@ -526,10 +442,9 @@ static uword CompileNativeCallback(const Function& c_signature,
|
|||
// library. Note that these functions will never be invoked by Dart, so it
|
||||
// doesn't matter that they all have the same name.
|
||||
Zone* const Z = thread->zone();
|
||||
const String& name =
|
||||
String::ZoneHandle(Symbols::New(Thread::Current(), "FfiCallback"));
|
||||
const Library& lib = Library::Handle(Library::FfiLibrary());
|
||||
const Class& owner_class = Class::Handle(lib.toplevel_class());
|
||||
const String& name = String::Handle(Symbols::New(thread, "FfiCallback"));
|
||||
const Library& lib = Library::Handle(Z, Library::FfiLibrary());
|
||||
const Class& owner_class = Class::Handle(Z, lib.toplevel_class());
|
||||
const Function& function =
|
||||
Function::Handle(Z, Function::New(name, RawFunction::kFfiTrampoline,
|
||||
/*is_static=*/true,
|
||||
|
@ -587,13 +502,49 @@ static uword CompileNativeCallback(const Function& c_signature,
|
|||
thread->SetFfiCallbackCode(callback_id, code);
|
||||
|
||||
return code.EntryPoint();
|
||||
#endif // defined(TARGET_ARCH_DBC)
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER)
|
||||
#endif
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_fromFunction, 1, 2) {
|
||||
// Static invocations to this method are translated directly in streaming FGB
|
||||
// and bytecode FGB. However, we can still reach this entrypoint in the bytecode
|
||||
// interpreter.
|
||||
DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 1) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
ASSERT(FLAG_enable_interpreter);
|
||||
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
GET_NATIVE_TYPE_ARGUMENT(dart_type, arguments->NativeTypeArgAt(0));
|
||||
GET_NATIVE_TYPE_ARGUMENT(native_type, arguments->NativeTypeArgAt(1));
|
||||
|
||||
const Function& dart_signature =
|
||||
Function::Handle(zone, Type::Cast(dart_type).signature());
|
||||
const Function& native_signature =
|
||||
Function::Handle(zone, Type::Cast(native_type).signature());
|
||||
const Function& function = Function::Handle(
|
||||
compiler::ffi::TrampolineFunction(dart_signature, native_signature));
|
||||
|
||||
// Set the c function pointer in the context of the closure rather than in
|
||||
// the function so that we can reuse the function for each c function with
|
||||
// the same signature.
|
||||
const Context& context = Context::Handle(Context::New(1));
|
||||
context.SetAt(0,
|
||||
Integer::Handle(zone, Integer::New(pointer.NativeAddress())));
|
||||
|
||||
return Closure::New(Object::null_type_arguments(),
|
||||
Object::null_type_arguments(), function, context,
|
||||
Heap::kOld);
|
||||
#endif
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_fromFunction, 1, 2) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER) || \
|
||||
defined(TARGET_ARCH_DBC)
|
||||
// https://github.com/dart-lang/sdk/issues/37295
|
||||
// FFI is supported, but callbacks are not.
|
||||
Exceptions::ThrowUnsupportedError(
|
||||
"FFI callbacks are not yet supported in AOT or on DBC.");
|
||||
#else
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Closure, closure, arguments->NativeArgAt(0));
|
||||
|
|
|
@ -18,6 +18,15 @@ class DynamicLibrary {
|
|||
Pointer<T> lookup<T extends NativeType>(String symbolName)
|
||||
native "Ffi_dl_lookup";
|
||||
|
||||
// The real implementation of this function lives in FfiUseSitesTransformer
|
||||
// for interface calls. Only dynamic calls (which are illegal) reach this
|
||||
// implementation.
|
||||
@patch
|
||||
F lookupFunction<T extends Function, F extends Function>(String symbolName) {
|
||||
throw UnsupportedError(
|
||||
"Dynamic invocation of lookupFunction is not supported.");
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): Expose this to users, or extend Pointer?
|
||||
// https://github.com/dart-lang/sdk/issues/35881
|
||||
int getHandle() native "Ffi_dl_getHandle";
|
||||
|
|
|
@ -11,6 +11,12 @@ Pointer<T> _allocate<T extends NativeType>(int count) native "Ffi_allocate";
|
|||
|
||||
Pointer<T> _fromAddress<T extends NativeType>(int ptr) native "Ffi_fromAddress";
|
||||
|
||||
// The real implementation of this function (for interface calls) lives in
|
||||
// BuildFfiAsFunctionCall in the Kernel frontend. No calls can actually reach
|
||||
// this function.
|
||||
DS _asFunctionInternal<DS extends Function, NS extends Function>(
|
||||
Pointer<NativeFunction<NS>> ptr) native "Ffi_asFunctionInternal";
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Pointer<T extends NativeType> {
|
||||
|
@ -55,7 +61,9 @@ class Pointer<T extends NativeType> {
|
|||
Pointer<U> cast<U extends NativeType>() native "Ffi_cast";
|
||||
|
||||
@patch
|
||||
R asFunction<R extends Function>() native "Ffi_asFunction";
|
||||
R asFunction<R extends Function>() {
|
||||
throw UnsupportedError("Pointer.asFunction cannot be called dynamically.");
|
||||
}
|
||||
|
||||
@patch
|
||||
void free() native "Ffi_free";
|
||||
|
|
|
@ -383,7 +383,7 @@ namespace dart {
|
|||
V(Ffi_offsetBy, 2) \
|
||||
V(Ffi_cast, 1) \
|
||||
V(Ffi_sizeOf, 0) \
|
||||
V(Ffi_asFunction, 1) \
|
||||
V(Ffi_asFunctionInternal, 1) \
|
||||
V(Ffi_fromFunction, 2) \
|
||||
V(Ffi_dl_open, 1) \
|
||||
V(Ffi_dl_lookup, 2) \
|
||||
|
|
|
@ -5109,9 +5109,6 @@ class AllocateObjectInstr : public TemplateAllocation<0, NoThrow> {
|
|||
DISALLOW_COPY_AND_ASSIGN(AllocateObjectInstr);
|
||||
};
|
||||
|
||||
// TODO(vegorov) the name of the instruction is confusing. At some point
|
||||
// it used to allocate uninitialized storage, but this is no longer true.
|
||||
// These days it allocates null initialized storage.
|
||||
class AllocateUninitializedContextInstr
|
||||
: public TemplateAllocation<0, NoThrow> {
|
||||
public:
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
#include "vm/compiler/backend/locations.h"
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
#include "vm/growable_array.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/stack_frame.h"
|
||||
#include "vm/symbols.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
|
@ -427,6 +429,45 @@ Location ResultLocation(Representation result_rep) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// TODO(36607): Cache the trampolines.
|
||||
RawFunction* TrampolineFunction(const Function& dart_signature,
|
||||
const Function& c_signature) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
String& name = String::Handle(zone, Symbols::New(thread, "FfiTrampoline"));
|
||||
const Library& lib = Library::Handle(zone, Library::FfiLibrary());
|
||||
const Class& owner_class = Class::Handle(zone, lib.toplevel_class());
|
||||
Function& function =
|
||||
Function::Handle(zone, Function::New(name, RawFunction::kFfiTrampoline,
|
||||
/*is_static=*/true,
|
||||
/*is_const=*/false,
|
||||
/*is_abstract=*/false,
|
||||
/*is_external=*/false,
|
||||
/*is_native=*/false, owner_class,
|
||||
TokenPosition::kMinSource));
|
||||
function.set_is_debuggable(false);
|
||||
function.set_num_fixed_parameters(dart_signature.num_fixed_parameters());
|
||||
function.set_result_type(AbstractType::Handle(dart_signature.result_type()));
|
||||
function.set_parameter_types(Array::Handle(dart_signature.parameter_types()));
|
||||
|
||||
// The signature function won't have any names for the parameters. We need to
|
||||
// assign unique names for scope building and error messages.
|
||||
const intptr_t num_params = dart_signature.num_fixed_parameters();
|
||||
const Array& parameter_names = Array::Handle(Array::New(num_params));
|
||||
for (intptr_t i = 0; i < num_params; ++i) {
|
||||
if (i == 0) {
|
||||
name = Symbols::ClosureParameter().raw();
|
||||
} else {
|
||||
name = Symbols::NewFormatted(thread, ":ffi_param%" Pd, i);
|
||||
}
|
||||
parameter_names.SetAt(i, name);
|
||||
}
|
||||
function.set_parameter_names(parameter_names);
|
||||
function.SetFfiCSignature(c_signature);
|
||||
|
||||
return function.raw();
|
||||
}
|
||||
|
||||
// Accounts for alignment, where some stack slots are used as padding.
|
||||
template <class Location>
|
||||
intptr_t TemplateNumStackSlots(const ZoneGrowableArray<Location>& locations) {
|
||||
|
@ -532,6 +573,27 @@ Representation FfiSignatureDescriptor::ResultRepresentation() const {
|
|||
|
||||
#endif // defined(TARGET_ARCH_DBC)
|
||||
|
||||
bool IsAsFunctionInternal(Zone* zone, Isolate* isolate, const Function& func) {
|
||||
Object& asFunctionInternal =
|
||||
Object::Handle(zone, isolate->object_store()->ffi_as_function_internal());
|
||||
if (asFunctionInternal.raw() == Object::null()) {
|
||||
// Cache the reference.
|
||||
Library& ffi =
|
||||
Library::Handle(zone, isolate->object_store()->ffi_library());
|
||||
asFunctionInternal =
|
||||
ffi.LookupFunctionAllowPrivate(Symbols::AsFunctionInternal());
|
||||
// Cannot assert that 'asFunctionInternal' is found because it may have been
|
||||
// tree-shaken.
|
||||
if (asFunctionInternal.IsNull()) {
|
||||
// Set the entry in the object store to a sentinel so we don't try to look
|
||||
// it up again.
|
||||
asFunctionInternal = Object::sentinel().raw();
|
||||
}
|
||||
isolate->object_store()->set_ffi_as_function_internal(asFunctionInternal);
|
||||
}
|
||||
return func.raw() == asFunctionInternal.raw();
|
||||
}
|
||||
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
} // namespace ffi
|
||||
|
|
|
@ -41,6 +41,9 @@ bool NativeTypeIsVoid(const AbstractType& result_type);
|
|||
// Location for the result of a C signature function.
|
||||
Location ResultLocation(Representation result_rep);
|
||||
|
||||
RawFunction* TrampolineFunction(const Function& dart_signature,
|
||||
const Function& c_signature);
|
||||
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
|
||||
// Unboxed representations of the arguments to a C signature function.
|
||||
|
@ -133,6 +136,8 @@ class CallbackArgumentTranslator : public ValueObject {
|
|||
intptr_t argument_slots_required_ = 0;
|
||||
};
|
||||
|
||||
bool IsAsFunctionInternal(Zone* zone, Isolate* isolate, const Function& func);
|
||||
|
||||
} // namespace ffi
|
||||
|
||||
} // namespace compiler
|
||||
|
|
|
@ -601,9 +601,14 @@ void BaseFlowGraphBuilder::Push(Definition* definition) {
|
|||
Value::AddToList(new (Z) Value(definition), &stack_);
|
||||
}
|
||||
|
||||
Definition* BaseFlowGraphBuilder::Peek() {
|
||||
ASSERT(stack_ != NULL);
|
||||
return stack_->definition();
|
||||
Definition* BaseFlowGraphBuilder::Peek(intptr_t depth) {
|
||||
Value* head = stack_;
|
||||
for (intptr_t i = 0; i < depth; ++i) {
|
||||
ASSERT(head != nullptr);
|
||||
head = head->next_use();
|
||||
}
|
||||
ASSERT(head != nullptr);
|
||||
return head->definition();
|
||||
}
|
||||
|
||||
Value* BaseFlowGraphBuilder::Pop() {
|
||||
|
@ -813,6 +818,62 @@ Fragment BaseFlowGraphBuilder::LoadClassId() {
|
|||
return Fragment(load);
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::AllocateObject(TokenPosition position,
|
||||
const Class& klass,
|
||||
intptr_t argument_count) {
|
||||
ArgumentArray arguments = GetArguments(argument_count);
|
||||
AllocateObjectInstr* allocate =
|
||||
new (Z) AllocateObjectInstr(position, klass, arguments);
|
||||
Push(allocate);
|
||||
return Fragment(allocate);
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall(
|
||||
const TypeArguments& signatures) {
|
||||
ASSERT(signatures.IsInstantiated() && signatures.Length() == 2);
|
||||
|
||||
const AbstractType& dart_type = AbstractType::Handle(signatures.TypeAt(0));
|
||||
const AbstractType& native_type = AbstractType::Handle(signatures.TypeAt(1));
|
||||
|
||||
ASSERT(dart_type.IsFunctionType() && native_type.IsFunctionType());
|
||||
const Function& target =
|
||||
Function::ZoneHandle(compiler::ffi::TrampolineFunction(
|
||||
Function::Handle(Z, Type::Cast(dart_type).signature()),
|
||||
Function::Handle(Z, Type::Cast(native_type).signature())));
|
||||
|
||||
Fragment code;
|
||||
code += LoadNativeField(Slot::Pointer_c_memory_address());
|
||||
LocalVariable* address = MakeTemporary();
|
||||
|
||||
auto& context_variables = CompilerState::Current().GetDummyContextVariables(
|
||||
/*context_id=*/0, /*num_variables=*/1);
|
||||
code += AllocateContext(context_variables);
|
||||
LocalVariable* context = MakeTemporary();
|
||||
|
||||
code += LoadLocal(context);
|
||||
code += LoadLocal(address);
|
||||
code += StoreInstanceField(
|
||||
TokenPosition::kNoSource,
|
||||
Slot::GetContextVariableSlotFor(thread_, *context_variables[0]));
|
||||
|
||||
code += AllocateClosure(TokenPosition::kNoSource, target);
|
||||
LocalVariable* closure = MakeTemporary();
|
||||
|
||||
code += LoadLocal(closure);
|
||||
code += LoadLocal(context);
|
||||
code += StoreInstanceField(TokenPosition::kNoSource, Slot::Closure_context());
|
||||
|
||||
code += LoadLocal(closure);
|
||||
code += Constant(target);
|
||||
code +=
|
||||
StoreInstanceField(TokenPosition::kNoSource, Slot::Closure_function());
|
||||
|
||||
// Drop address and context.
|
||||
code += DropTempsPreserveTop(2);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace dart
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ class BaseFlowGraphBuilder {
|
|||
Fragment StoreIndexed(intptr_t class_id);
|
||||
|
||||
void Push(Definition* definition);
|
||||
Definition* Peek();
|
||||
Definition* Peek(intptr_t depth = 0);
|
||||
Value* Pop();
|
||||
Fragment Drop();
|
||||
// Drop given number of temps from the stack but preserve top of the stack.
|
||||
|
@ -292,6 +292,15 @@ class BaseFlowGraphBuilder {
|
|||
return stack_ == nullptr ? 0 : stack_->definition()->temp_index() + 1;
|
||||
}
|
||||
|
||||
// Builds the graph for an invocation of '_asFunctionInternal'.
|
||||
//
|
||||
// 'signatures' contains the pair [<dart signature>, <native signature>].
|
||||
Fragment BuildFfiAsFunctionInternalCall(const TypeArguments& signatures);
|
||||
|
||||
Fragment AllocateObject(TokenPosition position,
|
||||
const Class& klass,
|
||||
intptr_t argument_count);
|
||||
|
||||
protected:
|
||||
intptr_t AllocateBlockId() { return ++last_used_block_id_; }
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "vm/compiler/frontend/bytecode_flow_graph_builder.h"
|
||||
|
||||
#include "vm/compiler/backend/il_printer.h"
|
||||
#include "vm/compiler/ffi.h"
|
||||
#include "vm/compiler/frontend/bytecode_reader.h"
|
||||
#include "vm/compiler/frontend/prologue_builder.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
|
@ -776,6 +777,11 @@ void BytecodeFlowGraphBuilder::BuildDirectCall() {
|
|||
const Function& target = Function::Cast(ConstantAt(DecodeOperandD()).value());
|
||||
const intptr_t argc = DecodeOperandF().value();
|
||||
|
||||
if (compiler::ffi::IsAsFunctionInternal(Z, Isolate::Current(), target)) {
|
||||
BuildFfiAsFunction();
|
||||
return;
|
||||
}
|
||||
|
||||
// Recognize identical() call.
|
||||
// Note: similar optimization is performed in AST flow graph builder - see
|
||||
// StreamingFlowGraphBuilder::BuildStaticInvocation, special_case_identical.
|
||||
|
@ -1560,6 +1566,22 @@ void BytecodeFlowGraphBuilder::BuildAllocateClosure() {
|
|||
code_ += B->AllocateClosure(position_, target);
|
||||
}
|
||||
|
||||
// Builds graph for a call to 'dart:ffi::_asFunctionInternal'. The stack must
|
||||
// look like:
|
||||
//
|
||||
// <receiver> => pointer argument
|
||||
// <type arguments vector> => signatures
|
||||
// ...
|
||||
void BytecodeFlowGraphBuilder::BuildFfiAsFunction() {
|
||||
// The bytecode FGB doesn't eagerly insert PushArguments, so the type
|
||||
// arguments won't be wrapped in a PushArgumentsInstr.
|
||||
const TypeArguments& type_args =
|
||||
TypeArguments::Cast(B->Peek(/*depth=*/1)->AsConstant()->value());
|
||||
// Drop type arguments, preserving pointer.
|
||||
code_ += B->DropTempsPreserveTop(1);
|
||||
code_ += B->BuildFfiAsFunctionInternalCall(type_args);
|
||||
}
|
||||
|
||||
static bool IsICDataEntry(const ObjectPool& object_pool, intptr_t index) {
|
||||
if (object_pool.TypeAt(index) != ObjectPool::EntryType::kTaggedObject) {
|
||||
return false;
|
||||
|
|
|
@ -150,6 +150,7 @@ class BytecodeFlowGraphBuilder {
|
|||
void BuildInterfaceCallCommon(bool is_unchecked_call);
|
||||
|
||||
void BuildInstruction(KernelBytecode::Opcode opcode);
|
||||
void BuildFfiAsFunction();
|
||||
|
||||
#define DECLARE_BUILD_METHOD(name, encoding, kind, op1, op2, op3) \
|
||||
void Build##name();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "vm/compiler/frontend/kernel_binary_flowgraph.h"
|
||||
|
||||
#include "vm/compiler/ffi.h"
|
||||
#include "vm/compiler/frontend/bytecode_flow_graph_builder.h"
|
||||
#include "vm/compiler/frontend/bytecode_reader.h"
|
||||
#include "vm/compiler/frontend/flow_graph_builder.h" // For dart::FlowGraphBuilder::SimpleInstanceOfType.
|
||||
|
@ -3048,6 +3049,10 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(bool is_const,
|
|||
++argument_count;
|
||||
}
|
||||
|
||||
if (compiler::ffi::IsAsFunctionInternal(Z, Isolate::Current(), target)) {
|
||||
return BuildFfiAsFunctionInternal();
|
||||
}
|
||||
|
||||
Fragment instructions;
|
||||
LocalVariable* instance_variable = NULL;
|
||||
|
||||
|
@ -5000,6 +5005,25 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
|
|||
return instructions;
|
||||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
|
||||
const intptr_t argc = ReadUInt(); // read argument count.
|
||||
ASSERT(argc == 1); // pointer
|
||||
const intptr_t list_length = ReadListLength(); // read types list length.
|
||||
ASSERT(list_length == 2); // dart signature, then native signature
|
||||
const TypeArguments& type_arguments =
|
||||
T.BuildTypeArguments(list_length); // read types.
|
||||
Fragment code;
|
||||
const intptr_t positional_count =
|
||||
ReadListLength(); // read positional argument count
|
||||
ASSERT(positional_count == 1);
|
||||
code += BuildExpression(); // build first positional argument (pointer)
|
||||
const intptr_t named_args_len =
|
||||
ReadListLength(); // skip (empty) named arguments list
|
||||
ASSERT(named_args_len == 0);
|
||||
code += B->BuildFfiAsFunctionInternalCall(type_arguments);
|
||||
return code;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace dart
|
||||
|
||||
|
|
|
@ -350,6 +350,10 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
|||
Fragment BuildFunctionNode(TokenPosition parent_position,
|
||||
StringIndex name_index);
|
||||
|
||||
// Build build FG for '_asFunctionInternal'. Reads an Arguments from the
|
||||
// Kernel buffer and pushes the resulting closure.
|
||||
Fragment BuildFfiAsFunctionInternal();
|
||||
|
||||
FlowGraphBuilder* flow_graph_builder_;
|
||||
ActiveClass* const active_class_;
|
||||
TypeTranslator type_translator_;
|
||||
|
|
|
@ -216,16 +216,6 @@ Fragment FlowGraphBuilder::TranslateInstantiatedTypeArguments(
|
|||
return instructions;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::AllocateObject(TokenPosition position,
|
||||
const Class& klass,
|
||||
intptr_t argument_count) {
|
||||
ArgumentArray arguments = GetArguments(argument_count);
|
||||
AllocateObjectInstr* allocate =
|
||||
new (Z) AllocateObjectInstr(position, klass, arguments);
|
||||
Push(allocate);
|
||||
return Fragment(allocate);
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::CatchBlockEntry(const Array& handler_types,
|
||||
intptr_t handler_index,
|
||||
bool needs_stacktrace,
|
||||
|
|
|
@ -127,9 +127,6 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
|||
Fragment TranslateInstantiatedTypeArguments(
|
||||
const TypeArguments& type_arguments);
|
||||
|
||||
Fragment AllocateObject(TokenPosition position,
|
||||
const Class& klass,
|
||||
intptr_t argument_count);
|
||||
Fragment CatchBlockEntry(const Array& handler_types,
|
||||
intptr_t handler_index,
|
||||
bool needs_stacktrace,
|
||||
|
|
|
@ -98,9 +98,6 @@ static void PrecompilationModeHandler(bool value) {
|
|||
|
||||
FLAG_background_compilation = false;
|
||||
FLAG_enable_mirrors = false;
|
||||
// TODO(dacoharkes): Ffi support in AOT
|
||||
// https://github.com/dart-lang/sdk/issues/35765
|
||||
FLAG_enable_ffi = false;
|
||||
FLAG_fields_may_be_reset = true;
|
||||
FLAG_interpret_irregexp = true;
|
||||
FLAG_lazy_dispatchers = false;
|
||||
|
|
|
@ -17273,6 +17273,10 @@ bool AbstractType::IsDartClosureType() const {
|
|||
return !IsFunctionType() && (type_class_id() == kClosureCid);
|
||||
}
|
||||
|
||||
bool AbstractType::IsFfiPointerType() const {
|
||||
return HasTypeClass() && type_class_id() == kFfiPointerCid;
|
||||
}
|
||||
|
||||
bool AbstractType::IsSubtypeOf(const AbstractType& other,
|
||||
Heap::Space space) const {
|
||||
ASSERT(IsFinalized());
|
||||
|
|
|
@ -6842,6 +6842,9 @@ class AbstractType : public Instance {
|
|||
// Check if this type represents the Dart '_Closure' type.
|
||||
bool IsDartClosureType() const;
|
||||
|
||||
// Check if this type represents the 'Pointer' type from "dart:ffi".
|
||||
bool IsFfiPointerType() const;
|
||||
|
||||
// Check the subtype relationship.
|
||||
bool IsSubtypeOf(const AbstractType& other, Heap::Space space) const;
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ class ObjectPointerVisitor;
|
|||
RW(Class, ffi_pointer_class) \
|
||||
RW(Class, ffi_native_type_class) \
|
||||
RW(Class, ffi_struct_class) \
|
||||
RW(Object, ffi_as_function_internal) \
|
||||
// Please remember the last entry must be referred in the 'to' function below.
|
||||
|
||||
// The object store is a per isolate instance which stores references to
|
||||
|
@ -235,7 +236,9 @@ class ObjectStore {
|
|||
OBJECT_STORE_FIELD_LIST(DECLARE_OBJECT_STORE_FIELD,
|
||||
DECLARE_OBJECT_STORE_FIELD)
|
||||
#undef DECLARE_OBJECT_STORE_FIELD
|
||||
RawObject** to() { return reinterpret_cast<RawObject**>(&ffi_struct_class_); }
|
||||
RawObject** to() {
|
||||
return reinterpret_cast<RawObject**>(&ffi_as_function_internal_);
|
||||
}
|
||||
RawObject** to_snapshot(Snapshot::Kind kind) {
|
||||
switch (kind) {
|
||||
case Snapshot::kFull:
|
||||
|
|
|
@ -25,6 +25,7 @@ class ObjectPointerVisitor;
|
|||
V(ApiError, "ApiError") \
|
||||
V(ArgDescVar, ":arg_desc") \
|
||||
V(ArgumentError, "ArgumentError") \
|
||||
V(AsFunctionInternal, "_asFunctionInternal") \
|
||||
V(AssertionError, "_AssertionError") \
|
||||
V(AssignIndexToken, "[]=") \
|
||||
V(AsyncCompleter, ":async_completer") \
|
||||
|
|
|
@ -20,9 +20,8 @@ class DynamicLibrary {
|
|||
external Pointer<T> lookup<T extends NativeType>(String symbolName);
|
||||
|
||||
/// Helper that combines lookup and cast to a Dart function.
|
||||
F lookupFunction<T extends Function, F extends Function>(String symbolName) {
|
||||
return lookup<NativeFunction<T>>(symbolName)?.asFunction<F>();
|
||||
}
|
||||
external F lookupFunction<T extends Function, F extends Function>(
|
||||
String symbolName);
|
||||
|
||||
/// Dynamic libraries are equal if they load the same library.
|
||||
external bool operator ==(other);
|
||||
|
|
|
@ -46,6 +46,9 @@ class Pointer<T extends NativeType> extends NativeType {
|
|||
/// The pointer returned will remain alive for the duration of the current
|
||||
/// isolate's lifetime. After the isolate it was created in is terminated,
|
||||
/// invoking it from native code will cause undefined behavior.
|
||||
///
|
||||
/// Does not accept dynamic invocations -- where the type of the receiver is
|
||||
/// [dynamic].
|
||||
external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
||||
@DartRepresentationOf("T") Function f, Object exceptionalReturn);
|
||||
|
||||
|
@ -81,7 +84,8 @@ class Pointer<T extends NativeType> extends NativeType {
|
|||
/// Convert to Dart function, automatically marshalling the arguments
|
||||
/// and return value.
|
||||
///
|
||||
/// Can only be called on [Pointer]<[NativeFunction]>.
|
||||
/// Can only be called on [Pointer]<[NativeFunction]>. Does not accept dynamic
|
||||
/// invocations -- where the type of the receiver is [dynamic].
|
||||
external R asFunction<@DartRepresentationOf("T") R extends Function>();
|
||||
|
||||
/// Free memory on the C heap pointed to by this pointer with free().
|
||||
|
|
|
@ -19,7 +19,7 @@ function_callbacks_test/02: Skip
|
|||
function_callbacks_test/03: Skip
|
||||
|
||||
[ $runtime == dart_precompiled ]
|
||||
*: Skip # AOT is not yet supported: dartbug.com/35765
|
||||
function_callbacks_test: Skip # Issue dartbug.com/37295
|
||||
|
||||
[ $arch == arm && $system != android ]
|
||||
*: Skip # "hardfp" calling convention is not yet supported (iOS is also supported but not tested): dartbug.com/36309
|
||||
|
|
|
@ -13,6 +13,8 @@ import "package:expect/expect.dart";
|
|||
main() {
|
||||
testWrongArity();
|
||||
testWrongTypes();
|
||||
testDynamicAsFunction();
|
||||
testDynamicLookupFunction();
|
||||
}
|
||||
|
||||
ffi.DynamicLibrary ffiTestFunctions =
|
||||
|
@ -61,3 +63,21 @@ void testWrongTypes() {
|
|||
Expect.throwsTypeError(() => pointerOp(0));
|
||||
}
|
||||
}
|
||||
|
||||
// Test that invoking 'Pointer.asFunction' with a dynamic receiver type throws
|
||||
// an exception.
|
||||
void testDynamicAsFunction() {
|
||||
dynamic x = ffi.nullptr.cast<ffi.NativeFunction<ffi.Void Function()>>();
|
||||
Expect.throwsUnsupportedError(() {
|
||||
x.asFunction<void Function()>();
|
||||
});
|
||||
}
|
||||
|
||||
// Test that invoking 'DynamicLibrary.lookupFunction' with a dynamic receiver
|
||||
// type throws an exception.
|
||||
void testDynamicLookupFunction() {
|
||||
dynamic lib = ffiTestFunctions;
|
||||
Expect.throwsUnsupportedError(() {
|
||||
lib.lookupFunction<ffi.Void Function(), void Function()>("_");
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue