[vm/ffi] Optimize @Native calls

This CL removes static fields for storing the `@Native`'s function
addresses. Instead, the function addresses are stored in the object
pool for all archs except for ia32. ia32 has no AOT and no AppJit
snapshots, so the addresses are directly embedded in the code.

This CL removes the closure wrapping for `@Native`s. Instead of
`pointer.asFunctionInternal()()` where `asFunction` returns a closure
which contains the trampoline, the function is compiled to a body
which contains the trampoline `Native()`. This is possible for
`@Native`s because the dylib and symbol names are known statically.

Doing the compilation in kernel_to_il instead of a CFE transform
enables supporting static linking later. (The alternative would have
been to transform in the cfe to a `@pragma('vm:cachable-idempotent')`
instead of constructing the IL in kernel_to_il.

To enable running resolution in ia32 in kernel_to_il.cc, the
resolution function has been made available via
`runtime/lib/ffi_dynamic_library.h`.

Because the new calls are simply static calls, the TFA can figure
out const arguments flowing to these calls. This leads to constant
locations in the parameters to FfiCalls. So, this CL also introduces
logic to move constants into `NativeLocation`s.

TEST=runtime/vm/compiler/backend/il_test.cc
TEST=tests/ffi/function_*_native_(leaf_)test.dart
TEST=pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart

Closes: https://github.com/dart-lang/sdk/issues/47625
Closes: https://github.com/dart-lang/sdk/issues/51618
Change-Id: Ic5d017005dedcedea40c455c4d24dbe774f91603
CoreLibraryReviewExempt: Internal FFI implementation changes
Cq-Include-Trybots: luci.dart.try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64c-try,vm-aot-win-release-x64-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-x64-try,vm-kernel-linux-debug-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/284300
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Daco Harkes 2023-11-02 16:00:53 +00:00
parent 600acad478
commit e16bb210d2
34 changed files with 1345 additions and 898 deletions

View file

@ -1160,19 +1160,31 @@ class FfiTransformer extends Transformer {
]))
..fileOffset = fileOffset;
if (dartSignature is FunctionType) {
final returnType = dartSignature.returnType;
if (returnType is InterfaceType) {
final clazz = returnType.classNode;
if (clazz.superclass == structClass || clazz.superclass == unionClass) {
return invokeCompoundConstructor(asFunctionInternalInvocation, clazz);
}
}
final possibleCompoundReturn = findCompoundReturnType(dartSignature);
if (possibleCompoundReturn != null) {
return invokeCompoundConstructor(
asFunctionInternalInvocation, possibleCompoundReturn);
}
return asFunctionInternalInvocation;
}
/// Returns the compound [Class] if a compound is returned, otherwise `null`.
Class? findCompoundReturnType(DartType dartSignature) {
if (dartSignature is! FunctionType) {
return null;
}
final returnType = dartSignature.returnType;
if (returnType is! InterfaceType) {
return null;
}
final clazz = returnType.classNode;
if (clazz.superclass == structClass || clazz.superclass == unionClass) {
return clazz;
}
return null;
}
/// Returns
/// - `true` if leaf
/// - `false` if not leaf

View file

