Skip to content

Commit

Permalink
[vm/ffi] Support missing info in marshaller
Browse files Browse the repository at this point in the history
When ABI-specific integers are introduced, their mappings can be
partial. We need to account for this in the marshaller.

This CL refactors the marshaller API to take an char** error return
parameter and changes the return type to a pointer (nullable) rather
than a reference.

Note that with only this CL we can not generate errors yet, because all
native types are still complete.

TEST=runtime/vm/compiler/ffi/native_type_vm_test.cc
TEST=tests/ffi*

Bug: #42816

Change-Id: Id97e73795c357e129e6280e8c5b528d18072c14d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221632
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Ryan Macnak <[email protected]>
  • Loading branch information
dcharkes authored and Commit Bot committed Dec 2, 2021
1 parent cda2289 commit aa27868
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 93 deletions.
95 changes: 71 additions & 24 deletions runtime/vm/compiler/ffi/marshaller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ namespace ffi {
const intptr_t kNativeParamsStartAt = 1;

// Representations of the arguments and return value of a C signature function.
static const NativeFunctionType& NativeFunctionSignature(
const NativeFunctionType* NativeFunctionTypeFromFunctionType(
Zone* zone,
const FunctionType& c_signature) {
const FunctionType& c_signature,
const char** error) {
ASSERT(c_signature.NumOptionalParameters() == 0);
ASSERT(c_signature.NumOptionalPositionalParameters() == 0);

Expand All @@ -38,29 +39,41 @@ static const NativeFunctionType& NativeFunctionSignature(
for (intptr_t i = 0; i < num_arguments; i++) {
AbstractType& arg_type = AbstractType::Handle(
zone, c_signature.ParameterTypeAt(i + kNativeParamsStartAt));
const auto& rep = NativeType::FromAbstractType(zone, arg_type);
argument_representations.Add(&rep);
const auto rep = NativeType::FromAbstractType(zone, arg_type, error);
if (*error != nullptr) {
return nullptr;
}
argument_representations.Add(rep);
}

const auto& result_type =
AbstractType::Handle(zone, c_signature.result_type());
const auto& result_representation =
NativeType::FromAbstractType(zone, result_type);
const auto result_representation =
NativeType::FromAbstractType(zone, result_type, error);
if (*error != nullptr) {
return nullptr;
}

const auto& result = *new (zone) NativeFunctionType(argument_representations,
result_representation);
const auto result = new (zone)
NativeFunctionType(argument_representations, *result_representation);
return result;
}

BaseMarshaller::BaseMarshaller(Zone* zone, const Function& dart_signature)
: zone_(zone),
dart_signature_(dart_signature),
c_signature_(
FunctionType::ZoneHandle(zone, dart_signature.FfiCSignature())),
native_calling_convention_(NativeCallingConvention::FromSignature(
zone,
NativeFunctionSignature(zone_, c_signature_))) {
ASSERT(dart_signature_.IsZoneHandle());
CallMarshaller* CallMarshaller::FromFunction(Zone* zone,
const Function& function,
const char** error) {
ASSERT(function.IsZoneHandle());
const auto& c_signature =
FunctionType::ZoneHandle(zone, function.FfiCSignature());
const auto native_function_signature =
NativeFunctionTypeFromFunctionType(zone, c_signature, error);
if (*error != nullptr) {
return nullptr;
}
const auto& native_calling_convention =
NativeCallingConvention::FromSignature(zone, *native_function_signature);
return new (zone)
CallMarshaller(zone, function, c_signature, native_calling_convention);
}

AbstractTypePtr BaseMarshaller::CType(intptr_t arg_index) const {
Expand All @@ -72,6 +85,27 @@ AbstractTypePtr BaseMarshaller::CType(intptr_t arg_index) const {
return c_signature_.ParameterTypeAt(arg_index + kNativeParamsStartAt);
}

// Keep consistent with Function::FfiCSignatureReturnsStruct.
bool BaseMarshaller::IsCompound(intptr_t arg_index) const {
const auto& type = AbstractType::Handle(zone_, CType(arg_index));
if (IsFfiTypeClassId(type.type_class_id())) {
return false;
}
#ifdef DEBUG
const auto& cls = Class::Handle(this->zone_, type.type_class());
const auto& superClass = Class::Handle(this->zone_, cls.SuperClass());
// TODO(http://dartbug.com/42563): Implement AbiSpecificInt.
const bool is_struct =
String::Handle(this->zone_, superClass.UserVisibleName())
.Equals(Symbols::Struct());
const bool is_union =
String::Handle(this->zone_, superClass.UserVisibleName())
.Equals(Symbols::Union());
RELEASE_ASSERT(is_struct || is_union);
#endif
return true;
}

bool BaseMarshaller::ContainsHandles() const {
return dart_signature_.FfiCSignatureContainsHandles();
}
Expand Down Expand Up @@ -574,13 +608,26 @@ class CallbackArgumentTranslator : public ValueObject {
intptr_t argument_slots_required_ = 0;
};

CallbackMarshaller::CallbackMarshaller(Zone* zone,
const Function& dart_signature)
: BaseMarshaller(zone, dart_signature),
callback_locs_(CallbackArgumentTranslator::TranslateArgumentLocations(
zone_,
native_calling_convention_.argument_locations(),
native_calling_convention_.return_location())) {}
CallbackMarshaller* CallbackMarshaller::FromFunction(Zone* zone,
const Function& function,
const char** error) {
ASSERT(function.IsZoneHandle());
const auto& c_signature =
FunctionType::ZoneHandle(zone, function.FfiCSignature());
const auto native_function_signature =
NativeFunctionTypeFromFunctionType(zone, c_signature, error);
if (*error != nullptr) {
return nullptr;
}
const auto& native_calling_convention =
NativeCallingConvention::FromSignature(zone, *native_function_signature);
const auto& callback_locs =
CallbackArgumentTranslator::TranslateArgumentLocations(
zone, native_calling_convention.argument_locations(),
native_calling_convention.return_location());
return new (zone) CallbackMarshaller(
zone, function, c_signature, native_calling_convention, callback_locs);
}

const NativeLocation& CallbackMarshaller::NativeLocationOfNativeParameter(
intptr_t def_index) const {
Expand Down
51 changes: 42 additions & 9 deletions runtime/vm/compiler/ffi/marshaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ namespace ffi {
// Values below 0 index result (result might be multiple if composite).
const intptr_t kResultIndex = -1;

// Inspects the function signature and transitively any class and field
// definitions and annotations.
const NativeFunctionType* NativeFunctionTypeFromFunctionType(
Zone* zone,
const FunctionType& c_signature,
const char** error);

// Provides the mapping from the native calling convention to the Dart calling
// convention.
//
Expand Down Expand Up @@ -114,11 +121,7 @@ class BaseMarshaller : public ZoneAllocated {
kFfiBoolCid;
}

bool IsCompound(intptr_t arg_index) const {
const auto& type = AbstractType::Handle(zone_, CType(arg_index));
const bool predefined = IsFfiTypeClassId(type.type_class_id());
return !predefined;
}
bool IsCompound(intptr_t arg_index) const;

// Treated as a null constant in Dart.
bool IsVoid(intptr_t arg_index) const {
Expand All @@ -131,7 +134,14 @@ class BaseMarshaller : public ZoneAllocated {
StringPtr function_name() const { return dart_signature_.name(); }

protected:
BaseMarshaller(Zone* zone, const Function& dart_signature);
BaseMarshaller(Zone* zone,
const Function& dart_signature,
const FunctionType& c_signature,
const NativeCallingConvention& native_calling_convention)
: zone_(zone),
dart_signature_(dart_signature),
c_signature_(c_signature),
native_calling_convention_(native_calling_convention) {}

~BaseMarshaller() {}

Expand All @@ -145,8 +155,18 @@ class BaseMarshaller : public ZoneAllocated {

class CallMarshaller : public BaseMarshaller {
public:
CallMarshaller(Zone* zone, const Function& dart_signature)
: BaseMarshaller(zone, dart_signature) {}
static CallMarshaller* FromFunction(Zone* zone,
const Function& function,
const char** error);

CallMarshaller(Zone* zone,
const Function& dart_signature,
const FunctionType& c_signature,
const NativeCallingConvention& native_calling_convention)
: BaseMarshaller(zone,
dart_signature,
c_signature,
native_calling_convention) {}

virtual Representation RepInFfiCall(intptr_t def_index_global) const;

Expand All @@ -173,7 +193,20 @@ class CallMarshaller : public BaseMarshaller {

class CallbackMarshaller : public BaseMarshaller {
public:
CallbackMarshaller(Zone* zone, const Function& dart_signature);
static CallbackMarshaller* FromFunction(Zone* zone,
const Function& function,
const char** error);

CallbackMarshaller(Zone* zone,
const Function& dart_signature,
const FunctionType& c_signature,
const NativeCallingConvention& native_calling_convention,
const NativeLocations& callback_locs)
: BaseMarshaller(zone,
dart_signature,
c_signature,
native_calling_convention),
callback_locs_(callback_locs) {}

virtual Representation RepInFfiCall(intptr_t def_index_global) const;

Expand Down
116 changes: 68 additions & 48 deletions runtime/vm/compiler/ffi/native_type.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/class_id.h"
#include "vm/compiler/ffi/abi.h"
#include "vm/constants.h"
#include "vm/zone_text_buffer.h"

Expand Down Expand Up @@ -386,53 +387,22 @@ static PrimitiveType TypeRepresentation(classid_t class_id) {
}
}

NativeType& NativeType::FromTypedDataClassId(Zone* zone, classid_t class_id) {
const NativeType& NativeType::FromTypedDataClassId(Zone* zone,
classid_t class_id) {
ASSERT(IsFfiPredefinedClassId(class_id));
const auto fundamental_rep = TypeRepresentation(class_id);
return *new (zone) NativePrimitiveType(fundamental_rep);
}

#if !defined(FFI_UNIT_TESTS)
NativeType& NativeType::FromAbstractType(Zone* zone, const AbstractType& type) {
const classid_t class_id = type.type_class_id();
if (IsFfiPredefinedClassId(class_id)) {
return NativeType::FromTypedDataClassId(zone, class_id);
}

// User-defined structs.
const auto& cls = Class::Handle(zone, type.type_class());
const auto& superClass = Class::Handle(zone, cls.SuperClass());
const bool is_struct = String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::Struct());
ASSERT(is_struct || String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::Union()));

auto& pragmas = Object::Handle(zone);
Library::FindPragma(dart::Thread::Current(), /*only_core=*/false, cls,
Symbols::vm_ffi_struct_fields(), /*multiple=*/true,
&pragmas);
ASSERT(!pragmas.IsNull());
ASSERT(pragmas.IsGrowableObjectArray());
const auto& pragmas_array = GrowableObjectArray::Cast(pragmas);
auto& pragma = Instance::Handle(zone);
auto& clazz = Class::Handle(zone);
auto& library = Library::Handle(zone);
for (intptr_t i = 0; i < pragmas_array.Length(); i++) {
pragma ^= pragmas_array.At(i);
clazz ^= pragma.clazz();
library ^= clazz.library();
if (String::Handle(zone, clazz.UserVisibleName())
.Equals(Symbols::FfiStructLayout()) &&
String::Handle(zone, library.url()).Equals(Symbols::DartFfi())) {
break;
}
}

static const NativeType* CompoundFromPragma(Zone* zone,
const Instance& pragma,
bool is_struct,
const char** error) {
const auto& struct_layout = pragma;
const auto& struct_layout_class = clazz;
ASSERT(String::Handle(zone, struct_layout_class.UserVisibleName())
const auto& clazz = Class::Handle(zone, struct_layout.clazz());
ASSERT(String::Handle(zone, clazz.UserVisibleName())
.Equals(Symbols::FfiStructLayout()));
ASSERT(String::Handle(zone, library.url()).Equals(Symbols::DartFfi()));
const auto& struct_layout_fields = Array::Handle(zone, clazz.fields());
ASSERT(struct_layout_fields.Length() == 2);
const auto& types_field =
Expand Down Expand Up @@ -460,8 +430,11 @@ NativeType& NativeType::FromAbstractType(Zone* zone, const AbstractType& type) {
// Subtype of NativeType: Struct, native integer or native float.
field_type ^= field_types.At(i);
const auto& field_native_type =
NativeType::FromAbstractType(zone, field_type);
field_native_types.Add(&field_native_type);
NativeType::FromAbstractType(zone, field_type, error);
if (*error != nullptr) {
return nullptr;
}
field_native_types.Add(field_native_type);
} else {
// Inline array.
const auto& struct_layout_array_class =
Expand All @@ -482,20 +455,67 @@ NativeType& NativeType::FromAbstractType(Zone* zone, const AbstractType& type) {
.Equals(Symbols::Length()));
const auto& length = Smi::Handle(
zone, Smi::RawCast(field_instance.GetField(length_field)));
const auto& element_type = NativeType::FromAbstractType(zone, field_type);
const auto& field_native_type =
*new (zone) NativeArrayType(element_type, length.AsInt64Value());
field_native_types.Add(&field_native_type);
const auto element_type =
NativeType::FromAbstractType(zone, field_type, error);
if (*error != nullptr) {
return nullptr;
}
const auto field_native_type =
new (zone) NativeArrayType(*element_type, length.AsInt64Value());
field_native_types.Add(field_native_type);
}
}

if (is_struct) {
return NativeStructType::FromNativeTypes(zone, field_native_types,
member_packing);
return &NativeStructType::FromNativeTypes(zone, field_native_types,
member_packing);
} else {
return NativeUnionType::FromNativeTypes(zone, field_native_types);
return &NativeUnionType::FromNativeTypes(zone, field_native_types);
}
}

// TODO(http://dartbug.com/42563): Implement AbiSpecificInt.
const NativeType* NativeType::FromAbstractType(Zone* zone,
const AbstractType& type,
const char** error) {
const classid_t class_id = type.type_class_id();
if (IsFfiPredefinedClassId(class_id)) {
return &NativeType::FromTypedDataClassId(zone, class_id);
}

// User-defined structs or unions.
const auto& cls = Class::Handle(zone, type.type_class());
const auto& superClass = Class::Handle(zone, cls.SuperClass());
const bool is_struct = String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::Struct());
const bool is_union = String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::Union());
RELEASE_ASSERT(is_struct || is_union);

auto& pragmas = Object::Handle(zone);
String& pragma_name = String::Handle(zone);
pragma_name = Symbols::vm_ffi_struct_fields().ptr();
Library::FindPragma(dart::Thread::Current(), /*only_core=*/false, cls,
pragma_name, /*multiple=*/true, &pragmas);
ASSERT(!pragmas.IsNull());
ASSERT(pragmas.IsGrowableObjectArray());
const auto& pragmas_array = GrowableObjectArray::Cast(pragmas);
auto& pragma = Instance::Handle(zone);
auto& clazz = Class::Handle(zone);
auto& library = Library::Handle(zone);
const String& class_symbol = Symbols::FfiStructLayout();
for (intptr_t i = 0; i < pragmas_array.Length(); i++) {
pragma ^= pragmas_array.At(i);
clazz ^= pragma.clazz();
library ^= clazz.library();
if (String::Handle(zone, clazz.UserVisibleName()).Equals(class_symbol) &&
String::Handle(zone, library.url()).Equals(Symbols::DartFfi())) {
break;
}
}

return CompoundFromPragma(zone, pragma, is_struct, error);
}
#endif

#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
Expand Down
6 changes: 4 additions & 2 deletions runtime/vm/compiler/ffi/native_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ class NativeCompoundType;
class NativeType : public ZoneAllocated {
public:
#if !defined(FFI_UNIT_TESTS)
static NativeType& FromAbstractType(Zone* zone, const AbstractType& type);
static const NativeType* FromAbstractType(Zone* zone,
const AbstractType& type,
const char** error);
#endif
static NativeType& FromTypedDataClassId(Zone* zone, classid_t class_id);
static const NativeType& FromTypedDataClassId(Zone* zone, classid_t class_id);

#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
static NativePrimitiveType& FromUnboxedRepresentation(Zone* zone,
Expand Down
Loading

0 comments on commit aa27868

Please sign in to comment.