Revert "[vm/ffi] Optimize @Native calls"

This reverts commit e16bb210d2.

Reason for revert: Indication this caused engine test
failures of the kind:
```
[ RUN      ] EmbedderA11yTest.A11yTreeIsConsistentUsingV1Callbacks
../../flutter/shell/platform/embedder/tests/embedder_a11y_unittests.cc:639: Failure
Expected equality of these values:
  std::strncmp(kTooltip, node->tooltip, sizeof(kTooltip) - 1)
    Which is: 116
  0
```

Original change's description:
> [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>

Change-Id: Icc87a6ca33bffecabb15c6b168a06ccc38c2fe5b
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
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/333840
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann 2023-11-03 09:04:24 +00:00
parent 85e1bbf45c
commit 96921245f9
34 changed files with 898 additions and 1345 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -27,17 +27,15 @@ final class Struct1ByteInt extends ffi::Struct {
static get #sizeOf() → core::int*
return #C13.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
static final field (core::int) → self::Struct1ByteInt _returnStruct1ByteIntNative$Method$FfiNative$Ptr = block {
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase(typ::Uint8List::•(#C12)));
} =>ffi::_asFunctionInternal<(core::int) → self::Struct1ByteInt, (ffi::Int8) → self::Struct1ByteInt>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Int8) → self::Struct1ByteInt>*>(ffi::_ffi_resolver(#C14, #C15, #C12){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
static method main() → void {
final self::Struct1ByteInt result = self::returnStruct1ByteIntNative(1.{core::int::unary-}(){() → core::int});
core::print("result = ${result}");
}
@#C11
static method returnStruct1ByteIntNative(core::int a0) → self::Struct1ByteInt
return 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;
return self::_returnStruct1ByteIntNative$Method$FfiNative$Ptr(a0){(core::int) → self::Struct1ByteInt};
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8)
@ -52,10 +50,6 @@ constants {
#C11 = core::pragma {name:#C10, options:#C4}
#C12 = 1
#C13 = <core::int*>[#C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12]
#C14 = "vm:ffi:native"
#C14 = "#lib"
#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,26 +22,3 @@ Related files:
* [pkg/vm/test/native_assets_validator_test.dart](../../../pkg/vm/test/native_assets_validator_test.dart)
* [runtime/lib/ffi_dynamic_library.cc](../../../runtime/lib/ffi_dynamic_library.cc)
* [runtime/vm/ffi/native_assets.cc](../../../runtime/vm/ffi/native_assets.cc)
## Native
The fully expanded version of `@Native` passed to the VM.
```
@pragma(
'vm:ffi:native',
Native<IntPtr Function(Pointer<Void>, IntPtr)>(
symbol: 'MyClass_MyMethod',
assetId: '<library uri>',
),
)
external int _myFunction$FfiNative(Pointer<Void> self, int x);
```
This is passed as a pragma so it is treated consistently with other pragmas.
Related files:
* [runtime/vm/kernel_loader.cc](../../../runtime/vm/kernel_loader.cc)
* [runtime/vm/object.cc](../../../runtime/vm/object.cc)

View file

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

View file

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

View file

@ -1,20 +0,0 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_
#define RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_
#include "platform/globals.h"
#include "vm/object.h"
namespace dart {
intptr_t FfiResolveInternal(const String& asset,
const String& symbol,
uintptr_t args_n,
char** error);
} // namespace dart
#endif // RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -155,19 +155,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
// NOTE: FunctionNode is read further below the if.
intptr_t pos = 0;
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()) {
if (function.IsClosureFunction()) {
LocalVariable* closure_parameter = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ClosureParameter(), AbstractType::dynamic_type());

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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