@ -183,81 +183,6 @@ class FfiNativeTransformer extends FfiTransformer {
);
}
// Create field holding the resolved native function pointer.
//
// For:
// @FfiNative<IntPtr Function(Pointer<Void>)>('DoXYZ', isLeaf:true)
// external int doXyz(NativeFieldWrapperClass1 obj);
//
// Create:
// static final _doXyz$FfiNative$ptr =
// Pointer<NativeFunction<IntPtr Function(Pointer<Void>)>>
// .fromAddress(_ffi_resolver('..', 'DoXYZ', 1))
// .asFunction<int Function(Pointer<Void>)>(isLeaf:true);
Field _createResolvedFfiNativeField(
String dartFunctionName,
StringConstant nativeFunctionName,
StringConstant? assetName,
bool isLeaf,
FunctionType dartFunctionType,
FunctionType ffiFunctionType,
int fileOffset,
Uri fileUri,
) {
// Derive number of arguments from the native function signature.
final numberNativeArgs = ffiFunctionType.positionalParameters.length;
final nativeFunctionType = InterfaceType(
nativeFunctionClass,
Nullability.legacy,
<DartType>[ffiFunctionType],
);
// _ffi_resolver('...', 'DoXYZ', 1)
final resolverInvocation = FunctionInvocation(
FunctionAccessKind.FunctionType,
StaticGet(resolverField),
Arguments(<Expression>[
ConstantExpression(
assetName ?? StringConstant(currentLibrary.importUri.toString())),
ConstantExpression(nativeFunctionName),
ConstantExpression(IntConstant(numberNativeArgs)),
]),
functionType: resolverField.type as FunctionType)
..fileOffset = fileOffset;
// _fromAddress<NativeFunction<Double Function(Double)>>(...)
final functionPointerExpression = StaticInvocation(
fromAddressInternal,
Arguments(
<Expression>[resolverInvocation],
types: [nativeFunctionType],
))
..fileOffset = fileOffset;
final asFunctionInvocation = buildAsFunctionInternal(
functionPointer: functionPointerExpression,
dartSignature: dartFunctionType,
nativeSignature: ffiFunctionType,
isLeaf: isLeaf,
fileOffset: fileOffset,
);
// static final _doXyz$FfiNative$Ptr = ...
final fieldName =
Name('_$dartFunctionName\$FfiNative\$Ptr', currentLibrary);
final functionPointerField = Field.immutable(fieldName,
type: dartFunctionType,
initializer: asFunctionInvocation,
isStatic: true,
isFinal: true,
fileUri: fileUri,
getterReference: currentLibraryIndex?.lookupGetterReference(fieldName))
..fileOffset = fileOffset;
return functionPointerField;
}
// Whether a parameter of [dartParameterType], passed as [ffiParameterType],
// needs to be converted to Pointer.
bool _requiresPointerConversion(
@ -350,7 +275,7 @@ class FfiNativeTransformer extends FfiTransformer {
// reachabilityFence(#t0);
// } => #t1
Expression _wrapArgumentsAndReturn({
required FunctionInvocation invocation,
required StaticInvocation invocation,
required FunctionType dartFunctionType,
required FunctionType ffiFunctionType,
bool checkReceiverForNullptr = false,
@ -487,6 +412,8 @@ class FfiNativeTransformer extends FfiTransformer {
return validSignature;
}
static const vmFfiNative = 'vm:ffi:native';
Procedure _transformProcedure(
Procedure node,
StringConstant nativeFunctionName,
@ -508,74 +435,135 @@ class FfiNativeTransformer extends FfiTransformer {
return node;
}
final parent = node.parent;
// static final _myMethod$FfiNative$Ptr = ..
final resolvedField = _createResolvedFfiNativeField(
'${node.name.text}\$${node.kind.name}',
nativeFunctionName,
assetName,
isLeaf,
wrappedDartFunctionType,
ffiFunctionType,
node.fileOffset,
node.fileUri,
final pragmaConstant = ConstantExpression(
InstanceConstant(pragmaClass.reference, [], {
pragmaName.fieldReference: StringConstant(vmFfiNative),
pragmaOptions.fieldReference: InstanceConstant(
nativeClass.reference,
[ffiFunctionType],
{
nativeSymbolField.fieldReference: nativeFunctionName,
nativeAssetField.fieldReference: assetName ??
StringConstant(currentLibrary.importUri.toString()),
nativeIsLeafField.fieldReference: BoolConstant(isLeaf),
},
)
}),
InterfaceType(
pragmaClass,
Nullability.nonNullable,
[],
),
);
// Add field to the parent the FfiNative function belongs to.
final possibleCompoundReturn = findCompoundReturnType(dartFunctionType);
if (dartFunctionType == wrappedDartFunctionType &&
node.isStatic &&
possibleCompoundReturn == null) {
// We are not wrapping/unwrapping arguments or return value.
node.addAnnotation(pragmaConstant);
return node;
}
// Introduce a new function as external with the annotation and give the
// current function a body that does the wrapping/unwrapping.
node.isExternal = false;
node.addAnnotation(ConstantExpression(
InstanceConstant(pragmaClass.reference, [], {
pragmaName.fieldReference: StringConstant("vm:prefer-inline"),
pragmaOptions.fieldReference: NullConstant(),
}),
));
final parent = node.parent;
var fileUri = currentLibrary.fileUri;
if (parent is Class) {
parent.addField(resolvedField);
fileUri = parent.fileUri;
} else if (parent is Library) {
parent.addField(resolvedField);
fileUri = parent.fileUri;
}
int varCounter = 0;
final nonWrappedFfiNative = Procedure(
Name('_${node.name.text}\$${node.kind.name}\$FfiNative', currentLibrary),
ProcedureKind.Method,
FunctionNode(
/*body=*/ null,
requiredParameterCount: wrappedDartFunctionType.requiredParameterCount,
positionalParameters: [
for (final positionalParameter
in wrappedDartFunctionType.positionalParameters)
VariableDeclaration(
/*name=*/ '#t${varCounter++}',
type: positionalParameter,
)..fileOffset = node.fileOffset,
],
returnType: wrappedDartFunctionType.returnType,
)..fileOffset = node.fileOffset,
fileUri: fileUri,
isStatic: true,
isExternal: true,
)
..isNonNullableByDefault = node.isNonNullableByDefault
..fileOffset = node.fileOffset;
nonWrappedFfiNative.addAnnotation(pragmaConstant);
// Add procedure to the parent the FfiNative function belongs to.
if (parent is Class) {
parent.addProcedure(nonWrappedFfiNative);
} else if (parent is Library) {
parent.addProcedure(nonWrappedFfiNative);
} else {
throw 'Unexpected parent of @FfiNative function. '
throw 'Unexpected parent of @Native function. '
'Expected Class or Library, but found ${parent}.';
}
// _myFunction$FfiNative$Ptr(obj, x)
final functionPointerInvocation = FunctionInvocation(
FunctionAccessKind.FunctionType,
StaticGet(resolvedField),
Arguments(argumentList),
functionType: wrappedDartFunctionType)
..fileOffset = node.fileOffset;
final nonWrappedInvocation = StaticInvocation(
nonWrappedFfiNative,
Arguments(argumentList),
)..fileOffset = node.fileOffset;
Expression result = (wrappedDartFunctionType == dartFunctionType
? functionPointerInvocation
? nonWrappedInvocation
: _wrapArgumentsAndReturn(
invocation: functionPointerInvocation,
invocation: nonWrappedInvocation,
dartFunctionType: dartFunctionType,
ffiFunctionType: ffiFunctionType,
checkReceiverForNullptr: checkReceiverForNullptr,
));
if (possibleCompoundReturn != null) {
result = invokeCompoundConstructor(result, possibleCompoundReturn);
}
// => _myFunction$FfiNative$Ptr(
// Pointer<Void>.fromAddress(_getNativeField(obj)), x)
node.function.body = ReturnStatement(result)..parent = node.function;
return node;
}
// Transform FfiNative instance methods.
//
// Example:
//
// class MyNativeClass extends NativeFieldWrapperClass1 {
// @FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('MyClass_MyMethod')
// @Native<IntPtr Function(Pointer<Void>, IntPtr)>(symbol: 'MyClass_MyMethod')
// external int myMethod(int x);
// }
//
// Becomes, roughly:
// ... {
// static final _myMethod$FfiNative$Ptr = ...
// static _myMethod$FfiNative(MyNativeClass self, int x)
// => _myMethod$FfiNative$Ptr(
// Pointer<Void>.fromAddress(_getNativeField(self)), x);
// int myMethod(int x) => _myMethod$FfiNative(this, x);
// }
//
// ... {
// static final _myMethod$FfiNative$Ptr = ...
// int myMethod(int x)
// => _myMethod$FfiNative$Ptr(
// Pointer<Void>.fromAddress(_getNativeField(this)), x);
// @pragma(
// 'vm:ffi:native',
// Native<IntPtr Function(Pointer<Void>, IntPtr)>(
// symbol: 'MyClass_MyMethod',
// assetId: '<library uri>',
// ),
// )
// external int _myFunction$FfiNative(Pointer<Void> self, int x);
//
// @pragma('vm:prefer-inline')
// int myMethod(int x) => _myMethod$FfiNative(_getNativeField(this), x);
// }
Procedure _transformInstanceMethod(
Procedure node,
@ -609,13 +597,26 @@ class FfiNativeTransformer extends FfiTransformer {
}
// Transform FfiNative static functions.
//
// Example:
// @FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('MyFunction')
//
// @Native<IntPtr Function(Pointer<Void>, IntPtr)>(symbol: 'MyFunction')
// external int myFunction(MyNativeClass obj, int x);
//
// Becomes, roughly:
// static final _myFunction$FfiNative$Ptr = ...
//
// @pragma(
// 'vm:ffi:native',
// Native<IntPtr Function(Pointer<Void>, IntPtr)>(
// symbol: 'MyFunction',
// assetId: '<library uri>',
// ),
// )
// external int _myFunction$FfiNative(Pointer<Void> self, int x);
//
// @pragma('vm:prefer-inline')
// int myFunction(MyNativeClass obj, int x)
// => myFunction$FfiNative$Ptr(
// => _myFunction$FfiNative(
// Pointer<Void>.fromAddress(_getNativeField(obj)), x);
Procedure _transformStaticFunction(
Procedure node,
@ -648,7 +649,7 @@ class FfiNativeTransformer extends FfiTransformer {
@override
visitProcedure(Procedure node) {
// Only transform functions that are external and have FfiNative annotation:
// @FfiNative<Double Function(Double)>('Math_sqrt')
// @Native<Double Function(Double)>(symbol: 'Math_sqrt')
// external double _square_root(double x);
final ffiNativeAnnotation =
tryGetNativeAnnotation(node) ?? tryGetFfiNativeAnnotation(node);
@ -661,7 +662,6 @@ class FfiNativeTransformer extends FfiTransformer {
1, node.location?.file);
return node;
}
node.isExternal = false;
node.annotations.remove(ffiNativeAnnotation);

View file

@ -352,7 +352,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
.where((c) =>
c.superclass == structClass || c.superclass == unionClass)
.toList();
return _invokeCompoundConstructors(replacement, compoundClasses);
return invokeCompoundConstructors(replacement, compoundClasses);
} else if (target == allocateMethod) {
final DartType nativeType = node.arguments.types[0];
@ -387,7 +387,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
return node;
}
Expression _invokeCompoundConstructors(
Expression invokeCompoundConstructors(
Expression nestedExpression, List<Class> compoundClasses) =>
compoundClasses
.distinct()
@ -748,7 +748,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
.map((t) => t.classNode)
.where((c) => c.superclass == structClass || c.superclass == unionClass)
.toList();
return _invokeCompoundConstructors(replacement, compoundClasses);
return invokeCompoundConstructors(replacement, compoundClasses);
}
Expression _replaceGetRef(StaticInvocation node) {

View file

@ -1,129 +1,144 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:nativewrappers" as nat;
import "dart:ffi" as ffi;
import "dart:_internal" as _in;
import "dart:ffi";
import "dart:nativewrappers";
abstract class Classy extends core::Object {
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (core::int) → core::int _returnIntPtrStatic$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.unboxing-info.metadata=()->i] static method returnIntPtrStatic() → core::int
return self::Classy::_returnIntPtrStatic$Method$FfiNative$Ptr(#C4){(core::int) → core::int};
[@vm.unboxing-info.metadata=(i)->i] @#C6
external static method returnIntPtrStatic([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 222)] core::int x) → core::int;
}
class NativeClassy extends nat::NativeFieldWrapperClass1 {
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>, core::int) → void _goodHasReceiverPointer$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::int) → void, (ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (self::NativeClassy, core::int) → void _goodHasReceiverHandle$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::NativeClassy, core::int) → void, (ffi::Handle, ffi::IntPtr) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::IntPtr) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (self::NativeClassy, ffi::Pointer<ffi::Void>) → void _goodHasReceiverHandleAndPtr$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::NativeClassy, ffi::Pointer<ffi::Void>) → void, (ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (self::NativeClassy, self::NativeClassy) → void _goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::NativeClassy, self::NativeClassy) → void, (ffi::Handle, ffi::Handle) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::Handle) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>, self::NativeClassy) → void _goodHasReceiverPtrAndHandle$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, self::NativeClassy) → void, (ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>, core::bool) → core::Object? _meh$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → core::Object?, (ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>) → core::bool _blah$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void>) → ffi::Bool>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Bool>*>(ffi::_ffi_resolver(#C1, #C5, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>) → core::bool _myField$Getter$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void>) → ffi::Bool>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Bool>*>(ffi::_ffi_resolver(#C1, #C5, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>, core::bool) → void _myField$Setter$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → void, (ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
synthetic constructor •() → self::NativeClassy
: super nat::NativeFieldWrapperClass1::•()
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method goodHasReceiverPointer() → void
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] @#C9
method goodHasReceiverPointer() → void
return block {
final nat::NativeFieldWrapperClass1 #t1 = this;
final core::int #t2 = #C7;
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final core::int #t2 = #C10;
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t1);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
core::StateError::_throwNew(#C9);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
core::StateError::_throwNew(#C12);
else
;
} =>#pointerAddress), #t2){(ffi::Pointer<ffi::Void>, core::int) → void};
} =>#pointerAddress), #t2);
_in::reachabilityFence(#t1);
} =>#t3;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] method goodHasReceiverHandle() → void
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative$Ptr(this, #C7){(self::NativeClassy, core::int) → void};
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] method goodHasReceiverHandleAndPtr([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] @#C9
method goodHasReceiverHandle() → void
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative(this, #C10);
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] @#C9
method goodHasReceiverHandleAndPtr([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
return block {
final self::NativeClassy #t4 = this;
final nat::NativeFieldWrapperClass1 #t5 = v;
final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$Method$FfiNative$Ptr(#t4, ffi::_fromAddress<ffi::Void>([@vm.inferred-type.metadata=int] nat::_getNativeField(#t5))){(self::NativeClassy, ffi::Pointer<ffi::Void>) → void};
final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$Method$FfiNative(#t4, [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>([@vm.inferred-type.metadata=int] nat::_getNativeField(#t5)));
_in::reachabilityFence(#t5);
} =>#t6;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] method goodHasReceiverHandleAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr(this, v){(self::NativeClassy, self::NativeClassy) → void};
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10] method goodHasReceiverPtrAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] @#C9
method goodHasReceiverHandleAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative(this, v);
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10] @#C9
method goodHasReceiverPtrAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
return block {
final nat::NativeFieldWrapperClass1 #t7 = this;
final self::NativeClassy #t8 = v;
final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$Method$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t7);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
core::StateError::_throwNew(#C9);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
core::StateError::_throwNew(#C12);
else
;
} =>#pointerAddress), #t8){(ffi::Pointer<ffi::Void>, self::NativeClassy) → void};
} =>#pointerAddress), #t8);
_in::reachabilityFence(#t7);
} =>#t9;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12] method meh() → core::String?
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12] @#C9
method meh() → core::String?
return block {
final nat::NativeFieldWrapperClass1 #t10 = this;
final core::bool #t11 = #C10;
final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final core::bool #t11 = #C13;
final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$Method$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t10);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
core::StateError::_throwNew(#C9);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
core::StateError::_throwNew(#C12);
else
;
} =>#pointerAddress), #t11){(ffi::Pointer<ffi::Void>, core::bool) → core::Object?});
} =>#pointerAddress), #t11));
_in::reachabilityFence(#t10);
} =>#t12;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:13,getterSelectorId:14] method blah() → core::bool
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:13,getterSelectorId:14] @#C9
method blah() → core::bool
return block {
final nat::NativeFieldWrapperClass1 #t13 = this;
final core::bool #t14 = self::NativeClassy::_blah$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final core::bool #t14 = [@vm.inferred-type.metadata=dart.core::bool] self::NativeClassy::_blah$Method$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t13);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
core::StateError::_throwNew(#C9);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
core::StateError::_throwNew(#C12);
else
;
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
} =>#pointerAddress));
_in::reachabilityFence(#t13);
} =>#t14;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] get myField() → core::bool
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] @#C9
get myField() → core::bool
return block {
final nat::NativeFieldWrapperClass1 #t15 = this;
final core::bool #t16 = self::NativeClassy::_myField$Getter$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final core::bool #t16 = [@vm.inferred-type.metadata=dart.core::bool] self::NativeClassy::_myField$Getter$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t15);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
core::StateError::_throwNew(#C9);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
core::StateError::_throwNew(#C12);
else
;
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
} =>#pointerAddress));
_in::reachabilityFence(#t15);
} =>#t16;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] set myField([@vm.inferred-arg-type.metadata=dart.core::bool] core::bool value) → void
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] @#C9
set myField([@vm.inferred-arg-type.metadata=dart.core::bool] core::bool value) → void
return block {
final nat::NativeFieldWrapperClass1 #t17 = this;
final core::bool #t18 = value;
final void #t19 = self::NativeClassy::_myField$Setter$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final void #t19 = self::NativeClassy::_myField$Setter$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t17);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
core::StateError::_throwNew(#C9);
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
core::StateError::_throwNew(#C12);
else
;
} =>#pointerAddress), #t18){(ffi::Pointer<ffi::Void>, core::bool) → void};
} =>#pointerAddress), #t18);
_in::reachabilityFence(#t17);
} =>#t19;
[@vm.unboxing-info.metadata=(b,i)->b] @#C16
external static method _goodHasReceiverPointer$Method$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 175)] core::int #t1) → void;
[@vm.unboxing-info.metadata=(b,i)->b] @#C18
external static method _goodHasReceiverHandle$Method$FfiNative([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t0, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 175)] core::int #t1) → void;
@#C20
external static method _goodHasReceiverHandleAndPtr$Method$FfiNative([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t0, [@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t1) → void;
@#C22
external static method _goodHasReceiverHandleAndHandle$Method$FfiNative([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t0, [@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t1) → void;
@#C24
external static method _goodHasReceiverPtrAndHandle$Method$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0, [@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t1) → void;
@#C26
external static method _meh$Method$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0, [@vm.inferred-arg-type.metadata=dart.core::bool (value: true)] core::bool #t1) → core::Object?;
@#C28
external static method _blah$Method$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0) → core::bool;
@#C30
external static method _myField$Getter$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0) → core::bool;
@#C32
external static method _myField$Setter$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0, [@vm.inferred-arg-type.metadata=dart.core::bool] core::bool #t1) → void;
}
[@vm.inferred-type.metadata=dart.core::_Closure]static final field (core::int) → core::int _returnIntPtr$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
[@vm.inferred-type.metadata=dart.core::_Closure]static final field (core::int) → core::int _returnIntPtrLeaf$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
[@vm.unboxing-info.metadata=()->i]static method returnIntPtr() → core::int
return self::_returnIntPtr$Method$FfiNative$Ptr(#C11){(core::int) → core::int};
[@vm.unboxing-info.metadata=()->i]static method returnIntPtrLeaf() → core::int
return self::_returnIntPtrLeaf$Method$FfiNative$Ptr(#C12){(core::int) → core::int};
[@vm.unboxing-info.metadata=(i)->i]@#C6
external static method returnIntPtr([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 13)] core::int x) → core::int;
[@vm.unboxing-info.metadata=(i)->i]@#C34
external static method returnIntPtrLeaf([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 37)] core::int x) → core::int;
static method main() → void {
self::returnIntPtr();
self::returnIntPtrLeaf();
self::Classy::returnIntPtrStatic();
self::returnIntPtr(13);
self::returnIntPtrLeaf(37);
self::Classy::returnIntPtrStatic(222);
[@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverPointer] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverPointer}(){(core::int) → void};
[@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverHandle] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandle}(){(core::int) → void};
[@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverHandleAndPtr] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandleAndPtr}(new self::NativeClassy::•()){(self::NativeClassy) → void};
@ -135,16 +150,38 @@ static method main() → void {
[@vm.direct-call.metadata=#lib::NativeClassy.myField] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::myField} = !b;
}
constants {
#C1 = "#lib"
#C1 = "vm:ffi:native"
#C2 = "ReturnIntPtr"
#C3 = 1
#C4 = 222
#C5 = "doesntmatter"
#C6 = 2
#C7 = 175
#C8 = 0
#C9 = "A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed."
#C10 = true
#C11 = 13
#C12 = 37
#C3 = "#lib"
#C4 = false
#C5 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = "vm:prefer-inline"
#C8 = null
#C9 = core::pragma {name:#C7, options:#C8}
#C10 = 175
#C11 = 0
#C12 = "A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed."
#C13 = true
#C14 = "doesntmatter"
#C15 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
#C16 = core::pragma {name:#C1, options:#C15}
#C17 = ffi::Native<(ffi::Handle, ffi::IntPtr) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
#C18 = core::pragma {name:#C1, options:#C17}
#C19 = ffi::Native<(ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
#C20 = core::pragma {name:#C1, options:#C19}
#C21 = ffi::Native<(ffi::Handle, ffi::Handle) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
#C22 = core::pragma {name:#C1, options:#C21}
#C23 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
#C24 = core::pragma {name:#C1, options:#C23}
#C25 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle> {symbol:#C14, assetId:#C3, isLeaf:#C4}
#C26 = core::pragma {name:#C1, options:#C25}
#C27 = ffi::Native<(ffi::Pointer<ffi::Void>) → ffi::Bool> {symbol:#C14, assetId:#C3, isLeaf:#C4}
#C28 = core::pragma {name:#C1, options:#C27}
#C29 = ffi::Native<(ffi::Pointer<ffi::Void>) → ffi::Bool> {symbol:#C14, assetId:#C3, isLeaf:#C13}
#C30 = core::pragma {name:#C1, options:#C29}
#C31 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C13}
#C32 = core::pragma {name:#C1, options:#C31}
#C33 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C13}
#C34 = core::pragma {name:#C1, options:#C33}
}

View file

@ -1,128 +1,143 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:nativewrappers" as nat;
import "dart:ffi" as ffi;
import "dart:_internal" as _in;
import "dart:ffi";
import "dart:nativewrappers";
class Classy extends core::Object {
static final field (core::int) → core::int _returnIntPtrStatic$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
synthetic constructor •() → self::Classy
: super core::Object::•()
;
static method returnIntPtrStatic(core::int x) → core::int
return self::Classy::_returnIntPtrStatic$Method$FfiNative$Ptr(x){(core::int) → core::int};
@#C6
external static method returnIntPtrStatic(core::int x) → core::int;
}
class NativeClassy extends nat::NativeFieldWrapperClass1 {
static final field (ffi::Pointer<ffi::Void>, core::int) → void _goodHasReceiverPointer$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::int) → void, (ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static final field (self::NativeClassy, core::int) → void _goodHasReceiverHandle$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, core::int) → void, (ffi::Handle, ffi::IntPtr) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::IntPtr) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static final field (self::NativeClassy, ffi::Pointer<ffi::Void>) → void _goodHasReceiverHandleAndPtr$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, ffi::Pointer<ffi::Void>) → void, (ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static final field (self::NativeClassy, self::NativeClassy) → void _goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, self::NativeClassy) → void, (ffi::Handle, ffi::Handle) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::Handle) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static final field (ffi::Pointer<ffi::Void>, self::NativeClassy) → void _goodHasReceiverPtrAndHandle$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, self::NativeClassy) → void, (ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static final field (ffi::Pointer<ffi::Void>, core::bool) → core::Object? _meh$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → core::Object?, (ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static final field (ffi::Pointer<ffi::Void>) → core::bool _blah$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void>) → ffi::Bool>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Bool>*>(ffi::_ffi_resolver(#C1, #C4, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static final field (ffi::Pointer<ffi::Void>) → core::bool _myField$Getter$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void>) → ffi::Bool>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Bool>*>(ffi::_ffi_resolver(#C1, #C4, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
static final field (ffi::Pointer<ffi::Void>, core::bool) → void _myField$Setter$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → void, (ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
synthetic constructor •() → self::NativeClassy
: super nat::NativeFieldWrapperClass1::•()
;
@#C9
method goodHasReceiverPointer(core::int v) → void
return block {
final nat::NativeFieldWrapperClass1 #t1 = this;
final core::int #t2 = v;
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative(ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = nat::_getNativeField(#t1);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
core::StateError::_throwNew(#C7);
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C11);
else
;
} =>#pointerAddress), #t2){(ffi::Pointer<ffi::Void>, core::int) → void};
} =>#pointerAddress), #t2);
_in::reachabilityFence(#t1);
} =>#t3;
@#C9
method goodHasReceiverHandle(core::int v) → void
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative$Ptr(this, v){(self::NativeClassy, core::int) → void};
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative(this, v);
@#C9
method goodHasReceiverHandleAndPtr(self::NativeClassy v) → void
return block {
final self::NativeClassy #t4 = this;
final nat::NativeFieldWrapperClass1 #t5 = v;
final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$Method$FfiNative$Ptr(#t4, ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t5))){(self::NativeClassy, ffi::Pointer<ffi::Void>) → void};
final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$Method$FfiNative(#t4, ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t5)));
_in::reachabilityFence(#t5);
} =>#t6;
@#C9
method goodHasReceiverHandleAndHandle(self::NativeClassy v) → void
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr(this, v){(self::NativeClassy, self::NativeClassy) → void};
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative(this, v);
@#C9
method goodHasReceiverPtrAndHandle(self::NativeClassy v) → void
return block {
final nat::NativeFieldWrapperClass1 #t7 = this;
final self::NativeClassy #t8 = v;
final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$Method$FfiNative(ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = nat::_getNativeField(#t7);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
core::StateError::_throwNew(#C7);
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C11);
else
;
} =>#pointerAddress), #t8){(ffi::Pointer<ffi::Void>, self::NativeClassy) → void};
} =>#pointerAddress), #t8);
_in::reachabilityFence(#t7);
} =>#t9;
@#C9
method meh(core::bool blah) → core::String?
return block {
final nat::NativeFieldWrapperClass1 #t10 = this;
final core::bool #t11 = blah;
final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$Method$FfiNative(ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = nat::_getNativeField(#t10);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
core::StateError::_throwNew(#C7);
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C11);
else
;
} =>#pointerAddress), #t11){(ffi::Pointer<ffi::Void>, core::bool) → core::Object?});
} =>#pointerAddress), #t11));
_in::reachabilityFence(#t10);
} =>#t12;
@#C9
method blah() → core::bool
return block {
final nat::NativeFieldWrapperClass1 #t13 = this;
final core::bool #t14 = self::NativeClassy::_blah$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final core::bool #t14 = self::NativeClassy::_blah$Method$FfiNative(ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = nat::_getNativeField(#t13);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
core::StateError::_throwNew(#C7);
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C11);
else
;
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
} =>#pointerAddress));
_in::reachabilityFence(#t13);
} =>#t14;
@#C9
get myField() → core::bool
return block {
final nat::NativeFieldWrapperClass1 #t15 = this;
final core::bool #t16 = self::NativeClassy::_myField$Getter$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final core::bool #t16 = self::NativeClassy::_myField$Getter$FfiNative(ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = nat::_getNativeField(#t15);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
core::StateError::_throwNew(#C7);
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C11);
else
;
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
} =>#pointerAddress));
_in::reachabilityFence(#t15);
} =>#t16;
@#C9
set myField(core::bool value) → void
return block {
final nat::NativeFieldWrapperClass1 #t17 = this;
final core::bool #t18 = value;
final void #t19 = self::NativeClassy::_myField$Setter$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
final void #t19 = self::NativeClassy::_myField$Setter$FfiNative(ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = nat::_getNativeField(#t17);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
core::StateError::_throwNew(#C7);
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C11);
else
;
} =>#pointerAddress), #t18){(ffi::Pointer<ffi::Void>, core::bool) → void};
} =>#pointerAddress), #t18);
_in::reachabilityFence(#t17);
} =>#t19;
@#C14
external static method _goodHasReceiverPointer$Method$FfiNative(ffi::Pointer<ffi::Void> #t0, core::int #t1) → void;
@#C16
external static method _goodHasReceiverHandle$Method$FfiNative(self::NativeClassy #t0, core::int #t1) → void;
@#C18
external static method _goodHasReceiverHandleAndPtr$Method$FfiNative(self::NativeClassy #t0, ffi::Pointer<ffi::Void> #t1) → void;
@#C20
external static method _goodHasReceiverHandleAndHandle$Method$FfiNative(self::NativeClassy #t0, self::NativeClassy #t1) → void;
@#C22
external static method _goodHasReceiverPtrAndHandle$Method$FfiNative(ffi::Pointer<ffi::Void> #t0, self::NativeClassy #t1) → void;
@#C24
external static method _meh$Method$FfiNative(ffi::Pointer<ffi::Void> #t0, core::bool #t1) → core::Object?;
@#C26
external static method _blah$Method$FfiNative(ffi::Pointer<ffi::Void> #t0) → core::bool;
@#C29
external static method _myField$Getter$FfiNative(ffi::Pointer<ffi::Void> #t0) → core::bool;
@#C31
external static method _myField$Setter$FfiNative(ffi::Pointer<ffi::Void> #t0, core::bool #t1) → void;
}
static final field (core::int) → core::int _returnIntPtr$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static final field (core::int) → core::int _returnIntPtrLeaf$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
static method returnIntPtr(core::int x) → core::int
return self::_returnIntPtr$Method$FfiNative$Ptr(x){(core::int) → core::int};
static method returnIntPtrLeaf(core::int x) → core::int
return self::_returnIntPtrLeaf$Method$FfiNative$Ptr(x){(core::int) → core::int};
@#C6
external static method returnIntPtr(core::int x) → core::int;
@#C33
external static method returnIntPtrLeaf(core::int x) → core::int;
static method main() → void {
self::returnIntPtr(13);
self::returnIntPtrLeaf(37);
@ -138,11 +153,37 @@ static method main() → void {
new self::NativeClassy::•().{self::NativeClassy::myField} = !b;
}
constants {
#C1 = "#lib"
#C1 = "vm:ffi:native"
#C2 = "ReturnIntPtr"
#C3 = 1
#C4 = "doesntmatter"
#C5 = 2
#C6 = 0
#C7 = "A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed."
#C3 = "#lib"
#C4 = false
#C5 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = "vm:prefer-inline"
#C8 = null
#C9 = core::pragma {name:#C7, options:#C8}
#C10 = 0
#C11 = "A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed."
#C12 = "doesntmatter"
#C13 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
#C14 = core::pragma {name:#C1, options:#C13}
#C15 = ffi::Native<(ffi::Handle, ffi::IntPtr) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
#C16 = core::pragma {name:#C1, options:#C15}
#C17 = ffi::Native<(ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
#C18 = core::pragma {name:#C1, options:#C17}
#C19 = ffi::Native<(ffi::Handle, ffi::Handle) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
#C20 = core::pragma {name:#C1, options:#C19}
#C21 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
#C22 = core::pragma {name:#C1, options:#C21}
#C23 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle> {symbol:#C12, assetId:#C3, isLeaf:#C4}
#C24 = core::pragma {name:#C1, options:#C23}
#C25 = ffi::Native<(ffi::Pointer<ffi::Void>) → ffi::Bool> {symbol:#C12, assetId:#C3, isLeaf:#C4}
#C26 = core::pragma {name:#C1, options:#C25}
#C27 = true
#C28 = ffi::Native<(ffi::Pointer<ffi::Void>) → ffi::Bool> {symbol:#C12, assetId:#C3, isLeaf:#C27}
#C29 = core::pragma {name:#C1, options:#C28}
#C30 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C27}
#C31 = core::pragma {name:#C1, options:#C30}
#C32 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C27}
#C33 = core::pragma {name:#C1, options:#C32}
}

View file

@ -17,15 +17,17 @@ final class Struct1ByteInt extends ffi::Struct {
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3] method toString() → core::String
return "(${[@vm.direct-call.metadata=#lib::Struct1ByteInt.a0] this.{self::Struct1ByteInt::a0}{core::int}})";
}
[@vm.inferred-type.metadata=dart.core::_Closure]static final field (core::int) → self::Struct1ByteInt _returnStruct1ByteIntNative$Method$FfiNative$Ptr = block {
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C9)));
} =>[@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(core::int) → self::Struct1ByteInt, (ffi::Int8) → self::Struct1ByteInt>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Int8) → self::Struct1ByteInt>*>(ffi::_ffi_resolver(#C10, #C11, #C9){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static method main() → void {
final self::Struct1ByteInt result = self::returnStruct1ByteIntNative([@vm.direct-call.metadata=dart.core::_IntegerImplementation.unary-] [@vm.inferred-type.metadata=int (skip check)] 1.{core::int::unary-}(){() → core::int});
core::print("result = ${result}");
}
[@vm.unboxing-info.metadata=(i)->b]static method returnStruct1ByteIntNative([@vm.inferred-arg-type.metadata=int] core::int a0) → self::Struct1ByteInt
return self::_returnStruct1ByteIntNative$Method$FfiNative$Ptr(a0){(core::int) → self::Struct1ByteInt};
[@vm.unboxing-info.metadata=(i)->b]@#C10
static method returnStruct1ByteIntNative([@vm.inferred-arg-type.metadata=int] core::int a0) → self::Struct1ByteInt
return block {
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C11)));
} =>[@vm.inferred-type.metadata=#lib::Struct1ByteInt] self::_returnStruct1ByteIntNative$Method$FfiNative(a0);
[@vm.unboxing-info.metadata=(i)->b]@#C17
external static method _returnStruct1ByteIntNative$Method$FfiNative([@vm.inferred-arg-type.metadata=int] core::int #t0) → self::Struct1ByteInt;
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8)
@ -35,7 +37,13 @@ constants {
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = 0
#C8 = <core::int*>[#C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7]
#C9 = 1
#C10 = "#lib"
#C11 = "ReturnStruct1ByteInt"
#C9 = "vm:prefer-inline"
#C10 = core::pragma {name:#C9, options:#C4}
#C11 = 1
#C12 = "vm:ffi:native"
#C13 = "ReturnStruct1ByteInt"
#C14 = "#lib"
#C15 = false
#C16 = ffi::Native<(ffi::Int8) → self::Struct1ByteInt> {symbol:#C13, assetId:#C14, isLeaf:#C15}
#C17 = core::pragma {name:#C12, options:#C16}
}

View file

@ -27,15 +27,17 @@ final class Struct1ByteInt extends ffi::Struct {
static get #sizeOf() → core::int*
return #C13.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
static final field (core::int) → self::Struct1ByteInt _returnStruct1ByteIntNative$Method$FfiNative$Ptr = block {
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase(typ::Uint8List::•(#C12)));
} =>ffi::_asFunctionInternal<(core::int) → self::Struct1ByteInt, (ffi::Int8) → self::Struct1ByteInt>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Int8) → self::Struct1ByteInt>*>(ffi::_ffi_resolver(#C14, #C15, #C12){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static method main() → void {
final self::Struct1ByteInt result = self::returnStruct1ByteIntNative(1.{core::int::unary-}(){() → core::int});
core::print("result = ${result}");
}
@#C11
static method returnStruct1ByteIntNative(core::int a0) → self::Struct1ByteInt
return self::_returnStruct1ByteIntNative$Method$FfiNative$Ptr(a0){(core::int) → self::Struct1ByteInt};
return block {
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase(typ::Uint8List::•(#C12)));
} =>self::_returnStruct1ByteIntNative$Method$FfiNative(a0);
@#C19
external static method _returnStruct1ByteIntNative$Method$FfiNative(core::int #t0) → self::Struct1ByteInt;
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8)
@ -50,6 +52,10 @@ constants {
#C11 = core::pragma {name:#C10, options:#C4}
#C12 = 1
#C13 = <core::int*>[#C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12]
#C14 = "#lib"
#C14 = "vm:ffi:native"
#C15 = "ReturnStruct1ByteInt"
#C16 = "#lib"
#C17 = false
#C18 = ffi::Native<(ffi::Int8) → self::Struct1ByteInt> {symbol:#C15, assetId:#C16, isLeaf:#C17}
#C19 = core::pragma {name:#C14, options:#C18}
}

View file

@ -22,3 +22,26 @@ Related files:
* [pkg/vm/test/native_assets_validator_test.dart](../../../pkg/vm/test/native_assets_validator_test.dart)
* [runtime/lib/ffi_dynamic_library.cc](../../../runtime/lib/ffi_dynamic_library.cc)
* [runtime/vm/ffi/native_assets.cc](../../../runtime/vm/ffi/native_assets.cc)
## Native
The fully expanded version of `@Native` passed to the VM.
```
@pragma(
'vm:ffi:native',
Native<IntPtr Function(Pointer<Void>, IntPtr)>(
symbol: 'MyClass_MyMethod',
assetId: '<library uri>',
),
)
external int _myFunction$FfiNative(Pointer<Void> self, int x);
```
This is passed as a pragma so it is treated consistently with other pragmas.
Related files:
* [runtime/vm/kernel_loader.cc](../../../runtime/vm/kernel_loader.cc)
* [runtime/vm/object.cc](../../../runtime/vm/object.cc)

View file

@ -48,6 +48,7 @@ These pragma's are only used on AST nodes synthesized by us, so users defining t
| Pragma | Meaning |
| --- | --- |
| `vm:ffi:native-assets` | [Passing a native assets mapping to the VM](compiler/ffi_pragmas.md) |
| `vm:ffi:native`| [Passing a native arguments to the VM](compiler/ffi_pragmas.md) |
## Pragmas for internal testing

View file

@ -2,6 +2,8 @@
// 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 "lib/ffi_dynamic_library.h"
#include "platform/globals.h"
#if defined(DART_HOST_OS_WINDOWS)
#include <Psapi.h>
@ -438,6 +440,39 @@ static void ThrowFfiResolveError(const String& symbol,
Exceptions::ThrowArgumentError(error_message);
}
intptr_t FfiResolveInternal(const String& asset,
const String& symbol,
uintptr_t args_n,
char** error) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
// Resolver resolution.
auto resolver = GetFfiNativeResolver(thread, asset);
if (resolver != nullptr) {
void* ffi_native_result = FfiResolveWithFfiNativeResolver(
thread, resolver, symbol, args_n, error);
return reinterpret_cast<intptr_t>(ffi_native_result);
}
// Native assets resolution.
const auto& asset_location =
Array::Handle(zone, GetAssetLocation(thread, asset));
if (!asset_location.IsNull()) {
void* asset_result = FfiResolveAsset(thread, asset_location, symbol, error);
return reinterpret_cast<intptr_t>(asset_result);
}
// Resolution in current process.
#if !defined(DART_HOST_OS_WINDOWS)
void* const result = Utils::ResolveSymbolInDynamicLibrary(
RTLD_DEFAULT, symbol.ToCString(), error);
#else
void* const result = LookupSymbolInProcess(symbol.ToCString(), error);
#endif
return reinterpret_cast<intptr_t>(result);
}
// FFI native C function pointer resolver.
static intptr_t FfiResolve(Dart_Handle asset_handle,
Dart_Handle symbol_handle,
@ -449,40 +484,12 @@ static intptr_t FfiResolve(Dart_Handle asset_handle,
const String& symbol = Api::UnwrapStringHandle(zone, symbol_handle);
char* error = nullptr;
// Resolver resolution.
auto resolver = GetFfiNativeResolver(thread, asset);
if (resolver != nullptr) {
void* ffi_native_result = FfiResolveWithFfiNativeResolver(
thread, resolver, symbol, args_n, &error);
if (error != nullptr) {
ThrowFfiResolveError(symbol, asset, error);
}
return reinterpret_cast<intptr_t>(ffi_native_result);
}
// Native assets resolution.
const auto& asset_location =
Array::Handle(zone, GetAssetLocation(thread, asset));
if (!asset_location.IsNull()) {
void* asset_result =
FfiResolveAsset(thread, asset_location, symbol, &error);
if (error != nullptr) {
ThrowFfiResolveError(symbol, asset, error);
}
return reinterpret_cast<intptr_t>(asset_result);
}
// Resolution in current process.
#if !defined(DART_HOST_OS_WINDOWS)
void* const result = Utils::ResolveSymbolInDynamicLibrary(
RTLD_DEFAULT, symbol.ToCString(), &error);
#else
void* const result = LookupSymbolInProcess(symbol.ToCString(), &error);
#endif
const intptr_t result = FfiResolveInternal(asset, symbol, args_n, &error);
if (error != nullptr) {
ThrowFfiResolveError(symbol, asset, error);
}
return reinterpret_cast<intptr_t>(result);
ASSERT(result != 0x0);
return result;
}
// Bootstrap to get the FFI Native resolver through a `native` call.

View file

@ -0,0 +1,20 @@
// Copyright (c) 2023, 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.
#ifndef RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_
#define RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_
#include "platform/globals.h"
#include "vm/object.h"
namespace dart {
intptr_t FfiResolveInternal(const String& asset,
const String& symbol,
uintptr_t args_n,
char** error);
} // namespace dart
#endif // RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_

View file

@ -1377,7 +1377,7 @@ const char* Precompiler::MustRetainFunction(const Function& function) {
// * Selector matches a symbol used in Resolver::ResolveDynamic calls
// in dart_entry.cc or dart_api_impl.cc.
// * _Closure.call (used in async stack handling)
if (function.is_native()) {
if (function.is_old_native()) {
return "native function";
}
@ -1648,7 +1648,7 @@ void Precompiler::AddAnnotatedRoots() {
}
}
}
if (function.is_native()) {
if (function.is_old_native()) {
// The embedder will need to lookup this library to provide the native
// resolver, even if there are no embedder calls into the library.
AddApiUse(lib);
@ -2193,7 +2193,7 @@ void Precompiler::DropFunctions() {
// FFI trampolines may be dynamically called.
return AddRetainReason(sig, RetainReasons::kFfiTrampolineSignature);
}
if (function.is_native()) {
if (function.is_old_native()) {
return AddRetainReason(sig, RetainReasons::kNativeSignature);
}
if (function.HasRequiredNamedParameters()) {
@ -2983,7 +2983,7 @@ void Precompiler::DiscardCodeObjects() {
if (functions_to_retain_.ContainsKey(function_)) {
// Retain Code objects corresponding to native functions
// (to find native implementation).
if (function_.is_native()) {
if (function_.is_old_native()) {
++codes_with_native_function_;
return;
}

View file

@ -3427,6 +3427,66 @@ void FlowGraphCompiler::EmitMoveFromNative(
}
}
void FlowGraphCompiler::EmitMoveConst(const compiler::ffi::NativeLocation& dst,
Location src,
Representation src_type,
TemporaryRegisterAllocator* temp) {
ASSERT(src.IsConstant() || src.IsPairLocation());
const auto& dst_type = dst.payload_type();
Register scratch = kNoRegister;
if (dst.IsExpressibleAsLocation() &&
dst_type.IsExpressibleAsRepresentation() &&
dst_type.AsRepresentationOverApprox(zone_) == src_type) {
// We can directly emit the const in the right place and representation.
const Location dst_loc = dst.AsLocation();
assembler()->Comment("dst.IsExpressibleAsLocation() %s",
dst_loc.ToCString());
EmitMove(dst_loc, src, temp);
} else {
// We need an intermediate location.
Location intermediate;
if (dst_type.IsInt()) {
if (TMP == kNoRegister) {
scratch = temp->AllocateTemporary();
Location::RegisterLocation(scratch);
} else {
intermediate = Location::RegisterLocation(TMP);
}
} else {
ASSERT(dst_type.IsFloat());
intermediate = Location::FpuRegisterLocation(FpuTMP);
}
assembler()->Comment("constant using intermediate: %s",
intermediate.ToCString());
if (src.IsPairLocation()) {
for (intptr_t i : {0, 1}) {
const Representation src_type_split =
compiler::ffi::NativeType::FromUnboxedRepresentation(zone_,
src_type)
.Split(zone_, i)
.AsRepresentation();
const auto& intermediate_native =
compiler::ffi::NativeLocation::FromLocation(zone_, intermediate,
src_type_split);
EmitMove(intermediate, src.AsPairLocation()->At(i), temp);
EmitNativeMove(dst.Split(zone_, 2, i), intermediate_native, temp);
}
} else {
const auto& intermediate_native =
compiler::ffi::NativeLocation::FromLocation(zone_, intermediate,
src_type);
EmitMove(intermediate, src, temp);
EmitNativeMove(dst, intermediate_native, temp);
}
if (scratch != kNoRegister) {
temp->ReleaseTemporary();
}
}
return;
}
bool FlowGraphCompiler::CanPcRelativeCall(const Function& target) const {
return FLAG_precompiled_mode && (LoadingUnit::LoadingUnitOf(function()) ==
LoadingUnit::LoadingUnitOf(target));

View file

@ -520,6 +520,12 @@ class FlowGraphCompiler : public ValueObject {
const compiler::ffi::NativeLocation& src,
TemporaryRegisterAllocator* temp);
// Helper method to move a Dart const to a native location.
void EmitMoveConst(const compiler::ffi::NativeLocation& dst,
Location src,
Representation src_type,
TemporaryRegisterAllocator* temp);
bool CheckAssertAssignableTypeTestingABILocations(
const LocationSummary& locs);

View file

@ -7407,15 +7407,18 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
ConstantTemporaryAllocator temp_alloc(temp0);
if (origin.IsConstant()) {
// Can't occur because we currently don't inline FFI trampolines (see
// http://dartbug.com/45055), which means all incoming arguments
// originate from parameters and thus are non-constant.
UNREACHABLE();
}
// Handles are passed into FfiCalls as Tagged values on the stack, and
// then we pass pointers to these handles to the native function here.
if (marshaller_.IsHandle(arg_index)) {
__ Comment("origin.IsConstant()");
compiler->EmitMoveConst(def_target, origin, origin_rep, &temp_alloc);
} else if (origin.IsPairLocation() &&
(origin.AsPairLocation()->At(0).IsConstant() ||
origin.AsPairLocation()->At(1).IsConstant())) {
// Note: half of the pair can be constant.
__ Comment("origin.IsPairLocation() and constant");
compiler->EmitMoveConst(def_target, origin, origin_rep, &temp_alloc);
} else if (marshaller_.IsHandle(arg_index)) {
__ Comment("marshaller_.IsHandle(arg_index)");
// Handles are passed into FfiCalls as Tagged values on the stack, and
// then we pass pointers to these handles to the native function here.
ASSERT(compiler::target::LocalHandle::ptr_offset() == 0);
ASSERT(compiler::target::LocalHandle::InstanceSize() ==
compiler::target::kWordSize);

View file

@ -259,6 +259,7 @@ template <>
void FlowGraphSerializer::WriteTrait<const compiler::ffi::CallMarshaller&>::
Write(FlowGraphSerializer* s, const compiler::ffi::CallMarshaller& x) {
s->Write<const Function&>(x.dart_signature());
s->Write<const FunctionType&>(x.c_signature());
}
template <>
@ -266,9 +267,10 @@ const compiler::ffi::CallMarshaller&
FlowGraphDeserializer::ReadTrait<const compiler::ffi::CallMarshaller&>::Read(
FlowGraphDeserializer* d) {
const Function& dart_signature = d->Read<const Function&>();
const FunctionType& c_signature = d->Read<const FunctionType&>();
const char* error = nullptr;
return *compiler::ffi::CallMarshaller::FromFunction(d->zone(), dart_signature,
&error);
c_signature, &error);
}
template <>

View file

@ -1209,8 +1209,11 @@ ISOLATE_UNIT_TEST_CASE(IRTest_FfiCallInstrLeafDoesntSpill) {
// Construct the FFICallInstr from the trampoline matching our native
// function.
const char* error = nullptr;
auto* const zone = thread->zone();
const auto& c_signature =
FunctionType::ZoneHandle(zone, ffi_trampoline.FfiCSignature());
const auto marshaller_ptr = compiler::ffi::CallMarshaller::FromFunction(
thread->zone(), ffi_trampoline, &error);
zone, ffi_trampoline, c_signature, &error);
RELEASE_ASSERT(error == nullptr);
RELEASE_ASSERT(marshaller_ptr != nullptr);
const auto& marshaller = *marshaller_ptr;

View file

@ -87,10 +87,10 @@ const NativeFunctionType* NativeFunctionTypeFromFunctionType(
CallMarshaller* CallMarshaller::FromFunction(Zone* zone,
const Function& function,
const FunctionType& c_signature,
const char** error) {
DEBUG_ASSERT(function.IsNotTemporaryScopedHandle());
const auto& c_signature =
FunctionType::ZoneHandle(zone, function.FfiCSignature());
DEBUG_ASSERT(c_signature.IsNotTemporaryScopedHandle());
const auto native_function_signature =
NativeFunctionTypeFromFunctionType(zone, c_signature, error);
if (*error != nullptr) {
@ -169,7 +169,7 @@ bool BaseMarshaller::IsCompound(intptr_t arg_index) const {
}
bool BaseMarshaller::ContainsHandles() const {
return dart_signature_.FfiCSignatureContainsHandles();
return c_signature_.ContainsHandles();
}
intptr_t BaseMarshaller::NumDefinitions() const {

View file

@ -138,6 +138,7 @@ class BaseMarshaller : public ZoneAllocated {
}
const Function& dart_signature() const { return dart_signature_; }
const FunctionType& c_signature() const { return c_signature_; }
StringPtr function_name() const { return dart_signature_.name(); }
protected:
@ -164,6 +165,7 @@ class CallMarshaller : public BaseMarshaller {
public:
static CallMarshaller* FromFunction(Zone* zone,
const Function& function,
const FunctionType& c_signature,
const char** error);
CallMarshaller(Zone* zone,

View file

@ -678,8 +678,10 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionBody(
const bool has_body = ReadTag() == kSomething; // read first part of body.
if (dart_function.is_native()) {
if (dart_function.is_old_native()) {
body += B->NativeFunctionBody(dart_function, first_parameter);
} else if (dart_function.is_ffi_native()) {
body += B->FfiNativeFunctionBody(dart_function);
} else if (dart_function.is_external()) {
body += ThrowNoSuchMethodError(TokenPosition::kNoSource, dart_function,
/*incompatible_arguments=*/false);

View file

@ -6,6 +6,7 @@
#include <utility>
#include "lib/ffi_dynamic_library.h"
#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/class_id.h"
@ -593,7 +594,7 @@ Fragment FlowGraphBuilder::Return(TokenPosition position,
// Emit a type check of the return type in checked mode for all functions
// and in strong mode for native functions.
if (!omit_result_type_check && function.is_native()) {
if (!omit_result_type_check && function.is_old_native()) {
const AbstractType& return_type =
AbstractType::Handle(Z, function.result_type());
instructions += CheckAssignable(return_type, Symbols::FunctionResult());
@ -857,7 +858,7 @@ FlowGraph* FlowGraphBuilder::BuildGraph() {
Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
LocalVariable* first_parameter) {
ASSERT(function.is_native());
ASSERT(function.is_old_native());
ASSERT(!IsRecognizedMethodForFlowGraph(function));
Fragment body;
@ -4984,33 +4985,133 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
case FfiFunctionKind::kAsyncCallback:
return BuildGraphOfAsyncFfiCallback(function);
case FfiFunctionKind::kCall:
return BuildGraphOfFfiNative(function);
return BuildGraphOfFfiCall(function);
}
UNREACHABLE();
return nullptr;
}
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
Fragment FlowGraphBuilder::FfiNativeLookupAddress(const Function& function) {
ASSERT(function.is_ffi_native());
ASSERT(!IsRecognizedMethodForFlowGraph(function));
auto const& native_instance =
Instance::Handle(function.GetNativeAnnotation());
const auto& native_class = Class::Handle(Z, native_instance.clazz());
ASSERT(String::Handle(Z, native_class.UserVisibleName())
.Equals(Symbols::FfiNative()));
const auto& native_class_fields = Array::Handle(Z, native_class.fields());
ASSERT(native_class_fields.Length() == 3);
const auto& symbol_field =
Field::Handle(Z, Field::RawCast(native_class_fields.At(0)));
const auto& asset_id_field =
Field::Handle(Z, Field::RawCast(native_class_fields.At(1)));
const auto& symbol = String::ZoneHandle(
Z, String::RawCast(native_instance.GetField(symbol_field)));
const auto& asset_id = String::ZoneHandle(
Z, String::RawCast(native_instance.GetField(asset_id_field)));
const auto& type_args =
TypeArguments::Handle(Z, native_instance.GetTypeArguments());
ASSERT(type_args.Length() == 1);
const auto& native_type =
FunctionType::Cast(AbstractType::ZoneHandle(Z, type_args.TypeAt(0)));
const intptr_t arg_n =
native_type.NumParameters() - native_type.num_implicit_parameters();
const auto& ffi_resolver =
Function::ZoneHandle(Z, IG->object_store()->ffi_resolver_function());
#if !defined(TARGET_ARCH_IA32)
// Access to the pool, use cacheable static call.
Fragment body;
body += Constant(asset_id);
body += Constant(symbol);
body += Constant(Smi::ZoneHandle(Smi::New(arg_n)));
body += CachableIdempotentCall(TokenPosition::kNoSource, ffi_resolver,
/*argument_count=*/3,
/*argument_names=*/Array::null_array(),
/*type_args_count=*/0);
return body;
#else
// IA32 only has JIT and no pool. This function will only be compiled if
// immediately run afterwards, so do the lookup here.
char* error = nullptr;
#if !defined(DART_PRECOMPILER) || defined(TESTING)
const uintptr_t function_address =
FfiResolveInternal(asset_id, symbol, arg_n, &error);
#else
const uintptr_t function_address = 0;
UNREACHABLE(); // JIT runtime should not contain AOT code
#endif
if (error == nullptr) {
Fragment body;
body += UnboxedIntConstant(function_address, kUnboxedFfiIntPtr);
return body;
} else {
free(error);
// Lookup failed, we want to throw an error consistent with AOT, just
// compile into a lookup so that we can throw the error from the same
// error path.
Fragment body;
body += Constant(asset_id);
body += Constant(symbol);
body += Constant(Smi::ZoneHandle(Smi::New(arg_n)));
// Non-cacheable call, this is IA32.
body += StaticCall(TokenPosition::kNoSource, ffi_resolver,
/*argument_count=*/3, ICData::kStatic);
body += UnboxTruncate(kUnboxedFfiIntPtr);
return body;
}
#endif
}
Fragment FlowGraphBuilder::FfiCallLookupAddress(const Function& function) {
ASSERT(function.IsFfiTrampoline());
const intptr_t kClosureParameterOffset = 0;
const intptr_t kFirstArgumentParameterOffset = kClosureParameterOffset + 1;
Fragment body;
// Push the function pointer, which is stored (as Pointer object) in the
// first slot of the context.
body +=
LoadLocal(parsed_function_->ParameterVariable(kClosureParameterOffset));
body += LoadNativeField(Slot::Closure_context());
body += LoadNativeField(Slot::GetContextVariableSlotFor(
thread_, *MakeImplicitClosureScope(
Z, Class::Handle(IG->object_store()->ffi_pointer_class()))
->context_variables()[0]));
graph_entry_ =
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
// This can only be Pointer, so it is always safe to LoadUntagged.
body += LoadUntagged(compiler::target::PointerBase::data_offset());
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
return body;
}
auto normal_entry = BuildFunctionEntry(graph_entry_);
graph_entry_->set_normal_entry(normal_entry);
Fragment FlowGraphBuilder::FfiNativeFunctionBody(const Function& function) {
ASSERT(function.is_ffi_native());
ASSERT(!IsRecognizedMethodForFlowGraph(function));
PrologueInfo prologue_info(-1, -1);
const auto& c_signature =
FunctionType::ZoneHandle(Z, function.FfiCSignature());
BlockEntryInstr* instruction_cursor =
BuildPrologue(normal_entry, &prologue_info);
Fragment body;
body += FfiNativeLookupAddress(function);
body += FfiCallFunctionBody(function, c_signature);
return body;
}
Fragment function_body(instruction_cursor);
function_body += CheckStackOverflowInPrologue(function.token_pos());
Fragment FlowGraphBuilder::FfiCallFunctionBody(
const Function& function,
const FunctionType& c_signature) {
ASSERT(function.is_ffi_native() || function.IsFfiTrampoline());
const bool is_ffi_native = function.is_ffi_native();
const intptr_t kClosureParameterOffset = 0;
const intptr_t first_argument_parameter_offset =
is_ffi_native ? 0 : kClosureParameterOffset + 1;
LocalVariable* address = MakeTemporary("address");
Fragment body;
const char* error = nullptr;
const auto marshaller_ptr =
compiler::ffi::CallMarshaller::FromFunction(Z, function, &error);
const auto marshaller_ptr = compiler::ffi::CallMarshaller::FromFunction(
Z, function, c_signature, &error);
// AbiSpecific integers can be incomplete causing us to not know the calling
// convention. However, this is caught in asFunction in both JIT/AOT.
RELEASE_ASSERT(error == nullptr);
@ -5030,21 +5131,20 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
if (marshaller.IsHandle(i)) {
continue;
}
function_body += LoadLocal(
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i));
body += LoadLocal(parsed_function_->ParameterVariable(
first_argument_parameter_offset + i));
// TODO(http://dartbug.com/47486): Support entry without checking for null.
// Check for 'null'.
function_body += CheckNullOptimized(
body += CheckNullOptimized(
String::ZoneHandle(
Z, function.ParameterNameAt(kFirstArgumentParameterOffset + i)),
Z, function.ParameterNameAt(first_argument_parameter_offset + i)),
CheckNullInstr::kArgumentError);
function_body += StoreLocal(
TokenPosition::kNoSource,
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i));
function_body += Drop();
body += StoreLocal(TokenPosition::kNoSource,
parsed_function_->ParameterVariable(
first_argument_parameter_offset + i));
body += Drop();
}
Fragment body;
intptr_t try_handler_index = -1;
if (signature_contains_handles) {
// Wrap in Try catch to transition from Native to Generated on a throw from
@ -5072,12 +5172,12 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
if (marshaller.IsCompound(i)) {
body += FfiCallConvertCompoundArgumentToNative(
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset +
parsed_function_->ParameterVariable(first_argument_parameter_offset +
i),
marshaller, i);
} else {
body += LoadLocal(parsed_function_->ParameterVariable(
kFirstArgumentParameterOffset + i));
first_argument_parameter_offset + i));
// FfiCallInstr specifies all handle locations as Stack, and will pass a
// pointer to the stack slot as the native handle argument.
// Therefore we do not need to wrap handles.
@ -5087,20 +5187,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
}
}
// Push the function pointer, which is stored (as Pointer object) in the
// first slot of the context.
body +=
LoadLocal(parsed_function_->ParameterVariable(kClosureParameterOffset));
body += LoadNativeField(Slot::Closure_context());
body += LoadNativeField(Slot::GetContextVariableSlotFor(
thread_, *MakeImplicitClosureScope(
Z, Class::Handle(IG->object_store()->ffi_pointer_class()))
->context_variables()[0]));
// This can only be Pointer, so it is safe to load the data field.
body += LoadNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kCannotBeInnerPointer);
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
body += LoadLocal(address);
if (marshaller.PassTypedData()) {
body += LoadLocal(typed_data);
@ -5111,7 +5198,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
if (marshaller.IsPointer(i)) {
body += LoadLocal(parsed_function_->ParameterVariable(
kFirstArgumentParameterOffset + i));
first_argument_parameter_offset + i));
body += ReachabilityFence();
}
}
@ -5119,12 +5206,12 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
const intptr_t num_defs = marshaller.NumReturnDefinitions();
ASSERT(num_defs >= 1);
auto defs = new (Z) ZoneGrowableArray<LocalVariable*>(Z, num_defs);
LocalVariable* def = MakeTemporary();
LocalVariable* def = MakeTemporary("ffi call result");
defs->Add(def);
if (marshaller.PassTypedData()) {
// Drop call result, typed data with contents is already on the stack.
body += Drop();
body += DropTemporary(&def);
}
if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
@ -5141,14 +5228,13 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
body += ExitHandleScope();
}
body += DropTempsPreserveTop(1); // Drop address.
body += Return(TokenPosition::kNoSource);
if (signature_contains_handles) {
--try_depth_;
}
function_body += body;
if (signature_contains_handles) {
++catch_depth_;
Fragment catch_body =
@ -5167,6 +5253,30 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
--catch_depth_;
}
return body;
}
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCall(const Function& function) {
graph_entry_ =
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
auto normal_entry = BuildFunctionEntry(graph_entry_);
graph_entry_->set_normal_entry(normal_entry);
PrologueInfo prologue_info(-1, -1);
BlockEntryInstr* instruction_cursor =
BuildPrologue(normal_entry, &prologue_info);
Fragment function_body(instruction_cursor);
function_body += CheckStackOverflowInPrologue(function.token_pos());
const auto& c_signature =
FunctionType::ZoneHandle(Z, function.FfiCSignature());
function_body += FfiCallLookupAddress(function);
function_body += FfiCallFunctionBody(function, c_signature);
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
prologue_info);
}

View file

@ -137,8 +137,14 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
FlowGraph* BuildGraphOfFfiTrampoline(const Function& function);
FlowGraph* BuildGraphOfSyncFfiCallback(const Function& function);
FlowGraph* BuildGraphOfAsyncFfiCallback(const Function& function);
FlowGraph* BuildGraphOfFfiNative(const Function& function);
FlowGraph* BuildGraphOfFfiCall(const Function& function);
Fragment FfiCallLookupAddress(const Function& function);
Fragment FfiNativeLookupAddress(const Function& function);
// Expects target address on stack.
Fragment FfiCallFunctionBody(const Function& function,
const FunctionType& c_signature);
Fragment FfiNativeFunctionBody(const Function& function);
Fragment NativeFunctionBody(const Function& function,
LocalVariable* first_parameter);
Fragment LoadNativeArg(const compiler::ffi::CallbackMarshaller& marshaller,

View file

@ -155,7 +155,19 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
// NOTE: FunctionNode is read further below the if.
intptr_t pos = 0;
if (function.IsClosureFunction()) {
if (function.is_ffi_native()) {
needs_expr_temp_ = true;
// Calls with handles need try/catch variables.
if (function.FfiCSignatureContainsHandles()) {
++depth_.try_;
AddTryVariables();
--depth_.try_;
++depth_.catch_;
AddCatchVariables();
FinalizeCatchVariables();
--depth_.catch_;
}
} else if (function.IsClosureFunction()) {
LocalVariable* closure_parameter = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ClosureParameter(), AbstractType::dynamic_type());

File diff suppressed because it is too large Load diff

View file

@ -1749,6 +1749,9 @@ void KernelLoader::ReadVMAnnotations(const Library& library,
"vm:isolate-unsendable")) {
*pragma_bits = IsolateUnsendablePragma::update(true, *pragma_bits);
}
if (constant_reader.IsStringConstant(name_index, "vm:ffi:native")) {
*pragma_bits = FfiNativePragma::update(true, *pragma_bits);
}
}
} else {
helper_.SkipExpression();
@ -1798,13 +1801,14 @@ void KernelLoader::LoadProcedure(const Library& library,
// they are not reachable anymore and we never look them up by name.
const bool register_function = !name.Equals(Symbols::DebugProcedureName());
const bool is_ffi_native = FfiNativePragma::decode(pragma_bits);
const FunctionType& signature = FunctionType::Handle(Z, FunctionType::New());
const Function& function = Function::ZoneHandle(
Z, Function::New(signature, name, kind,
!is_method, // is_static
false, // is_const
is_abstract, is_external,
!native_name.IsNull(), // is_native
!native_name.IsNull() || is_ffi_native, // is_native
script_class, procedure_helper.start_position_));
function.set_has_pragma(HasPragma::decode(pragma_bits));
function.set_end_token_pos(procedure_helper.end_position_);

View file

@ -220,6 +220,8 @@ class KernelLoader : public ValueObject {
BitField<uint32_t, bool, ExternalNamePragma::kNextBit, 1>;
using IsolateUnsendablePragma =
BitField<uint32_t, bool, InvisibleFunctionPragma::kNextBit, 1>;
using FfiNativePragma =
BitField<uint32_t, bool, IsolateUnsendablePragma::kNextBit, 1>;
void FinishTopLevelClassLoading(const Class& toplevel_class,
const Library& library,

View file

@ -175,7 +175,7 @@ class NativeArguments {
}
static intptr_t ParameterCountForResolution(const Function& function) {
ASSERT(function.is_native());
ASSERT(function.is_old_native());
ASSERT(!function.IsGenerativeConstructor()); // Not supported.
intptr_t count = function.NumParameters();
if (function.is_static() && function.IsClosureFunction()) {
@ -189,7 +189,7 @@ class NativeArguments {
}
static int ComputeArgcTag(const Function& function) {
ASSERT(function.is_native());
ASSERT(function.is_old_native());
ASSERT(!function.IsGenerativeConstructor()); // Not supported.
int argc = function.NumParameters();
int function_bits = 0;

View file

@ -8358,7 +8358,7 @@ void Function::set_implicit_closure_function(const Function& value) const {
IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
ASSERT(!IsClosureFunction());
const Object& old_data = Object::Handle(data());
if (is_native()) {
if (is_old_native()) {
ASSERT(old_data.IsArray());
const auto& pair = Array::Cast(old_data);
ASSERT(pair.AtAcquire(NativeFunctionData::kTearOff) == Object::null() ||
@ -8378,26 +8378,38 @@ void Function::SetFfiCSignature(const FunctionType& sig) const {
}
FunctionTypePtr Function::FfiCSignature() const {
ASSERT(IsFfiTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).c_signature();
auto* const zone = Thread::Current()->zone();
if (IsFfiTrampoline()) {
const Object& obj = Object::Handle(zone, data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).c_signature();
}
ASSERT(is_ffi_native());
auto const& native_instance = Instance::Handle(GetNativeAnnotation());
const auto& type_args =
TypeArguments::Handle(zone, native_instance.GetTypeArguments());
ASSERT(type_args.Length() == 1);
const auto& native_type =
FunctionType::Cast(AbstractType::ZoneHandle(zone, type_args.TypeAt(0)));
return native_type.ptr();
}
bool Function::FfiCSignatureContainsHandles() const {
ASSERT(IsFfiTrampoline());
const FunctionType& c_signature = FunctionType::Handle(FfiCSignature());
const intptr_t num_params = c_signature.num_fixed_parameters();
return c_signature.ContainsHandles();
}
bool FunctionType::ContainsHandles() const {
const intptr_t num_params = num_fixed_parameters();
for (intptr_t i = 0; i < num_params; i++) {
const bool is_handle =
AbstractType::Handle(c_signature.ParameterTypeAt(i)).type_class_id() ==
AbstractType::Handle(ParameterTypeAt(i)).type_class_id() ==
kFfiHandleCid;
if (is_handle) {
return true;
}
}
return AbstractType::Handle(c_signature.result_type()).type_class_id() ==
kFfiHandleCid;
return AbstractType::Handle(result_type()).type_class_id() == kFfiHandleCid;
}
// Keep consistent with BaseMarshaller::IsCompound.
@ -8453,10 +8465,22 @@ void Function::AssignFfiCallbackId(int32_t callback_id) const {
}
bool Function::FfiIsLeaf() const {
ASSERT(IsFfiTrampoline());
const Object& obj = Object::Handle(untag()->data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).is_leaf();
if (IsFfiTrampoline()) {
const Object& obj = Object::Handle(untag()->data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).is_leaf();
}
ASSERT(is_ffi_native());
Zone* zone = Thread::Current()->zone();
auto const& native_instance = Instance::Handle(GetNativeAnnotation());
const auto& native_class = Class::Handle(zone, native_instance.clazz());
const auto& native_class_fields = Array::Handle(zone, native_class.fields());
ASSERT(native_class_fields.Length() == 3);
const auto& is_leaf_field =
Field::Handle(zone, Field::RawCast(native_class_fields.At(2)));
return Bool::Handle(zone,
Bool::RawCast(native_instance.GetField(is_leaf_field)))
.value();
}
void Function::SetFfiIsLeaf(bool is_leaf) const {
@ -8609,6 +8633,32 @@ void Function::set_native_name(const String& value) const {
pair.SetAt(NativeFunctionData::kNativeName, value);
}
InstancePtr Function::GetNativeAnnotation() const {
ASSERT(is_ffi_native());
Zone* zone = Thread::Current()->zone();
auto& pragma_value = Object::Handle(zone);
Library::FindPragma(dart::Thread::Current(), /*only_core=*/false,
Object::Handle(zone, ptr()),
String::Handle(zone, Symbols::vm_ffi_native().ptr()),
/*multiple=*/false, &pragma_value);
auto const& native_instance = Instance::Cast(pragma_value);
ASSERT(!native_instance.IsNull());
#if defined(DEBUG)
const auto& native_class = Class::Handle(zone, native_instance.clazz());
ASSERT(String::Handle(zone, native_class.UserVisibleName())
.Equals(Symbols::FfiNative()));
#endif
return native_instance.ptr();
}
bool Function::is_old_native() const {
return is_native() && !is_external();
}
bool Function::is_ffi_native() const {
return is_native() && is_external();
}
void Function::SetSignature(const FunctionType& value) const {
set_signature(value);
ASSERT(NumImplicitParameters() == value.num_implicit_parameters());
@ -8998,7 +9048,7 @@ bool Function::IsOptimizable() const {
return true;
}
if (ForceOptimize()) return true;
if (is_native()) {
if (is_old_native()) {
// Native methods don't need to be optimized.
return false;
}
@ -9081,7 +9131,7 @@ static bool InVmTests(const Function& function) {
}
bool Function::ForceOptimize() const {
if (RecognizedKindForceOptimize() || IsFfiTrampoline() ||
if (RecognizedKindForceOptimize() || IsFfiTrampoline() || is_ffi_native() ||
IsTypedDataViewFactory() || IsUnmodifiableTypedDataViewFactory()) {
return true;
}
@ -9196,7 +9246,7 @@ bool Function::RecognizedKindForceOptimize() const {
#if !defined(DART_PRECOMPILED_RUNTIME)
bool Function::CanBeInlined() const {
if (ForceOptimize()) {
if (IsFfiTrampoline()) {
if (IsFfiTrampoline() || is_ffi_native()) {
// We currently don't support inlining FFI trampolines. Some of them
// are naturally non-inlinable because they contain a try/catch block,
// but this condition is broader than strictly necessary.
@ -10295,7 +10345,7 @@ FunctionPtr Function::New(const FunctionType& signature,
const FfiTrampolineData& data =
FfiTrampolineData::Handle(FfiTrampolineData::New());
result.set_data(data);
} else if (is_native) {
} else if (result.is_old_native()) {
const auto& data =
Array::Handle(Array::New(NativeFunctionData::kLength, Heap::kOld));
result.set_data(data);

View file

@ -2979,8 +2979,7 @@ class Function : public Object {
// Can only be used on FFI trampolines.
void SetFfiCSignature(const FunctionType& sig) const;
// Retrieves the "C signature" for an FFI trampoline.
// Can only be used on FFI trampolines.
// Retrieves the "C signature" for an FFI trampoline or FFI native.
FunctionTypePtr FfiCSignature() const;
bool FfiCSignatureContainsHandles() const;
@ -3073,6 +3072,10 @@ class Function : public Object {
StringPtr native_name() const;
void set_native_name(const String& name) const;
InstancePtr GetNativeAnnotation() const;
bool is_ffi_native() const;
bool is_old_native() const;
AbstractTypePtr result_type() const {
return signature()->untag()->result_type();
}
@ -9663,6 +9666,8 @@ class FunctionType : public AbstractType {
static FunctionTypePtr Clone(const FunctionType& orig, Heap::Space space);
bool ContainsHandles() const;
private:
static FunctionTypePtr New(Heap::Space space);

View file

@ -543,6 +543,16 @@ void ObjectStore::LazyInitFfiMembers() {
ASSERT(!function.IsNull());
handle_native_finalizer_message_function_.store(function.ptr());
const auto& lib = Library::Handle(zone, Library::FfiLibrary());
const Class& klass = Class::ZoneHandle(zone, lib.toplevel_class());
ASSERT(!klass.IsNull());
error = klass.EnsureIsFinalized(thread);
ASSERT(error.IsNull());
function = klass.LookupStaticFunctionAllowPrivate(
Symbols::_ffi_resolver_function());
ASSERT(!function.IsNull());
ffi_resolver_function_.store(function.ptr());
cls = ffi_lib.LookupClass(Symbols::VarArgs());
ASSERT(!cls.IsNull());
varargs_class_.store(cls.ptr());

View file

@ -58,6 +58,7 @@ class ObjectPointerVisitor;
LAZY_INTERNAL(Class, symbol_class) \
LAZY_INTERNAL(Field, symbol_name_field) \
LAZY_FFI(Class, varargs_class) \
LAZY_FFI(Function, ffi_resolver_function) \
LAZY_FFI(Function, handle_finalizer_message_function) \
LAZY_FFI(Function, handle_native_finalizer_message_function) \
LAZY_ASYNC(Type, non_nullable_future_never_type) \

View file

@ -116,6 +116,7 @@ class ObjectPointerVisitor;
V(FfiInt8, "Int8") \
V(FfiIntPtr, "IntPtr") \
V(FfiIsolateLocalCallback, "_FfiIsolateLocalCallback") \
V(FfiNative, "Native") \
V(FfiNativeFunction, "NativeFunction") \
V(FfiNativeType, "NativeType") \
V(FfiNativeTypes, "nativeTypes") \
@ -426,6 +427,7 @@ class ObjectPointerVisitor;
V(_checkSetRangeArguments, "_checkSetRangeArguments") \
V(_current, "_current") \
V(_ensureScheduleImmediate, "_ensureScheduleImmediate") \
V(_ffi_resolver_function, "_ffi_resolver_function") \
V(future, "future") \
V(_future, "_future") \
V(_getRegisters, "_getRegisters") \
@ -525,6 +527,7 @@ class ObjectPointerVisitor;
V(vm_exact_result_type, "vm:exact-result-type") \
V(vm_external_name, "vm:external-name") \
V(vm_ffi_abi_specific_mapping, "vm:ffi:abi-specific-mapping") \
V(vm_ffi_native, "vm:ffi:native") \
V(vm_ffi_native_assets, "vm:ffi:native-assets") \
V(vm_ffi_struct_fields, "vm:ffi:struct-fields") \
V(vm_force_optimize, "vm:force-optimize") \

View file

@ -1355,3 +1355,11 @@ final class _ArraySize<T extends NativeType> implements Array<T> {
Object get _typedDataBase =>
throw UnsupportedError('_ArraySize._typedDataBase');
}
@patch
@pragma("vm:entry-point")
class FfiNative<T> {}
@patch
@pragma("vm:entry-point")
class Native<T> {}

View file

@ -1347,3 +1347,6 @@ external Pointer<NativeFunction<IntPtr Function(Handle, Handle, IntPtr)>>
final _ffi_resolver = _get_ffi_native_resolver<
NativeFunction<IntPtr Function(Handle, Handle, IntPtr)>>()
.asFunction<int Function(Object, Object, int)>();
@pragma('vm:entry-point')
int _ffi_resolver_function(Object a, Object s, int n) => _ffi_resolver(a, s, n);