[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; ..fileOffset = fileOffset;
if (dartSignature is FunctionType) { final possibleCompoundReturn = findCompoundReturnType(dartSignature);
final returnType = dartSignature.returnType; if (possibleCompoundReturn != null) {
if (returnType is InterfaceType) { return invokeCompoundConstructor(
final clazz = returnType.classNode; asFunctionInternalInvocation, possibleCompoundReturn);
if (clazz.superclass == structClass || clazz.superclass == unionClass) {
return invokeCompoundConstructor(asFunctionInternalInvocation, clazz);
}
}
} }
return asFunctionInternalInvocation; 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 /// Returns
/// - `true` if leaf /// - `true` if leaf
/// - `false` if not 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], // Whether a parameter of [dartParameterType], passed as [ffiParameterType],
// needs to be converted to Pointer. // needs to be converted to Pointer.
bool _requiresPointerConversion( bool _requiresPointerConversion(
@ -350,7 +275,7 @@ class FfiNativeTransformer extends FfiTransformer {
// reachabilityFence(#t0); // reachabilityFence(#t0);
// } => #t1 // } => #t1
Expression _wrapArgumentsAndReturn({ Expression _wrapArgumentsAndReturn({
required FunctionInvocation invocation, required StaticInvocation invocation,
required FunctionType dartFunctionType, required FunctionType dartFunctionType,
required FunctionType ffiFunctionType, required FunctionType ffiFunctionType,
bool checkReceiverForNullptr = false, bool checkReceiverForNullptr = false,
@ -487,6 +412,8 @@ class FfiNativeTransformer extends FfiTransformer {
return validSignature; return validSignature;
} }
static const vmFfiNative = 'vm:ffi:native';
Procedure _transformProcedure( Procedure _transformProcedure(
Procedure node, Procedure node,
StringConstant nativeFunctionName, StringConstant nativeFunctionName,
@ -508,74 +435,135 @@ class FfiNativeTransformer extends FfiTransformer {
return node; return node;
} }
final parent = node.parent; final pragmaConstant = ConstantExpression(
InstanceConstant(pragmaClass.reference, [], {
// static final _myMethod$FfiNative$Ptr = .. pragmaName.fieldReference: StringConstant(vmFfiNative),
final resolvedField = _createResolvedFfiNativeField( pragmaOptions.fieldReference: InstanceConstant(
'${node.name.text}\$${node.kind.name}', nativeClass.reference,
nativeFunctionName, [ffiFunctionType],
assetName, {
isLeaf, nativeSymbolField.fieldReference: nativeFunctionName,
wrappedDartFunctionType, nativeAssetField.fieldReference: assetName ??
ffiFunctionType, StringConstant(currentLibrary.importUri.toString()),
node.fileOffset, nativeIsLeafField.fieldReference: BoolConstant(isLeaf),
node.fileUri, },
)
}),
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) { if (parent is Class) {
parent.addField(resolvedField); fileUri = parent.fileUri;
} else if (parent is Library) { } 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 { } else {
throw 'Unexpected parent of @FfiNative function. ' throw 'Unexpected parent of @Native function. '
'Expected Class or Library, but found ${parent}.'; 'Expected Class or Library, but found ${parent}.';
} }
// _myFunction$FfiNative$Ptr(obj, x) final nonWrappedInvocation = StaticInvocation(
final functionPointerInvocation = FunctionInvocation( nonWrappedFfiNative,
FunctionAccessKind.FunctionType, Arguments(argumentList),
StaticGet(resolvedField), )..fileOffset = node.fileOffset;
Arguments(argumentList),
functionType: wrappedDartFunctionType)
..fileOffset = node.fileOffset;
Expression result = (wrappedDartFunctionType == dartFunctionType Expression result = (wrappedDartFunctionType == dartFunctionType
? functionPointerInvocation ? nonWrappedInvocation
: _wrapArgumentsAndReturn( : _wrapArgumentsAndReturn(
invocation: functionPointerInvocation, invocation: nonWrappedInvocation,
dartFunctionType: dartFunctionType, dartFunctionType: dartFunctionType,
ffiFunctionType: ffiFunctionType, ffiFunctionType: ffiFunctionType,
checkReceiverForNullptr: checkReceiverForNullptr, 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; node.function.body = ReturnStatement(result)..parent = node.function;
return node; return node;
} }
// Transform FfiNative instance methods. // Transform FfiNative instance methods.
//
// Example: // Example:
//
// class MyNativeClass extends NativeFieldWrapperClass1 { // 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); // external int myMethod(int x);
// } // }
//
// Becomes, roughly: // 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 = ... // @pragma(
// int myMethod(int x) // 'vm:ffi:native',
// => _myMethod$FfiNative$Ptr( // Native<IntPtr Function(Pointer<Void>, IntPtr)>(
// Pointer<Void>.fromAddress(_getNativeField(this)), x); // 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 _transformInstanceMethod(
Procedure node, Procedure node,
@ -609,13 +597,26 @@ class FfiNativeTransformer extends FfiTransformer {
} }
// Transform FfiNative static functions. // Transform FfiNative static functions.
//
// Example: // Example:
// @FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('MyFunction') //
// @Native<IntPtr Function(Pointer<Void>, IntPtr)>(symbol: 'MyFunction')
// external int myFunction(MyNativeClass obj, int x); // external int myFunction(MyNativeClass obj, int x);
//
// Becomes, roughly: // 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) // int myFunction(MyNativeClass obj, int x)
// => myFunction$FfiNative$Ptr( // => _myFunction$FfiNative(
// Pointer<Void>.fromAddress(_getNativeField(obj)), x); // Pointer<Void>.fromAddress(_getNativeField(obj)), x);
Procedure _transformStaticFunction( Procedure _transformStaticFunction(
Procedure node, Procedure node,
@ -648,7 +649,7 @@ class FfiNativeTransformer extends FfiTransformer {
@override @override
visitProcedure(Procedure node) { visitProcedure(Procedure node) {
// Only transform functions that are external and have FfiNative annotation: // 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); // external double _square_root(double x);
final ffiNativeAnnotation = final ffiNativeAnnotation =
tryGetNativeAnnotation(node) ?? tryGetFfiNativeAnnotation(node); tryGetNativeAnnotation(node) ?? tryGetFfiNativeAnnotation(node);
@ -661,7 +662,6 @@ class FfiNativeTransformer extends FfiTransformer {
1, node.location?.file); 1, node.location?.file);
return node; return node;
} }
node.isExternal = false;
node.annotations.remove(ffiNativeAnnotation); node.annotations.remove(ffiNativeAnnotation);

View file

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

View file

@ -1,129 +1,144 @@
library #lib; library #lib;
import self as self; import self as self;
import "dart:core" as core; import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:nativewrappers" as nat; import "dart:nativewrappers" as nat;
import "dart:ffi" as ffi;
import "dart:_internal" as _in; import "dart:_internal" as _in;
import "dart:ffi"; import "dart:ffi";
import "dart:nativewrappers"; import "dart:nativewrappers";
abstract class Classy extends core::Object { 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)->i] @#C6
[@vm.unboxing-info.metadata=()->i] static method returnIntPtrStatic() → core::int external static method returnIntPtrStatic([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 222)] core::int x) → core::int;
return self::Classy::_returnIntPtrStatic$Method$FfiNative$Ptr(#C4){(core::int) → core::int};
} }
class NativeClassy extends nat::NativeFieldWrapperClass1 { 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 synthetic constructor •() → self::NativeClassy
: super nat::NativeFieldWrapperClass1::•() : 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 { return block {
final nat::NativeFieldWrapperClass1 #t1 = this; final nat::NativeFieldWrapperClass1 #t1 = this;
final core::int #t2 = #C7; final core::int #t2 = #C10;
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block { 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); 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}) 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(#C9); core::StateError::_throwNew(#C12);
else else
; ;
} =>#pointerAddress), #t2){(ffi::Pointer<ffi::Void>, core::int) → void}; } =>#pointerAddress), #t2);
_in::reachabilityFence(#t1); _in::reachabilityFence(#t1);
} =>#t3; } =>#t3;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] method goodHasReceiverHandle() → void [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] @#C9
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative$Ptr(this, #C7){(self::NativeClassy, core::int) → void}; method goodHasReceiverHandle() → 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 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 { return block {
final self::NativeClassy #t4 = this; final self::NativeClassy #t4 = this;
final nat::NativeFieldWrapperClass1 #t5 = v; 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); _in::reachabilityFence(#t5);
} =>#t6; } =>#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 [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] @#C9
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr(this, v){(self::NativeClassy, self::NativeClassy) → void}; method goodHasReceiverHandleAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → 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 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 { return block {
final nat::NativeFieldWrapperClass1 #t7 = this; final nat::NativeFieldWrapperClass1 #t7 = this;
final self::NativeClassy #t8 = v; 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); 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}) 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(#C9); core::StateError::_throwNew(#C12);
else else
; ;
} =>#pointerAddress), #t8){(ffi::Pointer<ffi::Void>, self::NativeClassy) → void}; } =>#pointerAddress), #t8);
_in::reachabilityFence(#t7); _in::reachabilityFence(#t7);
} =>#t9; } =>#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 { return block {
final nat::NativeFieldWrapperClass1 #t10 = this; final nat::NativeFieldWrapperClass1 #t10 = this;
final core::bool #t11 = #C10; final core::bool #t11 = #C13;
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([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t10); 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}) 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(#C9); core::StateError::_throwNew(#C12);
else else
; ;
} =>#pointerAddress), #t11){(ffi::Pointer<ffi::Void>, core::bool) → core::Object?}); } =>#pointerAddress), #t11));
_in::reachabilityFence(#t10); _in::reachabilityFence(#t10);
} =>#t12; } =>#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 { return block {
final nat::NativeFieldWrapperClass1 #t13 = this; 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); 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}) 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(#C9); core::StateError::_throwNew(#C12);
else else
; ;
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool}; } =>#pointerAddress));
_in::reachabilityFence(#t13); _in::reachabilityFence(#t13);
} =>#t14; } =>#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 { return block {
final nat::NativeFieldWrapperClass1 #t15 = this; 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); 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}) 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(#C9); core::StateError::_throwNew(#C12);
else else
; ;
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool}; } =>#pointerAddress));
_in::reachabilityFence(#t15); _in::reachabilityFence(#t15);
} =>#t16; } =>#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 { return block {
final nat::NativeFieldWrapperClass1 #t17 = this; final nat::NativeFieldWrapperClass1 #t17 = this;
final core::bool #t18 = value; 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); 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}) 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(#C9); core::StateError::_throwNew(#C12);
else else
; ;
} =>#pointerAddress), #t18){(ffi::Pointer<ffi::Void>, core::bool) → void}; } =>#pointerAddress), #t18);
_in::reachabilityFence(#t17); _in::reachabilityFence(#t17);
} =>#t19; } =>#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.unboxing-info.metadata=(i)->i]@#C6
[@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*/; external static method returnIntPtr([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 13)] core::int x) → core::int;
[@vm.unboxing-info.metadata=()->i]static method returnIntPtr() → core::int [@vm.unboxing-info.metadata=(i)->i]@#C34
return self::_returnIntPtr$Method$FfiNative$Ptr(#C11){(core::int) → core::int}; external static method returnIntPtrLeaf([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 37)] core::int x) → core::int;
[@vm.unboxing-info.metadata=()->i]static method returnIntPtrLeaf() → core::int
return self::_returnIntPtrLeaf$Method$FfiNative$Ptr(#C12){(core::int) → core::int};
static method main() → void { static method main() → void {
self::returnIntPtr(); self::returnIntPtr(13);
self::returnIntPtrLeaf(); self::returnIntPtrLeaf(37);
self::Classy::returnIntPtrStatic(); 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.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.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}; [@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; [@vm.direct-call.metadata=#lib::NativeClassy.myField] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::myField} = !b;
} }
constants { constants {
#C1 = "#lib" #C1 = "vm:ffi:native"
#C2 = "ReturnIntPtr" #C2 = "ReturnIntPtr"
#C3 = 1 #C3 = "#lib"
#C4 = 222 #C4 = false
#C5 = "doesntmatter" #C5 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = 2 #C6 = core::pragma {name:#C1, options:#C5}
#C7 = 175 #C7 = "vm:prefer-inline"
#C8 = 0 #C8 = null
#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." #C9 = core::pragma {name:#C7, options:#C8}
#C10 = true #C10 = 175
#C11 = 13 #C11 = 0
#C12 = 37 #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; library #lib;
import self as self; import self as self;
import "dart:core" as core; import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:nativewrappers" as nat; import "dart:nativewrappers" as nat;
import "dart:ffi" as ffi;
import "dart:_internal" as _in; import "dart:_internal" as _in;
import "dart:ffi"; import "dart:ffi";
import "dart:nativewrappers"; import "dart:nativewrappers";
class Classy extends core::Object { 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 synthetic constructor •() → self::Classy
: super core::Object::•() : super core::Object::•()
; ;
static method returnIntPtrStatic(core::int x) → core::int @#C6
return self::Classy::_returnIntPtrStatic$Method$FfiNative$Ptr(x){(core::int) → core::int}; external static method returnIntPtrStatic(core::int x) → core::int;
} }
class NativeClassy extends nat::NativeFieldWrapperClass1 { 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 synthetic constructor •() → self::NativeClassy
: super nat::NativeFieldWrapperClass1::•() : super nat::NativeFieldWrapperClass1::•()
; ;
@#C9
method goodHasReceiverPointer(core::int v) → void method goodHasReceiverPointer(core::int v) → void
return block { return block {
final nat::NativeFieldWrapperClass1 #t1 = this; final nat::NativeFieldWrapperClass1 #t1 = this;
final core::int #t2 = v; 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); synthesized core::int #pointerAddress = nat::_getNativeField(#t1);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool}) if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C7); core::StateError::_throwNew(#C11);
else else
; ;
} =>#pointerAddress), #t2){(ffi::Pointer<ffi::Void>, core::int) → void}; } =>#pointerAddress), #t2);
_in::reachabilityFence(#t1); _in::reachabilityFence(#t1);
} =>#t3; } =>#t3;
@#C9
method goodHasReceiverHandle(core::int v) → void 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 method goodHasReceiverHandleAndPtr(self::NativeClassy v) → void
return block { return block {
final self::NativeClassy #t4 = this; final self::NativeClassy #t4 = this;
final nat::NativeFieldWrapperClass1 #t5 = v; 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); _in::reachabilityFence(#t5);
} =>#t6; } =>#t6;
@#C9
method goodHasReceiverHandleAndHandle(self::NativeClassy v) → void 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 method goodHasReceiverPtrAndHandle(self::NativeClassy v) → void
return block { return block {
final nat::NativeFieldWrapperClass1 #t7 = this; final nat::NativeFieldWrapperClass1 #t7 = this;
final self::NativeClassy #t8 = v; 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); synthesized core::int #pointerAddress = nat::_getNativeField(#t7);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool}) if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C7); core::StateError::_throwNew(#C11);
else else
; ;
} =>#pointerAddress), #t8){(ffi::Pointer<ffi::Void>, self::NativeClassy) → void}; } =>#pointerAddress), #t8);
_in::reachabilityFence(#t7); _in::reachabilityFence(#t7);
} =>#t9; } =>#t9;
@#C9
method meh(core::bool blah) → core::String? method meh(core::bool blah) → core::String?
return block { return block {
final nat::NativeFieldWrapperClass1 #t10 = this; final nat::NativeFieldWrapperClass1 #t10 = this;
final core::bool #t11 = blah; 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); synthesized core::int #pointerAddress = nat::_getNativeField(#t10);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool}) if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C7); core::StateError::_throwNew(#C11);
else else
; ;
} =>#pointerAddress), #t11){(ffi::Pointer<ffi::Void>, core::bool) → core::Object?}); } =>#pointerAddress), #t11));
_in::reachabilityFence(#t10); _in::reachabilityFence(#t10);
} =>#t12; } =>#t12;
@#C9
method blah() → core::bool method blah() → core::bool
return block { return block {
final nat::NativeFieldWrapperClass1 #t13 = this; 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); synthesized core::int #pointerAddress = nat::_getNativeField(#t13);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool}) if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C7); core::StateError::_throwNew(#C11);
else else
; ;
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool}; } =>#pointerAddress));
_in::reachabilityFence(#t13); _in::reachabilityFence(#t13);
} =>#t14; } =>#t14;
@#C9
get myField() → core::bool get myField() → core::bool
return block { return block {
final nat::NativeFieldWrapperClass1 #t15 = this; 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); synthesized core::int #pointerAddress = nat::_getNativeField(#t15);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool}) if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C7); core::StateError::_throwNew(#C11);
else else
; ;
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool}; } =>#pointerAddress));
_in::reachabilityFence(#t15); _in::reachabilityFence(#t15);
} =>#t16; } =>#t16;
@#C9
set myField(core::bool value) → void set myField(core::bool value) → void
return block { return block {
final nat::NativeFieldWrapperClass1 #t17 = this; final nat::NativeFieldWrapperClass1 #t17 = this;
final core::bool #t18 = value; 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); synthesized core::int #pointerAddress = nat::_getNativeField(#t17);
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool}) if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
core::StateError::_throwNew(#C7); core::StateError::_throwNew(#C11);
else else
; ;
} =>#pointerAddress), #t18){(ffi::Pointer<ffi::Void>, core::bool) → void}; } =>#pointerAddress), #t18);
_in::reachabilityFence(#t17); _in::reachabilityFence(#t17);
} =>#t19; } =>#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*/; @#C6
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*/; external static method returnIntPtr(core::int x) → core::int;
static method returnIntPtr(core::int x) → core::int @#C33
return self::_returnIntPtr$Method$FfiNative$Ptr(x){(core::int) → core::int}; external static method returnIntPtrLeaf(core::int x) → core::int;
static method returnIntPtrLeaf(core::int x) → core::int
return self::_returnIntPtrLeaf$Method$FfiNative$Ptr(x){(core::int) → core::int};
static method main() → void { static method main() → void {
self::returnIntPtr(13); self::returnIntPtr(13);
self::returnIntPtrLeaf(37); self::returnIntPtrLeaf(37);
@ -138,11 +153,37 @@ static method main() → void {
new self::NativeClassy::•().{self::NativeClassy::myField} = !b; new self::NativeClassy::•().{self::NativeClassy::myField} = !b;
} }
constants { constants {
#C1 = "#lib" #C1 = "vm:ffi:native"
#C2 = "ReturnIntPtr" #C2 = "ReturnIntPtr"
#C3 = 1 #C3 = "#lib"
#C4 = "doesntmatter" #C4 = false
#C5 = 2 #C5 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = 0 #C6 = core::pragma {name:#C1, options:#C5}
#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." #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 [@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}})"; 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 { 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}); 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}"); core::print("result = ${result}");
} }
[@vm.unboxing-info.metadata=(i)->b]static method returnStruct1ByteIntNative([@vm.inferred-arg-type.metadata=int] core::int a0) → self::Struct1ByteInt [@vm.unboxing-info.metadata=(i)->b]@#C10
return self::_returnStruct1ByteIntNative$Method$FfiNative$Ptr(a0){(core::int) → self::Struct1ByteInt}; 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 { constants {
#C1 = "vm:ffi:struct-fields" #C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8) #C2 = TypeLiteralConstant(ffi::Int8)
@ -35,7 +37,13 @@ constants {
#C6 = core::pragma {name:#C1, options:#C5} #C6 = core::pragma {name:#C1, options:#C5}
#C7 = 0 #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] #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 #C9 = "vm:prefer-inline"
#C10 = "#lib" #C10 = core::pragma {name:#C9, options:#C4}
#C11 = "ReturnStruct1ByteInt" #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* static get #sizeOf() → core::int*
return #C13.{core::List::[]}(ffi::_abi()){(core::int) → 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 { static method main() → void {
final self::Struct1ByteInt result = self::returnStruct1ByteIntNative(1.{core::int::unary-}(){() → core::int}); final self::Struct1ByteInt result = self::returnStruct1ByteIntNative(1.{core::int::unary-}(){() → core::int});
core::print("result = ${result}"); core::print("result = ${result}");
} }
@#C11
static method returnStruct1ByteIntNative(core::int a0) → self::Struct1ByteInt 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 { constants {
#C1 = "vm:ffi:struct-fields" #C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8) #C2 = TypeLiteralConstant(ffi::Int8)
@ -50,6 +52,10 @@ constants {
#C11 = core::pragma {name:#C10, options:#C4} #C11 = core::pragma {name:#C10, options:#C4}
#C12 = 1 #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] #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" #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) * [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/lib/ffi_dynamic_library.cc](../../../runtime/lib/ffi_dynamic_library.cc)
* [runtime/vm/ffi/native_assets.cc](../../../runtime/vm/ffi/native_assets.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 | | Pragma | Meaning |
| --- | --- | | --- | --- |
| `vm:ffi:native-assets` | [Passing a native assets mapping to the VM](compiler/ffi_pragmas.md) | | `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 ## Pragmas for internal testing

View file

@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a // 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. // BSD-style license that can be found in the LICENSE file.
#include "lib/ffi_dynamic_library.h"
#include "platform/globals.h" #include "platform/globals.h"
#if defined(DART_HOST_OS_WINDOWS) #if defined(DART_HOST_OS_WINDOWS)
#include <Psapi.h> #include <Psapi.h>
@ -438,6 +440,39 @@ static void ThrowFfiResolveError(const String& symbol,
Exceptions::ThrowArgumentError(error_message); 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. // FFI native C function pointer resolver.
static intptr_t FfiResolve(Dart_Handle asset_handle, static intptr_t FfiResolve(Dart_Handle asset_handle,
Dart_Handle symbol_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); const String& symbol = Api::UnwrapStringHandle(zone, symbol_handle);
char* error = nullptr; char* error = nullptr;
// Resolver resolution. const intptr_t result = FfiResolveInternal(asset, symbol, args_n, &error);
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
if (error != nullptr) { if (error != nullptr) {
ThrowFfiResolveError(symbol, asset, error); 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. // 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 // * Selector matches a symbol used in Resolver::ResolveDynamic calls
// in dart_entry.cc or dart_api_impl.cc. // in dart_entry.cc or dart_api_impl.cc.
// * _Closure.call (used in async stack handling) // * _Closure.call (used in async stack handling)
if (function.is_native()) { if (function.is_old_native()) {
return "native function"; 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 // The embedder will need to lookup this library to provide the native
// resolver, even if there are no embedder calls into the library. // resolver, even if there are no embedder calls into the library.
AddApiUse(lib); AddApiUse(lib);
@ -2193,7 +2193,7 @@ void Precompiler::DropFunctions() {
// FFI trampolines may be dynamically called. // FFI trampolines may be dynamically called.
return AddRetainReason(sig, RetainReasons::kFfiTrampolineSignature); return AddRetainReason(sig, RetainReasons::kFfiTrampolineSignature);
} }
if (function.is_native()) { if (function.is_old_native()) {
return AddRetainReason(sig, RetainReasons::kNativeSignature); return AddRetainReason(sig, RetainReasons::kNativeSignature);
} }
if (function.HasRequiredNamedParameters()) { if (function.HasRequiredNamedParameters()) {
@ -2983,7 +2983,7 @@ void Precompiler::DiscardCodeObjects() {
if (functions_to_retain_.ContainsKey(function_)) { if (functions_to_retain_.ContainsKey(function_)) {
// Retain Code objects corresponding to native functions // Retain Code objects corresponding to native functions
// (to find native implementation). // (to find native implementation).
if (function_.is_native()) { if (function_.is_old_native()) {
++codes_with_native_function_; ++codes_with_native_function_;
return; 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 { bool FlowGraphCompiler::CanPcRelativeCall(const Function& target) const {
return FLAG_precompiled_mode && (LoadingUnit::LoadingUnitOf(function()) == return FLAG_precompiled_mode && (LoadingUnit::LoadingUnitOf(function()) ==
LoadingUnit::LoadingUnitOf(target)); LoadingUnit::LoadingUnitOf(target));

View file

@ -520,6 +520,12 @@ class FlowGraphCompiler : public ValueObject {
const compiler::ffi::NativeLocation& src, const compiler::ffi::NativeLocation& src,
TemporaryRegisterAllocator* temp); 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( bool CheckAssertAssignableTypeTestingABILocations(
const LocationSummary& locs); const LocationSummary& locs);

View file

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

View file

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

View file

@ -1209,8 +1209,11 @@ ISOLATE_UNIT_TEST_CASE(IRTest_FfiCallInstrLeafDoesntSpill) {
// Construct the FFICallInstr from the trampoline matching our native // Construct the FFICallInstr from the trampoline matching our native
// function. // function.
const char* error = nullptr; 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( 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(error == nullptr);
RELEASE_ASSERT(marshaller_ptr != nullptr); RELEASE_ASSERT(marshaller_ptr != nullptr);
const auto& marshaller = *marshaller_ptr; const auto& marshaller = *marshaller_ptr;

View file

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

View file

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

View file

@ -678,8 +678,10 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionBody(
const bool has_body = ReadTag() == kSomething; // read first part of body. 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); 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()) { } else if (dart_function.is_external()) {
body += ThrowNoSuchMethodError(TokenPosition::kNoSource, dart_function, body += ThrowNoSuchMethodError(TokenPosition::kNoSource, dart_function,
/*incompatible_arguments=*/false); /*incompatible_arguments=*/false);

View file

@ -6,6 +6,7 @@
#include <utility> #include <utility>
#include "lib/ffi_dynamic_library.h"
#include "platform/assert.h" #include "platform/assert.h"
#include "platform/globals.h" #include "platform/globals.h"
#include "vm/class_id.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 // Emit a type check of the return type in checked mode for all functions
// and in strong mode for native 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 = const AbstractType& return_type =
AbstractType::Handle(Z, function.result_type()); AbstractType::Handle(Z, function.result_type());
instructions += CheckAssignable(return_type, Symbols::FunctionResult()); instructions += CheckAssignable(return_type, Symbols::FunctionResult());
@ -857,7 +858,7 @@ FlowGraph* FlowGraphBuilder::BuildGraph() {
Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function, Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
LocalVariable* first_parameter) { LocalVariable* first_parameter) {
ASSERT(function.is_native()); ASSERT(function.is_old_native());
ASSERT(!IsRecognizedMethodForFlowGraph(function)); ASSERT(!IsRecognizedMethodForFlowGraph(function));
Fragment body; Fragment body;
@ -4984,33 +4985,133 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
case FfiFunctionKind::kAsyncCallback: case FfiFunctionKind::kAsyncCallback:
return BuildGraphOfAsyncFfiCallback(function); return BuildGraphOfAsyncFfiCallback(function);
case FfiFunctionKind::kCall: case FfiFunctionKind::kCall:
return BuildGraphOfFfiNative(function); return BuildGraphOfFfiCall(function);
} }
UNREACHABLE(); UNREACHABLE();
return nullptr; 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 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_ = // This can only be Pointer, so it is always safe to LoadUntagged.
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId); body += LoadUntagged(compiler::target::PointerBase::data_offset());
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
return body;
}
auto normal_entry = BuildFunctionEntry(graph_entry_); Fragment FlowGraphBuilder::FfiNativeFunctionBody(const Function& function) {
graph_entry_->set_normal_entry(normal_entry); 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 = Fragment body;
BuildPrologue(normal_entry, &prologue_info); body += FfiNativeLookupAddress(function);
body += FfiCallFunctionBody(function, c_signature);
return body;
}
Fragment function_body(instruction_cursor); Fragment FlowGraphBuilder::FfiCallFunctionBody(
function_body += CheckStackOverflowInPrologue(function.token_pos()); 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 char* error = nullptr;
const auto marshaller_ptr = const auto marshaller_ptr = compiler::ffi::CallMarshaller::FromFunction(
compiler::ffi::CallMarshaller::FromFunction(Z, function, &error); Z, function, c_signature, &error);
// AbiSpecific integers can be incomplete causing us to not know the calling // AbiSpecific integers can be incomplete causing us to not know the calling
// convention. However, this is caught in asFunction in both JIT/AOT. // convention. However, this is caught in asFunction in both JIT/AOT.
RELEASE_ASSERT(error == nullptr); RELEASE_ASSERT(error == nullptr);
@ -5030,21 +5131,20 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
if (marshaller.IsHandle(i)) { if (marshaller.IsHandle(i)) {
continue; continue;
} }
function_body += LoadLocal( body += LoadLocal(parsed_function_->ParameterVariable(
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i)); first_argument_parameter_offset + i));
// TODO(http://dartbug.com/47486): Support entry without checking for null. // TODO(http://dartbug.com/47486): Support entry without checking for null.
// Check for 'null'. // Check for 'null'.
function_body += CheckNullOptimized( body += CheckNullOptimized(
String::ZoneHandle( String::ZoneHandle(
Z, function.ParameterNameAt(kFirstArgumentParameterOffset + i)), Z, function.ParameterNameAt(first_argument_parameter_offset + i)),
CheckNullInstr::kArgumentError); CheckNullInstr::kArgumentError);
function_body += StoreLocal( body += StoreLocal(TokenPosition::kNoSource,
TokenPosition::kNoSource, parsed_function_->ParameterVariable(
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i)); first_argument_parameter_offset + i));
function_body += Drop(); body += Drop();
} }
Fragment body;
intptr_t try_handler_index = -1; intptr_t try_handler_index = -1;
if (signature_contains_handles) { if (signature_contains_handles) {
// Wrap in Try catch to transition from Native to Generated on a throw from // 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++) { for (intptr_t i = 0; i < marshaller.num_args(); i++) {
if (marshaller.IsCompound(i)) { if (marshaller.IsCompound(i)) {
body += FfiCallConvertCompoundArgumentToNative( body += FfiCallConvertCompoundArgumentToNative(
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + parsed_function_->ParameterVariable(first_argument_parameter_offset +
i), i),
marshaller, i); marshaller, i);
} else { } else {
body += LoadLocal(parsed_function_->ParameterVariable( body += LoadLocal(parsed_function_->ParameterVariable(
kFirstArgumentParameterOffset + i)); first_argument_parameter_offset + i));
// FfiCallInstr specifies all handle locations as Stack, and will pass a // FfiCallInstr specifies all handle locations as Stack, and will pass a
// pointer to the stack slot as the native handle argument. // pointer to the stack slot as the native handle argument.
// Therefore we do not need to wrap handles. // 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 body += LoadLocal(address);
// 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);
if (marshaller.PassTypedData()) { if (marshaller.PassTypedData()) {
body += LoadLocal(typed_data); body += LoadLocal(typed_data);
@ -5111,7 +5198,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
for (intptr_t i = 0; i < marshaller.num_args(); i++) { for (intptr_t i = 0; i < marshaller.num_args(); i++) {
if (marshaller.IsPointer(i)) { if (marshaller.IsPointer(i)) {
body += LoadLocal(parsed_function_->ParameterVariable( body += LoadLocal(parsed_function_->ParameterVariable(
kFirstArgumentParameterOffset + i)); first_argument_parameter_offset + i));
body += ReachabilityFence(); body += ReachabilityFence();
} }
} }
@ -5119,12 +5206,12 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
const intptr_t num_defs = marshaller.NumReturnDefinitions(); const intptr_t num_defs = marshaller.NumReturnDefinitions();
ASSERT(num_defs >= 1); ASSERT(num_defs >= 1);
auto defs = new (Z) ZoneGrowableArray<LocalVariable*>(Z, num_defs); auto defs = new (Z) ZoneGrowableArray<LocalVariable*>(Z, num_defs);
LocalVariable* def = MakeTemporary(); LocalVariable* def = MakeTemporary("ffi call result");
defs->Add(def); defs->Add(def);
if (marshaller.PassTypedData()) { if (marshaller.PassTypedData()) {
// Drop call result, typed data with contents is already on the stack. // Drop call result, typed data with contents is already on the stack.
body += Drop(); body += DropTemporary(&def);
} }
if (marshaller.IsCompound(compiler::ffi::kResultIndex)) { if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
@ -5141,14 +5228,13 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
body += ExitHandleScope(); body += ExitHandleScope();
} }
body += DropTempsPreserveTop(1); // Drop address.
body += Return(TokenPosition::kNoSource); body += Return(TokenPosition::kNoSource);
if (signature_contains_handles) { if (signature_contains_handles) {
--try_depth_; --try_depth_;
} }
function_body += body;
if (signature_contains_handles) { if (signature_contains_handles) {
++catch_depth_; ++catch_depth_;
Fragment catch_body = Fragment catch_body =
@ -5167,6 +5253,30 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
--catch_depth_; --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_, return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
prologue_info); prologue_info);
} }

View file

@ -137,8 +137,14 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
FlowGraph* BuildGraphOfFfiTrampoline(const Function& function); FlowGraph* BuildGraphOfFfiTrampoline(const Function& function);
FlowGraph* BuildGraphOfSyncFfiCallback(const Function& function); FlowGraph* BuildGraphOfSyncFfiCallback(const Function& function);
FlowGraph* BuildGraphOfAsyncFfiCallback(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, Fragment NativeFunctionBody(const Function& function,
LocalVariable* first_parameter); LocalVariable* first_parameter);
Fragment LoadNativeArg(const compiler::ffi::CallbackMarshaller& marshaller, Fragment LoadNativeArg(const compiler::ffi::CallbackMarshaller& marshaller,

View file

@ -155,7 +155,19 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
// NOTE: FunctionNode is read further below the if. // NOTE: FunctionNode is read further below the if.
intptr_t pos = 0; 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( LocalVariable* closure_parameter = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource, TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ClosureParameter(), AbstractType::dynamic_type()); 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")) { "vm:isolate-unsendable")) {
*pragma_bits = IsolateUnsendablePragma::update(true, *pragma_bits); *pragma_bits = IsolateUnsendablePragma::update(true, *pragma_bits);
} }
if (constant_reader.IsStringConstant(name_index, "vm:ffi:native")) {
*pragma_bits = FfiNativePragma::update(true, *pragma_bits);
}
} }
} else { } else {
helper_.SkipExpression(); 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. // they are not reachable anymore and we never look them up by name.
const bool register_function = !name.Equals(Symbols::DebugProcedureName()); 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 FunctionType& signature = FunctionType::Handle(Z, FunctionType::New());
const Function& function = Function::ZoneHandle( const Function& function = Function::ZoneHandle(
Z, Function::New(signature, name, kind, Z, Function::New(signature, name, kind,
!is_method, // is_static !is_method, // is_static
false, // is_const false, // is_const
is_abstract, is_external, is_abstract, is_external,
!native_name.IsNull(), // is_native !native_name.IsNull() || is_ffi_native, // is_native
script_class, procedure_helper.start_position_)); script_class, procedure_helper.start_position_));
function.set_has_pragma(HasPragma::decode(pragma_bits)); function.set_has_pragma(HasPragma::decode(pragma_bits));
function.set_end_token_pos(procedure_helper.end_position_); 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>; BitField<uint32_t, bool, ExternalNamePragma::kNextBit, 1>;
using IsolateUnsendablePragma = using IsolateUnsendablePragma =
BitField<uint32_t, bool, InvisibleFunctionPragma::kNextBit, 1>; BitField<uint32_t, bool, InvisibleFunctionPragma::kNextBit, 1>;
using FfiNativePragma =
BitField<uint32_t, bool, IsolateUnsendablePragma::kNextBit, 1>;
void FinishTopLevelClassLoading(const Class& toplevel_class, void FinishTopLevelClassLoading(const Class& toplevel_class,
const Library& library, const Library& library,

View file

@ -175,7 +175,7 @@ class NativeArguments {
} }
static intptr_t ParameterCountForResolution(const Function& function) { static intptr_t ParameterCountForResolution(const Function& function) {
ASSERT(function.is_native()); ASSERT(function.is_old_native());
ASSERT(!function.IsGenerativeConstructor()); // Not supported. ASSERT(!function.IsGenerativeConstructor()); // Not supported.
intptr_t count = function.NumParameters(); intptr_t count = function.NumParameters();
if (function.is_static() && function.IsClosureFunction()) { if (function.is_static() && function.IsClosureFunction()) {
@ -189,7 +189,7 @@ class NativeArguments {
} }
static int ComputeArgcTag(const Function& function) { static int ComputeArgcTag(const Function& function) {
ASSERT(function.is_native()); ASSERT(function.is_old_native());
ASSERT(!function.IsGenerativeConstructor()); // Not supported. ASSERT(!function.IsGenerativeConstructor()); // Not supported.
int argc = function.NumParameters(); int argc = function.NumParameters();
int function_bits = 0; 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()); IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
ASSERT(!IsClosureFunction()); ASSERT(!IsClosureFunction());
const Object& old_data = Object::Handle(data()); const Object& old_data = Object::Handle(data());
if (is_native()) { if (is_old_native()) {
ASSERT(old_data.IsArray()); ASSERT(old_data.IsArray());
const auto& pair = Array::Cast(old_data); const auto& pair = Array::Cast(old_data);
ASSERT(pair.AtAcquire(NativeFunctionData::kTearOff) == Object::null() || ASSERT(pair.AtAcquire(NativeFunctionData::kTearOff) == Object::null() ||
@ -8378,26 +8378,38 @@ void Function::SetFfiCSignature(const FunctionType& sig) const {
} }
FunctionTypePtr Function::FfiCSignature() const { FunctionTypePtr Function::FfiCSignature() const {
ASSERT(IsFfiTrampoline()); auto* const zone = Thread::Current()->zone();
const Object& obj = Object::Handle(data()); if (IsFfiTrampoline()) {
ASSERT(!obj.IsNull()); const Object& obj = Object::Handle(zone, data());
return FfiTrampolineData::Cast(obj).c_signature(); 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 { bool Function::FfiCSignatureContainsHandles() const {
ASSERT(IsFfiTrampoline());
const FunctionType& c_signature = FunctionType::Handle(FfiCSignature()); 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++) { for (intptr_t i = 0; i < num_params; i++) {
const bool is_handle = const bool is_handle =
AbstractType::Handle(c_signature.ParameterTypeAt(i)).type_class_id() == AbstractType::Handle(ParameterTypeAt(i)).type_class_id() ==
kFfiHandleCid; kFfiHandleCid;
if (is_handle) { if (is_handle) {
return true; return true;
} }
} }
return AbstractType::Handle(c_signature.result_type()).type_class_id() == return AbstractType::Handle(result_type()).type_class_id() == kFfiHandleCid;
kFfiHandleCid;
} }
// Keep consistent with BaseMarshaller::IsCompound. // Keep consistent with BaseMarshaller::IsCompound.
@ -8453,10 +8465,22 @@ void Function::AssignFfiCallbackId(int32_t callback_id) const {
} }
bool Function::FfiIsLeaf() const { bool Function::FfiIsLeaf() const {
ASSERT(IsFfiTrampoline()); if (IsFfiTrampoline()) {
const Object& obj = Object::Handle(untag()->data()); const Object& obj = Object::Handle(untag()->data());
ASSERT(!obj.IsNull()); ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).is_leaf(); 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 { 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); 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 { void Function::SetSignature(const FunctionType& value) const {
set_signature(value); set_signature(value);
ASSERT(NumImplicitParameters() == value.num_implicit_parameters()); ASSERT(NumImplicitParameters() == value.num_implicit_parameters());
@ -8998,7 +9048,7 @@ bool Function::IsOptimizable() const {
return true; return true;
} }
if (ForceOptimize()) return true; if (ForceOptimize()) return true;
if (is_native()) { if (is_old_native()) {
// Native methods don't need to be optimized. // Native methods don't need to be optimized.
return false; return false;
} }
@ -9081,7 +9131,7 @@ static bool InVmTests(const Function& function) {
} }
bool Function::ForceOptimize() const { bool Function::ForceOptimize() const {
if (RecognizedKindForceOptimize() || IsFfiTrampoline() || if (RecognizedKindForceOptimize() || IsFfiTrampoline() || is_ffi_native() ||
IsTypedDataViewFactory() || IsUnmodifiableTypedDataViewFactory()) { IsTypedDataViewFactory() || IsUnmodifiableTypedDataViewFactory()) {
return true; return true;
} }
@ -9196,7 +9246,7 @@ bool Function::RecognizedKindForceOptimize() const {
#if !defined(DART_PRECOMPILED_RUNTIME) #if !defined(DART_PRECOMPILED_RUNTIME)
bool Function::CanBeInlined() const { bool Function::CanBeInlined() const {
if (ForceOptimize()) { if (ForceOptimize()) {
if (IsFfiTrampoline()) { if (IsFfiTrampoline() || is_ffi_native()) {
// We currently don't support inlining FFI trampolines. Some of them // We currently don't support inlining FFI trampolines. Some of them
// are naturally non-inlinable because they contain a try/catch block, // are naturally non-inlinable because they contain a try/catch block,
// but this condition is broader than strictly necessary. // but this condition is broader than strictly necessary.
@ -10295,7 +10345,7 @@ FunctionPtr Function::New(const FunctionType& signature,
const FfiTrampolineData& data = const FfiTrampolineData& data =
FfiTrampolineData::Handle(FfiTrampolineData::New()); FfiTrampolineData::Handle(FfiTrampolineData::New());
result.set_data(data); result.set_data(data);
} else if (is_native) { } else if (result.is_old_native()) {
const auto& data = const auto& data =
Array::Handle(Array::New(NativeFunctionData::kLength, Heap::kOld)); Array::Handle(Array::New(NativeFunctionData::kLength, Heap::kOld));
result.set_data(data); result.set_data(data);

View file

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

View file

@ -543,6 +543,16 @@ void ObjectStore::LazyInitFfiMembers() {
ASSERT(!function.IsNull()); ASSERT(!function.IsNull());
handle_native_finalizer_message_function_.store(function.ptr()); 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()); cls = ffi_lib.LookupClass(Symbols::VarArgs());
ASSERT(!cls.IsNull()); ASSERT(!cls.IsNull());
varargs_class_.store(cls.ptr()); varargs_class_.store(cls.ptr());

View file

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

View file

@ -116,6 +116,7 @@ class ObjectPointerVisitor;
V(FfiInt8, "Int8") \ V(FfiInt8, "Int8") \
V(FfiIntPtr, "IntPtr") \ V(FfiIntPtr, "IntPtr") \
V(FfiIsolateLocalCallback, "_FfiIsolateLocalCallback") \ V(FfiIsolateLocalCallback, "_FfiIsolateLocalCallback") \
V(FfiNative, "Native") \
V(FfiNativeFunction, "NativeFunction") \ V(FfiNativeFunction, "NativeFunction") \
V(FfiNativeType, "NativeType") \ V(FfiNativeType, "NativeType") \
V(FfiNativeTypes, "nativeTypes") \ V(FfiNativeTypes, "nativeTypes") \
@ -426,6 +427,7 @@ class ObjectPointerVisitor;
V(_checkSetRangeArguments, "_checkSetRangeArguments") \ V(_checkSetRangeArguments, "_checkSetRangeArguments") \
V(_current, "_current") \ V(_current, "_current") \
V(_ensureScheduleImmediate, "_ensureScheduleImmediate") \ V(_ensureScheduleImmediate, "_ensureScheduleImmediate") \
V(_ffi_resolver_function, "_ffi_resolver_function") \
V(future, "future") \ V(future, "future") \
V(_future, "_future") \ V(_future, "_future") \
V(_getRegisters, "_getRegisters") \ V(_getRegisters, "_getRegisters") \
@ -525,6 +527,7 @@ class ObjectPointerVisitor;
V(vm_exact_result_type, "vm:exact-result-type") \ V(vm_exact_result_type, "vm:exact-result-type") \
V(vm_external_name, "vm:external-name") \ V(vm_external_name, "vm:external-name") \
V(vm_ffi_abi_specific_mapping, "vm:ffi:abi-specific-mapping") \ 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_native_assets, "vm:ffi:native-assets") \
V(vm_ffi_struct_fields, "vm:ffi:struct-fields") \ V(vm_ffi_struct_fields, "vm:ffi:struct-fields") \
V(vm_force_optimize, "vm:force-optimize") \ 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 => Object get _typedDataBase =>
throw UnsupportedError('_ArraySize._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< final _ffi_resolver = _get_ffi_native_resolver<
NativeFunction<IntPtr Function(Handle, Handle, IntPtr)>>() NativeFunction<IntPtr Function(Handle, Handle, IntPtr)>>()
.asFunction<int Function(Object, Object, int)>(); .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);