8408232522
Introduces NativeRepresentation and NativeLocation for the compilation of FFI. NativeRepresentations are able to express all representations (or types) of the native ABIs we bind to with FFI, this is more representations that than that are used in Dart itself. NativeLocations are able to express all locations of the native ABIs we bind to with FFI, this is more types of locations than that are used for the Dart calling convention. See the documentation in the respective files. These NativeLocations and NativeRepresentations are computed by the NativeCallingConvention and consumed by the Marshaller and Assemblers. This reenginering is required for go/dart-ffi-by-value, hardfp (Arm S and D fpu registers), and iOS 64 bit (non-word-aligned stack arguments). In addition, by using the NativeRepresentations we also get slightly reduced code size: * The tracking of sizes is improved, so less sign/zero-extension operations are required. * UnboxedWidthExtenderInstr is fully removed, the size extension is done inside the native moves, coalescing moves and size extension when possible. * BitCastInstr is only used when really needed. This reduces code-size on arm32 softfp. This fixes the iOS arm64 calling convention, manually tested with https://github.com/flutter/flutter/pull/46078 and https://dart-review.googlesource.com/c/sdk/+/131074. Fixes: https://github.com/dart-lang/sdk/issues/39637 Issue: https://github.com/dart-lang/sdk/issues/36309 Issue: https://github.com/dart-lang/sdk/issues/36730 Change-Id: I8878bc0f314277bab4ca22f417c6295ecc017720 Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-precomp-linux-debug-x64-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,dart-sdk-linux-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129081 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
512 lines
20 KiB
C++
512 lines
20 KiB
C++
// Copyright (c) 2019, 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.
|
|
|
|
#include "include/dart_api.h"
|
|
#include "platform/globals.h"
|
|
#include "vm/bootstrap_natives.h"
|
|
#include "vm/class_finalizer.h"
|
|
#include "vm/class_id.h"
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/ffi/call.h"
|
|
#include "vm/compiler/ffi/callback.h"
|
|
#include "vm/compiler/ffi/native_type.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"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
// The following functions are runtime checks on type arguments.
|
|
// Some checks are also performed in kernel transformation, these are asserts.
|
|
// Some checks are only performed at runtime to allow for generic code, these
|
|
// throw ArgumentExceptions.
|
|
|
|
static bool IsPointerType(const AbstractType& type) {
|
|
return RawObject::IsFfiPointerClassId(type.type_class_id());
|
|
}
|
|
|
|
static void CheckSized(const AbstractType& type_arg) {
|
|
const classid_t type_cid = type_arg.type_class_id();
|
|
if (RawObject::IsFfiNativeTypeTypeClassId(type_cid) ||
|
|
RawObject::IsFfiTypeVoidClassId(type_cid) ||
|
|
RawObject::IsFfiTypeNativeFunctionClassId(type_cid)) {
|
|
const String& error = String::Handle(String::NewFormatted(
|
|
"%s does not have a predefined size (@unsized). "
|
|
"Unsized NativeTypes do not support [sizeOf] because their size "
|
|
"is unknown. "
|
|
"Consequently, [allocate], [Pointer.load], [Pointer.store], and "
|
|
"[Pointer.elementAt] are not available.",
|
|
String::Handle(type_arg.UserVisibleName()).ToCString()));
|
|
Exceptions::ThrowArgumentError(error);
|
|
}
|
|
}
|
|
|
|
// The following functions are runtime checks on arguments.
|
|
|
|
static const Integer& AsInteger(const Instance& instance) {
|
|
if (!instance.IsInteger()) {
|
|
const String& error = String::Handle(String::NewFormatted(
|
|
"Expected an int but found %s", instance.ToCString()));
|
|
Exceptions::ThrowArgumentError(error);
|
|
}
|
|
return Integer::Cast(instance);
|
|
}
|
|
|
|
static const Double& AsDouble(const Instance& instance) {
|
|
if (!instance.IsDouble()) {
|
|
const String& error = String::Handle(String::NewFormatted(
|
|
"Expected a double but found %s", instance.ToCString()));
|
|
Exceptions::ThrowArgumentError(error);
|
|
}
|
|
return Double::Cast(instance);
|
|
}
|
|
|
|
// Calcuate the size of a native type.
|
|
//
|
|
// You must check [IsConcreteNativeType] and [CheckSized] first to verify that
|
|
// this type has a defined size.
|
|
static size_t SizeOf(const AbstractType& type, Zone* zone) {
|
|
if (RawObject::IsFfiTypeClassId(type.type_class_id())) {
|
|
return compiler::ffi::NativeType::FromAbstractType(type, zone)
|
|
.SizeInBytes();
|
|
} else {
|
|
Class& struct_class = Class::Handle(type.type_class());
|
|
Object& result = Object::Handle(
|
|
struct_class.InvokeGetter(Symbols::SizeOfStructField(),
|
|
/*throw_nsm_if_absent=*/false,
|
|
/*respect_reflectable=*/false));
|
|
ASSERT(!result.IsNull() && result.IsInteger());
|
|
return Integer::Cast(result).AsInt64Value();
|
|
}
|
|
}
|
|
|
|
// The remainder of this file implements the dart:ffi native methods.
|
|
|
|
DEFINE_NATIVE_ENTRY(Ffi_fromAddress, 1, 1) {
|
|
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Integer, arg_ptr, arguments->NativeArgAt(0));
|
|
return Pointer::New(type_arg, arg_ptr.AsInt64Value());
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Ffi_address, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
|
return Integer::New(pointer.NativeAddress());
|
|
}
|
|
|
|
static RawObject* LoadValueNumeric(Zone* zone,
|
|
const Pointer& target,
|
|
classid_t type_cid,
|
|
const Integer& index) {
|
|
// TODO(36370): Make representation consistent with kUnboxedFfiIntPtr.
|
|
const size_t address =
|
|
target.NativeAddress() +
|
|
static_cast<intptr_t>(index.AsInt64Value()) *
|
|
compiler::ffi::NativeType::FromTypedDataClassId(type_cid, zone)
|
|
.SizeInBytes();
|
|
switch (type_cid) {
|
|
case kFfiInt8Cid:
|
|
return Integer::New(*reinterpret_cast<int8_t*>(address));
|
|
case kFfiInt16Cid:
|
|
return Integer::New(*reinterpret_cast<int16_t*>(address));
|
|
case kFfiInt32Cid:
|
|
return Integer::New(*reinterpret_cast<int32_t*>(address));
|
|
case kFfiInt64Cid:
|
|
return Integer::New(*reinterpret_cast<int64_t*>(address));
|
|
case kFfiUint8Cid:
|
|
return Integer::NewFromUint64(*reinterpret_cast<uint8_t*>(address));
|
|
case kFfiUint16Cid:
|
|
return Integer::NewFromUint64(*reinterpret_cast<uint16_t*>(address));
|
|
case kFfiUint32Cid:
|
|
return Integer::NewFromUint64(*reinterpret_cast<uint32_t*>(address));
|
|
case kFfiUint64Cid:
|
|
return Integer::NewFromUint64(*reinterpret_cast<uint64_t*>(address));
|
|
case kFfiIntPtrCid:
|
|
return Integer::New(*reinterpret_cast<intptr_t*>(address));
|
|
case kFfiFloatCid:
|
|
return Double::New(*reinterpret_cast<float_t*>(address));
|
|
case kFfiDoubleCid:
|
|
return Double::New(*reinterpret_cast<double_t*>(address));
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
#define DEFINE_NATIVE_ENTRY_LOAD(type) \
|
|
DEFINE_NATIVE_ENTRY(Ffi_load##type, 0, 2) { \
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); \
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1)); \
|
|
return LoadValueNumeric(zone, pointer, kFfi##type##Cid, index); \
|
|
}
|
|
CLASS_LIST_FFI_NUMERIC(DEFINE_NATIVE_ENTRY_LOAD)
|
|
#undef DEFINE_NATIVE_ENTRY_LOAD
|
|
|
|
DEFINE_NATIVE_ENTRY(Ffi_loadPointer, 1, 2) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
|
|
|
|
const auto& pointer_type_arg =
|
|
AbstractType::Handle(zone, pointer.type_argument());
|
|
const AbstractType& type_arg =
|
|
AbstractType::Handle(TypeArguments::Handle(pointer_type_arg.arguments())
|
|
.TypeAt(Pointer::kNativeTypeArgPos));
|
|
|
|
// TODO(36370): Make representation consistent with kUnboxedFfiIntPtr.
|
|
const size_t address =
|
|
pointer.NativeAddress() + static_cast<intptr_t>(index.AsInt64Value()) *
|
|
SizeOf(pointer_type_arg, zone);
|
|
|
|
return Pointer::New(type_arg, *reinterpret_cast<uword*>(address));
|
|
}
|
|
|
|
static RawObject* LoadValueStruct(Zone* zone,
|
|
const Pointer& target,
|
|
const AbstractType& instance_type_arg) {
|
|
// Result is a struct class -- find <class name>.#fromPointer
|
|
// constructor and call it.
|
|
const Class& cls = Class::Handle(zone, instance_type_arg.type_class());
|
|
const Function& constructor =
|
|
Function::Handle(cls.LookupFunctionAllowPrivate(String::Handle(
|
|
String::Concat(String::Handle(String::Concat(
|
|
String::Handle(cls.Name()), Symbols::Dot())),
|
|
Symbols::StructFromPointer()))));
|
|
ASSERT(!constructor.IsNull());
|
|
ASSERT(constructor.IsGenerativeConstructor());
|
|
ASSERT(!Object::Handle(constructor.VerifyCallEntryPoint()).IsError());
|
|
const Instance& new_object = Instance::Handle(Instance::New(cls));
|
|
ASSERT(cls.is_allocated() || Dart::vm_snapshot_kind() != Snapshot::kFullAOT);
|
|
const Array& args = Array::Handle(zone, Array::New(2));
|
|
args.SetAt(0, new_object);
|
|
args.SetAt(1, target);
|
|
const Object& constructorResult =
|
|
Object::Handle(DartEntry::InvokeFunction(constructor, args));
|
|
ASSERT(!constructorResult.IsError());
|
|
return new_object.raw();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Ffi_loadStruct, 0, 2) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
|
const AbstractType& pointer_type_arg =
|
|
AbstractType::Handle(pointer.type_argument());
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
|
|
|
|
// TODO(36370): Make representation consistent with kUnboxedFfiIntPtr.
|
|
const size_t address =
|
|
pointer.NativeAddress() + static_cast<intptr_t>(index.AsInt64Value()) *
|
|
SizeOf(pointer_type_arg, zone);
|
|
const Pointer& pointer_offset =
|
|
Pointer::Handle(zone, Pointer::New(pointer_type_arg, address));
|
|
|
|
return LoadValueStruct(zone, pointer_offset, pointer_type_arg);
|
|
}
|
|
|
|
static void StoreValueNumeric(Zone* zone,
|
|
const Pointer& pointer,
|
|
classid_t type_cid,
|
|
const Integer& index,
|
|
const Instance& new_value) {
|
|
// TODO(36370): Make representation consistent with kUnboxedFfiIntPtr.
|
|
const size_t address =
|
|
pointer.NativeAddress() +
|
|
static_cast<intptr_t>(index.AsInt64Value()) *
|
|
compiler::ffi::NativeType::FromTypedDataClassId(type_cid, zone)
|
|
.SizeInBytes();
|
|
switch (type_cid) {
|
|
case kFfiInt8Cid:
|
|
*reinterpret_cast<int8_t*>(address) = AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiInt16Cid:
|
|
*reinterpret_cast<int16_t*>(address) =
|
|
AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiInt32Cid:
|
|
*reinterpret_cast<int32_t*>(address) =
|
|
AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiInt64Cid:
|
|
*reinterpret_cast<int64_t*>(address) =
|
|
AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiUint8Cid:
|
|
*reinterpret_cast<uint8_t*>(address) =
|
|
AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiUint16Cid:
|
|
*reinterpret_cast<uint16_t*>(address) =
|
|
AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiUint32Cid:
|
|
*reinterpret_cast<uint32_t*>(address) =
|
|
AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiUint64Cid:
|
|
*reinterpret_cast<uint64_t*>(address) =
|
|
AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiIntPtrCid:
|
|
*reinterpret_cast<intptr_t*>(address) =
|
|
AsInteger(new_value).AsInt64Value();
|
|
break;
|
|
case kFfiFloatCid:
|
|
*reinterpret_cast<float*>(address) = AsDouble(new_value).value();
|
|
break;
|
|
case kFfiDoubleCid:
|
|
*reinterpret_cast<double*>(address) = AsDouble(new_value).value();
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
#define DEFINE_NATIVE_ENTRY_STORE(type) \
|
|
DEFINE_NATIVE_ENTRY(Ffi_store##type, 0, 3) { \
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); \
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1)); \
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(2)); \
|
|
StoreValueNumeric(zone, pointer, kFfi##type##Cid, index, value); \
|
|
return Object::null(); \
|
|
}
|
|
CLASS_LIST_FFI_NUMERIC(DEFINE_NATIVE_ENTRY_STORE)
|
|
#undef DEFINE_NATIVE_ENTRY_STORE
|
|
|
|
DEFINE_NATIVE_ENTRY(Ffi_storePointer, 0, 3) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, new_value, arguments->NativeArgAt(2));
|
|
AbstractType& pointer_type_arg =
|
|
AbstractType::Handle(pointer.type_argument());
|
|
|
|
auto& new_value_type =
|
|
AbstractType::Handle(zone, new_value.GetType(Heap::kNew));
|
|
if (!new_value_type.IsSubtypeOf(NNBDMode::kLegacyLib, pointer_type_arg,
|
|
Heap::kNew)) {
|
|
const String& error = String::Handle(String::NewFormatted(
|
|
"New value (%s) is not a subtype of '%s'.",
|
|
String::Handle(new_value_type.UserVisibleName()).ToCString(),
|
|
String::Handle(pointer_type_arg.UserVisibleName()).ToCString()));
|
|
Exceptions::ThrowArgumentError(error);
|
|
}
|
|
|
|
ASSERT(IsPointerType(pointer_type_arg));
|
|
// TODO(36370): Make representation consistent with kUnboxedFfiIntPtr.
|
|
const size_t address =
|
|
pointer.NativeAddress() + static_cast<intptr_t>(index.AsInt64Value()) *
|
|
SizeOf(pointer_type_arg, zone);
|
|
*reinterpret_cast<uword*>(address) = new_value.NativeAddress();
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Ffi_sizeOf, 1, 0) {
|
|
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
|
CheckSized(type_arg);
|
|
|
|
return Integer::New(SizeOf(type_arg, zone));
|
|
}
|
|
|
|
// 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_asExternalTypedData, 0, 2) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Integer, count, arguments->NativeArgAt(1));
|
|
const auto& pointer_type_arg = AbstractType::Handle(pointer.type_argument());
|
|
const classid_t type_cid = pointer_type_arg.type_class_id();
|
|
classid_t cid = 0;
|
|
|
|
switch (type_cid) {
|
|
case kFfiInt8Cid:
|
|
cid = kExternalTypedDataInt8ArrayCid;
|
|
break;
|
|
case kFfiUint8Cid:
|
|
cid = kExternalTypedDataUint8ArrayCid;
|
|
break;
|
|
case kFfiInt16Cid:
|
|
cid = kExternalTypedDataInt16ArrayCid;
|
|
break;
|
|
case kFfiUint16Cid:
|
|
cid = kExternalTypedDataUint16ArrayCid;
|
|
break;
|
|
case kFfiInt32Cid:
|
|
cid = kExternalTypedDataInt32ArrayCid;
|
|
break;
|
|
case kFfiUint32Cid:
|
|
cid = kExternalTypedDataUint32ArrayCid;
|
|
break;
|
|
case kFfiInt64Cid:
|
|
cid = kExternalTypedDataInt64ArrayCid;
|
|
break;
|
|
case kFfiUint64Cid:
|
|
cid = kExternalTypedDataUint64ArrayCid;
|
|
break;
|
|
case kFfiIntPtrCid:
|
|
cid = kWordSize == 4 ? kExternalTypedDataInt32ArrayCid
|
|
: kExternalTypedDataInt64ArrayCid;
|
|
break;
|
|
case kFfiFloatCid:
|
|
cid = kExternalTypedDataFloat32ArrayCid;
|
|
break;
|
|
case kFfiDoubleCid:
|
|
cid = kExternalTypedDataFloat64ArrayCid;
|
|
break;
|
|
default: {
|
|
const String& error = String::Handle(
|
|
String::NewFormatted("Cannot create a TypedData from a Pointer to %s",
|
|
pointer_type_arg.ToCString()));
|
|
Exceptions::ThrowArgumentError(error);
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
const intptr_t element_count = count.AsInt64Value();
|
|
|
|
if (element_count < 0 ||
|
|
element_count > ExternalTypedData::MaxElements(cid)) {
|
|
const String& error = String::Handle(
|
|
String::NewFormatted("Count must be in the range [0, %" Pd "].",
|
|
ExternalTypedData::MaxElements(cid)));
|
|
Exceptions::ThrowArgumentError(error);
|
|
}
|
|
|
|
// The address must be aligned by the element size.
|
|
const intptr_t element_size = ExternalTypedData::ElementSizeFor(cid);
|
|
if (!Utils::IsAligned(pointer.NativeAddress(), element_size)) {
|
|
const String& error = String::Handle(
|
|
String::NewFormatted("Pointer address must be aligned to a multiple of"
|
|
"the element size (%" Pd ").",
|
|
element_size));
|
|
Exceptions::ThrowArgumentError(error);
|
|
}
|
|
|
|
const auto& typed_data_class =
|
|
Class::Handle(zone, isolate->class_table()->At(cid));
|
|
const auto& error =
|
|
Error::Handle(zone, typed_data_class.EnsureIsFinalized(thread));
|
|
if (!error.IsNull()) {
|
|
Exceptions::PropagateError(error);
|
|
}
|
|
|
|
return ExternalTypedData::New(
|
|
cid, reinterpret_cast<uint8_t*>(pointer.NativeAddress()), element_count,
|
|
Heap::kNew);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Ffi_nativeCallbackFunction, 1, 2) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
|
// Calls to this function are removed by the flow-graph builder in AOT.
|
|
// See StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction().
|
|
UNREACHABLE();
|
|
#else
|
|
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Closure, closure, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, exceptional_return,
|
|
arguments->NativeArgAt(1));
|
|
|
|
ASSERT(type_arg.IsInstantiated() && type_arg.IsFunctionType());
|
|
const Function& native_signature =
|
|
Function::Handle(zone, Type::Cast(type_arg).signature());
|
|
Function& func = Function::Handle(zone, closure.function());
|
|
|
|
// The FE verifies that the target of a 'fromFunction' is a static method, so
|
|
// the value we see here must be a static tearoff. See ffi_use_sites.dart for
|
|
// details.
|
|
//
|
|
// TODO(36748): Define hot-reload semantics of native callbacks. We may need
|
|
// to look up the target by name.
|
|
ASSERT(func.IsImplicitClosureFunction());
|
|
func = func.parent_function();
|
|
ASSERT(func.is_static());
|
|
|
|
// We are returning an object which is not an Instance here. This is only OK
|
|
// because we know that the result will be passed directly to
|
|
// _pointerFromFunction and will not leak out into user code.
|
|
arguments->SetReturn(
|
|
Function::Handle(zone, compiler::ffi::NativeCallbackFunction(
|
|
native_signature, func, exceptional_return)));
|
|
|
|
// Because we have already set the return value.
|
|
return Object::sentinel().raw();
|
|
#endif
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Ffi_pointerFromFunction, 1, 1) {
|
|
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
|
const Function& function =
|
|
Function::CheckedHandle(zone, arguments->NativeArg0());
|
|
|
|
Code& code = Code::Handle(zone);
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
code = function.CurrentCode();
|
|
|
|
// Blobs snapshots don't support BSS-relative relocations required by native
|
|
// callbacks (yet). Issue an error if the code has an unpatched relocation.
|
|
if (!code.VerifyBSSRelocations()) {
|
|
Exceptions::ThrowUnsupportedError(
|
|
"FFI callbacks are not yet supported in blobs snapshots. Please use "
|
|
"ELF or Assembly snapshots instead.");
|
|
}
|
|
#else
|
|
// We compile the callback immediately because we need to return a pointer to
|
|
// the entry-point. Native calls do not use patching like Dart calls, so we
|
|
// cannot compile it lazily.
|
|
const Object& result = Object::Handle(
|
|
zone, Compiler::CompileOptimizedFunction(thread, function));
|
|
if (result.IsError()) {
|
|
Exceptions::PropagateError(Error::Cast(result));
|
|
}
|
|
ASSERT(result.IsCode());
|
|
code ^= result.raw();
|
|
#endif
|
|
|
|
ASSERT(!code.IsNull());
|
|
thread->SetFfiCallbackCode(function.FfiCallbackId(), code);
|
|
|
|
uword entry_point = code.EntryPoint();
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
if (NativeCallbackTrampolines::Enabled()) {
|
|
entry_point = isolate->native_callback_trampolines()->TrampolineForId(
|
|
function.FfiCallbackId());
|
|
}
|
|
#endif
|
|
|
|
return Pointer::New(type_arg, entry_point);
|
|
}
|
|
|
|
} // namespace dart
|