[cfe/ffi] Support missing Abis in NativeTypeCfe

When ABI-specific integers are introduced, their mappings can be
partial. We need to account for this in the transformation and the code
we generate.

In the transformation, all sizes and offsets become nullable.
In the generated code we add `null` constants and a call to check
whether the value is not-null at runtime.

Note that with only this CL we can not generate nulls yet, because all
size and offset mappings are still complete.

TEST=pkg/front_end/testcases/nnbd/ffi*
TEST=tests/ffi*

Bug: https://github.com/dart-lang/sdk/issues/42563

Change-Id: I80d45f3f52001670bc0679a033f7daa22198d55e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221631
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Daco Harkes 2021-12-02 12:10:27 +00:00 committed by Commit Bot
parent eea0e48f6b
commit 734eb8e891
4 changed files with 174 additions and 65 deletions

View file

@ -234,6 +234,7 @@ class FfiTransformer extends Transformer {
final Procedure lookupFunctionTearoff;
final Procedure getNativeFieldFunction;
final Procedure reachabilityFenceFunction;
final Procedure checkAbiSpecificIntegerMappingFunction;
late final InterfaceType nativeFieldWrapperClass1Type;
late final InterfaceType voidType;
@ -417,7 +418,9 @@ class FfiTransformer extends Transformer {
getNativeFieldFunction = index.getTopLevelProcedure(
'dart:nativewrappers', '_getNativeField'),
reachabilityFenceFunction =
index.getTopLevelProcedure('dart:_internal', 'reachabilityFence') {
index.getTopLevelProcedure('dart:_internal', 'reachabilityFence'),
checkAbiSpecificIntegerMappingFunction = index.getTopLevelProcedure(
'dart:ffi', "_checkAbiSpecificIntegerMapping") {
nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
coreTypes, Nullability.nonNullable);
voidType = nativeTypesClasses[NativeType.kVoid]!
@ -453,7 +456,7 @@ class FfiTransformer extends Transformer {
/// [Bool] -> [bool]
/// [Void] -> [void]
/// [Pointer]<T> -> [Pointer]<T>
/// T extends [Pointer] -> T
/// T extends [Compound] -> T
/// [Handle] -> [Object]
/// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3)
/// where DartRepresentationOf(Tn) -> Sn
@ -535,27 +538,42 @@ class FfiTransformer extends Transformer {
InterfaceType _listOfIntType() => InterfaceType(
listClass, Nullability.legacy, [coreTypes.intLegacyRawType]);
ConstantExpression intListConstantExpression(List<int> values) =>
ConstantExpression intListConstantExpression(List<int?> values) =>
ConstantExpression(
ListConstant(coreTypes.intLegacyRawType,
[for (var v in values) IntConstant(v)]),
ListConstant(coreTypes.intLegacyRawType, [
for (var v in values)
if (v != null) IntConstant(v) else NullConstant()
]),
_listOfIntType());
/// Expression that queries VM internals at runtime to figure out on which ABI
/// we are.
Expression runtimeBranchOnLayout(Map<Abi, int> values) {
return InstanceInvocation(
Expression runtimeBranchOnLayout(Map<Abi, int?> values) {
final result = InstanceInvocation(
InstanceAccessKind.Instance,
intListConstantExpression([
for (final abi in Abi.values) values[abi]!,
for (final abi in Abi.values) values[abi],
]),
listElementAt.name,
Arguments([StaticInvocation(abiMethod, Arguments([]))]),
interfaceTarget: listElementAt,
functionType: Substitution.fromInterfaceType(_listOfIntType())
.substituteType(listElementAt.getterType) as FunctionType);
if (values.isPartial) {
return checkAbiSpecificIntegerMapping(result);
}
return result;
}
Expression checkAbiSpecificIntegerMapping(Expression nullableExpression) =>
StaticInvocation(
checkAbiSpecificIntegerMappingFunction,
Arguments(
[nullableExpression],
types: [InterfaceType(intClass, Nullability.nonNullable)],
),
);
/// Generates an expression that returns a new `Pointer<dartType>` offset
/// by [offset] from [pointer].
///
@ -819,3 +837,8 @@ bool importsFfi(Component component, List<Library> libraries) {
}
return false;
}
extension on Map<Abi, Object?> {
bool get isPartial =>
[for (final abi in Abi.values) this[abi]].contains(null);
}

View file

@ -448,9 +448,8 @@ class _FfiDefinitionTransformer extends FfiTransformer {
// This class is invalid, but continue reporting other errors on it.
success = false;
} else {
final DartType nativeType = InterfaceType(
nativeTypesClasses[_getFieldType(nativeTypeAnnos.first)!]!,
Nullability.legacy);
final DartType nativeType =
InterfaceType(nativeTypeAnnos.first, Nullability.legacy);
final DartType? shouldBeDartType = convertNativeTypeToDartType(
nativeType,
allowCompounds: true,
@ -704,7 +703,6 @@ class _FfiDefinitionTransformer extends FfiTransformer {
static const vmFfiStructFields = "vm:ffi:struct-fields";
// return value is nullable.
InstanceConstant? _compoundAnnotatedFields(Class node) {
for (final annotation in node.annotations) {
if (annotation is ConstantExpression) {
@ -774,7 +772,6 @@ class _FfiDefinitionTransformer extends FfiTransformer {
return UnionNativeTypeCfe(compoundClass, members);
}
// packing is `int?`.
void _annoteCompoundWithFields(
Class node, List<NativeTypeCfe> types, int? packing) {
List<Constant> constants =
@ -794,8 +791,13 @@ class _FfiDefinitionTransformer extends FfiTransformer {
InterfaceType(pragmaClass, Nullability.nonNullable, [])));
}
void _generateMethodsForField(Class node, Field field, NativeTypeCfe type,
Map<Abi, int> offsets, bool unalignedAccess, IndexedClass? indexedClass) {
void _generateMethodsForField(
Class node,
Field field,
NativeTypeCfe type,
Map<Abi, int?> offsets,
bool unalignedAccess,
IndexedClass? indexedClass) {
// TODO(johnniwinther): Avoid passing [indexedClass]. When compiling
// incrementally, [field] should already carry the references from
// [indexedClass].
@ -846,7 +848,7 @@ class _FfiDefinitionTransformer extends FfiTransformer {
/// If sizes are not supplied still emits a field so that the use site
/// transformer can still rewrite to it.
void _addSizeOfField(Class compound, IndexedClass? indexedClass,
[Map<Abi, int>? sizes = null]) {
[Map<Abi, int?>? sizes = null]) {
if (sizes == null) {
sizes = {for (var abi in Abi.values) abi: 0};
}

View file

@ -52,12 +52,12 @@ abstract class NativeTypeCfe {
}
/// The size in bytes per [Abi].
Map<Abi, int> get size;
Map<Abi, int?> get size;
/// The alignment inside structs in bytes per [Abi].
///
/// This is not the alignment on stack, this is only calculated in the VM.
Map<Abi, int> get alignment;
Map<Abi, int?> get alignment;
/// Generates a Constant representing the type which is consumed by the VM.
///
@ -70,7 +70,7 @@ abstract class NativeTypeCfe {
///
/// Takes [transformer] to be able to lookup classes and methods.
ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer);
Map<Abi, int?> offsets, bool unalignedAccess, FfiTransformer transformer);
/// Generates the return statement for a compound field setter with this type.
///
@ -78,7 +78,7 @@ abstract class NativeTypeCfe {
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer);
@ -90,7 +90,7 @@ class InvalidNativeTypeCfe implements NativeTypeCfe {
InvalidNativeTypeCfe(this.reason);
@override
Map<Abi, int> get alignment => throw reason;
Map<Abi, int?> get alignment => throw reason;
@override
Constant generateConstant(FfiTransformer transformer) => throw reason;
@ -99,7 +99,7 @@ class InvalidNativeTypeCfe implements NativeTypeCfe {
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) =>
throw reason;
@ -108,14 +108,14 @@ class InvalidNativeTypeCfe implements NativeTypeCfe {
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
throw reason;
@override
Map<Abi, int> get size => throw reason;
Map<Abi, int?> get size => throw reason;
}
class PrimitiveNativeTypeCfe implements NativeTypeCfe {
@ -126,7 +126,7 @@ class PrimitiveNativeTypeCfe implements NativeTypeCfe {
PrimitiveNativeTypeCfe(this.nativeType, this.clazz);
@override
Map<Abi, int> get size {
Map<Abi, int?> get size {
final int size = nativeTypeSizes[nativeType]!;
if (size == WORD_SIZE) {
return wordSize;
@ -147,7 +147,7 @@ class PrimitiveNativeTypeCfe implements NativeTypeCfe {
bool get isFloat =>
nativeType == NativeType.kFloat || nativeType == NativeType.kDouble;
bool isUnaligned(Map<Abi, int> offsets) {
bool isUnaligned(Map<Abi, int?> offsets) {
final alignments = alignment;
for (final abi in offsets.keys) {
final offset = offsets[abi]!;
@ -168,7 +168,7 @@ class PrimitiveNativeTypeCfe implements NativeTypeCfe {
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
@ -191,7 +191,7 @@ class PrimitiveNativeTypeCfe implements NativeTypeCfe {
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
@ -210,10 +210,10 @@ class PrimitiveNativeTypeCfe implements NativeTypeCfe {
class PointerNativeTypeCfe implements NativeTypeCfe {
@override
Map<Abi, int> get size => wordSize;
Map<Abi, int?> get size => wordSize;
@override
Map<Abi, int> get alignment => wordSize;
Map<Abi, int?> get alignment => wordSize;
@override
Constant generateConstant(FfiTransformer transformer) => TypeLiteralConstant(
@ -231,7 +231,7 @@ class PointerNativeTypeCfe implements NativeTypeCfe {
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
@ -259,7 +259,7 @@ class PointerNativeTypeCfe implements NativeTypeCfe {
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
@ -281,15 +281,15 @@ class PointerNativeTypeCfe implements NativeTypeCfe {
/// The layout of a `Struct` or `Union` in one [Abi].
class CompoundLayout {
/// Size of the entire struct or union.
final int size;
final int? size;
/// Alignment of struct or union when nested in a struct.
final int alignment;
final int? alignment;
/// Offset in bytes for each field, indexed by field number.
///
/// Always 0 for unions.
final List<int> offsets;
final List<int?> offsets;
CompoundLayout(this.size, this.alignment, this.offsets);
}
@ -304,11 +304,11 @@ abstract class CompoundNativeTypeCfe implements NativeTypeCfe {
CompoundNativeTypeCfe._(this.clazz, this.members, this.layout);
@override
Map<Abi, int> get size =>
Map<Abi, int?> get size =>
layout.map((abi, layout) => MapEntry(abi, layout.size));
@override
Map<Abi, int> get alignment =>
Map<Abi, int?> get alignment =>
layout.map((abi, layout) => MapEntry(abi, layout.alignment));
@override
@ -323,8 +323,12 @@ abstract class CompoundNativeTypeCfe implements NativeTypeCfe {
/// );
/// ```
@override
ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) {
final constructor = clazz.constructors
.firstWhere((c) => c.name == Name("#fromTypedDataBase"));
@ -351,7 +355,7 @@ abstract class CompoundNativeTypeCfe implements NativeTypeCfe {
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
@ -389,23 +393,23 @@ class StructNativeTypeCfe extends CompoundNativeTypeCfe {
// NativeStructType::FromNativeTypes.
static CompoundLayout _calculateLayout(
List<NativeTypeCfe> types, int? packing, Abi abi) {
int offset = 0;
final offsets = <int>[];
int structAlignment = 1;
int? offset = 0;
final offsets = <int?>[];
int? structAlignment = 1;
for (int i = 0; i < types.length; i++) {
final int size = types[i].size[abi]!;
int alignment = types[i].alignment[abi]!;
if (packing != null && packing < alignment) {
alignment = packing;
final int? size = types[i].size[abi];
int? alignment = types[i].alignment[abi];
if (packing != null) {
alignment = min(packing, alignment);
}
if (alignment > 0) {
offset = _alignOffset(offset, alignment);
if (alignment != null && alignment > 0) {
offset = offset.align(alignment);
}
offsets.add(offset);
offset += size;
structAlignment = math.max(structAlignment, alignment);
structAlignment = max(structAlignment, alignment);
}
final int size = _alignOffset(offset, structAlignment);
final int? size = offset.align(structAlignment);
return CompoundLayout(size, structAlignment, offsets);
}
}
@ -425,15 +429,15 @@ class UnionNativeTypeCfe extends CompoundNativeTypeCfe {
// Keep consistent with runtime/vm/compiler/ffi/native_type.cc
// NativeUnionType::FromNativeTypes.
static CompoundLayout _calculateLayout(List<NativeTypeCfe> types, Abi abi) {
int unionSize = 1;
int unionAlignment = 1;
int? unionSize = 1;
int? unionAlignment = 1;
for (int i = 0; i < types.length; i++) {
final int size = types[i].size[abi]!;
int alignment = types[i].alignment[abi]!;
unionSize = math.max(unionSize, size);
unionAlignment = math.max(unionAlignment, alignment);
final int? size = types[i].size[abi];
int? alignment = types[i].alignment[abi];
unionSize = max(unionSize, size);
unionAlignment = max(unionAlignment, alignment);
}
final int size = _alignOffset(unionSize, unionAlignment);
final int? size = unionSize.align(unionAlignment);
return CompoundLayout(size, unionAlignment, List.filled(types.length, 0));
}
}
@ -476,11 +480,11 @@ class ArrayNativeTypeCfe implements NativeTypeCfe {
}
@override
Map<Abi, int> get size =>
Map<Abi, int?> get size =>
elementType.size.map((abi, size) => MapEntry(abi, size * length));
@override
Map<Abi, int> get alignment => elementType.alignment;
Map<Abi, int?> get alignment => elementType.alignment;
// Note that we flatten multi dimensional arrays.
@override
@ -500,8 +504,12 @@ class ArrayNativeTypeCfe implements NativeTypeCfe {
/// );
/// ```
@override
ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) {
InterfaceType typeArgument =
(dartType as InterfaceType).typeArguments.single as InterfaceType;
return ReturnStatement(ConstructorInvocation(
@ -531,7 +539,7 @@ class ArrayNativeTypeCfe implements NativeTypeCfe {
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
@ -549,5 +557,71 @@ class ArrayNativeTypeCfe implements NativeTypeCfe {
..fileOffset = fileOffset);
}
int _alignOffset(int offset, int alignment) =>
((offset + alignment - 1) ~/ alignment) * alignment;
extension on int? {
int? align(int? alignment) =>
((this + alignment - 1) ~/ alignment) * alignment;
int? operator *(int? other) {
final this_ = this;
if (this_ == null) {
return null;
}
if (other == null) {
return null;
}
return this_ * other;
}
int? operator +(int? other) {
final this_ = this;
if (this_ == null) {
return null;
}
if (other == null) {
return null;
}
return this_ + other;
}
int? operator -(int? other) {
final this_ = this;
if (this_ == null) {
return null;
}
if (other == null) {
return null;
}
return this_ - other;
}
int? operator ~/(int? other) {
final this_ = this;
if (this_ == null) {
return null;
}
if (other == null) {
return null;
}
return this_ ~/ other;
}
}
int? max(int? a, int? b) {
if (a == null) {
return null;
}
if (b == null) {
return null;
}
return math.max(a, b);
}
int? min(int? a, int? b) {
if (a == null) {
return null;
}
if (b == null) {
return null;
}
return math.min(a, b);
}

View file

@ -448,6 +448,16 @@ Pointer<Pointer<S>> _elementAtPointer<S extends NativeType>(
Pointer<Pointer<S>> pointer, int index) =>
Pointer.fromAddress(pointer.address + _intPtrSize * index);
@pragma("vm:prefer-inline")
@pragma("vm:entry-point")
T _checkAbiSpecificIntegerMapping<T>(T? object) {
if (object == null) {
throw ArgumentError(
'AbiSpecificInteger is missing mapping for "${Abi.current()}".');
}
return object;
}
extension NativeFunctionPointer<NF extends Function>
on Pointer<NativeFunction<NF>> {
@patch