mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
[vm/ffi] Optimize @Native
calls
This CL removes static fields for storing the `@Native`'s function addresses. Instead, the function addresses are stored in the object pool for all archs except for ia32. ia32 has no AOT and no AppJit snapshots, so the addresses are directly embedded in the code. This CL removes the closure wrapping for `@Native`s. Instead of `pointer.asFunctionInternal()()` where `asFunction` returns a closure which contains the trampoline, the function is compiled to a body which contains the trampoline `Native()`. This is possible for `@Native`s because the dylib and symbol names are known statically. Doing the compilation in kernel_to_il instead of a CFE transform enables supporting static linking later. (The alternative would have been to transform in the cfe to a `@pragma('vm:cachable-idempotent')` instead of constructing the IL in kernel_to_il. To enable running resolution in ia32 in kernel_to_il.cc, the resolution function has been made available via `runtime/lib/ffi_dynamic_library.h`. Because the new calls are simply static calls, the TFA can figure out const arguments flowing to these calls. This leads to constant locations in the parameters to FfiCalls. So, this CL also introduces logic to move constants into `NativeLocation`s. TEST=runtime/vm/compiler/backend/il_test.cc TEST=tests/ffi/function_*_native_(leaf_)test.dart TEST=pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart Closes: https://github.com/dart-lang/sdk/issues/47625 Closes: https://github.com/dart-lang/sdk/issues/51618 Change-Id: Ic5d017005dedcedea40c455c4d24dbe774f91603 CoreLibraryReviewExempt: Internal FFI implementation changes Cq-Include-Trybots: luci.dart.try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64c-try,vm-aot-win-release-x64-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-x64-try,vm-kernel-linux-debug-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/284300 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
600acad478
commit
e16bb210d2
|
@ -1160,19 +1160,31 @@ class FfiTransformer extends Transformer {
|
|||
]))
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
if (dartSignature is FunctionType) {
|
||||
final returnType = dartSignature.returnType;
|
||||
if (returnType is InterfaceType) {
|
||||
final clazz = returnType.classNode;
|
||||
if (clazz.superclass == structClass || clazz.superclass == unionClass) {
|
||||
return invokeCompoundConstructor(asFunctionInternalInvocation, clazz);
|
||||
}
|
||||
}
|
||||
final possibleCompoundReturn = findCompoundReturnType(dartSignature);
|
||||
if (possibleCompoundReturn != null) {
|
||||
return invokeCompoundConstructor(
|
||||
asFunctionInternalInvocation, possibleCompoundReturn);
|
||||
}
|
||||
|
||||
return asFunctionInternalInvocation;
|
||||
}
|
||||
|
||||
/// Returns the compound [Class] if a compound is returned, otherwise `null`.
|
||||
Class? findCompoundReturnType(DartType dartSignature) {
|
||||
if (dartSignature is! FunctionType) {
|
||||
return null;
|
||||
}
|
||||
final returnType = dartSignature.returnType;
|
||||
if (returnType is! InterfaceType) {
|
||||
return null;
|
||||
}
|
||||
final clazz = returnType.classNode;
|
||||
if (clazz.superclass == structClass || clazz.superclass == unionClass) {
|
||||
return clazz;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns
|
||||
/// - `true` if leaf
|
||||
/// - `false` if not leaf
|
||||
|
|
|
@ -183,81 +183,6 @@ class FfiNativeTransformer extends FfiTransformer {
|
|||
);
|
||||
}
|
||||
|
||||
// Create field holding the resolved native function pointer.
|
||||
//
|
||||
// For:
|
||||
// @FfiNative<IntPtr Function(Pointer<Void>)>('DoXYZ', isLeaf:true)
|
||||
// external int doXyz(NativeFieldWrapperClass1 obj);
|
||||
//
|
||||
// Create:
|
||||
// static final _doXyz$FfiNative$ptr =
|
||||
// Pointer<NativeFunction<IntPtr Function(Pointer<Void>)>>
|
||||
// .fromAddress(_ffi_resolver('..', 'DoXYZ', 1))
|
||||
// .asFunction<int Function(Pointer<Void>)>(isLeaf:true);
|
||||
Field _createResolvedFfiNativeField(
|
||||
String dartFunctionName,
|
||||
StringConstant nativeFunctionName,
|
||||
StringConstant? assetName,
|
||||
bool isLeaf,
|
||||
FunctionType dartFunctionType,
|
||||
FunctionType ffiFunctionType,
|
||||
int fileOffset,
|
||||
Uri fileUri,
|
||||
) {
|
||||
// Derive number of arguments from the native function signature.
|
||||
final numberNativeArgs = ffiFunctionType.positionalParameters.length;
|
||||
|
||||
final nativeFunctionType = InterfaceType(
|
||||
nativeFunctionClass,
|
||||
Nullability.legacy,
|
||||
<DartType>[ffiFunctionType],
|
||||
);
|
||||
|
||||
// _ffi_resolver('...', 'DoXYZ', 1)
|
||||
final resolverInvocation = FunctionInvocation(
|
||||
FunctionAccessKind.FunctionType,
|
||||
StaticGet(resolverField),
|
||||
Arguments(<Expression>[
|
||||
ConstantExpression(
|
||||
assetName ?? StringConstant(currentLibrary.importUri.toString())),
|
||||
ConstantExpression(nativeFunctionName),
|
||||
ConstantExpression(IntConstant(numberNativeArgs)),
|
||||
]),
|
||||
functionType: resolverField.type as FunctionType)
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
// _fromAddress<NativeFunction<Double Function(Double)>>(...)
|
||||
final functionPointerExpression = StaticInvocation(
|
||||
fromAddressInternal,
|
||||
Arguments(
|
||||
<Expression>[resolverInvocation],
|
||||
types: [nativeFunctionType],
|
||||
))
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
final asFunctionInvocation = buildAsFunctionInternal(
|
||||
functionPointer: functionPointerExpression,
|
||||
dartSignature: dartFunctionType,
|
||||
nativeSignature: ffiFunctionType,
|
||||
isLeaf: isLeaf,
|
||||
fileOffset: fileOffset,
|
||||
);
|
||||
|
||||
// static final _doXyz$FfiNative$Ptr = ...
|
||||
final fieldName =
|
||||
Name('_$dartFunctionName\$FfiNative\$Ptr', currentLibrary);
|
||||
final functionPointerField = Field.immutable(fieldName,
|
||||
type: dartFunctionType,
|
||||
initializer: asFunctionInvocation,
|
||||
isStatic: true,
|
||||
isFinal: true,
|
||||
fileUri: fileUri,
|
||||
getterReference: currentLibraryIndex?.lookupGetterReference(fieldName))
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
return functionPointerField;
|
||||
}
|
||||
|
||||
// Whether a parameter of [dartParameterType], passed as [ffiParameterType],
|
||||
// needs to be converted to Pointer.
|
||||
bool _requiresPointerConversion(
|
||||
|
@ -350,7 +275,7 @@ class FfiNativeTransformer extends FfiTransformer {
|
|||
// reachabilityFence(#t0);
|
||||
// } => #t1
|
||||
Expression _wrapArgumentsAndReturn({
|
||||
required FunctionInvocation invocation,
|
||||
required StaticInvocation invocation,
|
||||
required FunctionType dartFunctionType,
|
||||
required FunctionType ffiFunctionType,
|
||||
bool checkReceiverForNullptr = false,
|
||||
|
@ -487,6 +412,8 @@ class FfiNativeTransformer extends FfiTransformer {
|
|||
return validSignature;
|
||||
}
|
||||
|
||||
static const vmFfiNative = 'vm:ffi:native';
|
||||
|
||||
Procedure _transformProcedure(
|
||||
Procedure node,
|
||||
StringConstant nativeFunctionName,
|
||||
|
@ -508,74 +435,135 @@ class FfiNativeTransformer extends FfiTransformer {
|
|||
return node;
|
||||
}
|
||||
|
||||
final parent = node.parent;
|
||||
|
||||
// static final _myMethod$FfiNative$Ptr = ..
|
||||
final resolvedField = _createResolvedFfiNativeField(
|
||||
'${node.name.text}\$${node.kind.name}',
|
||||
nativeFunctionName,
|
||||
assetName,
|
||||
isLeaf,
|
||||
wrappedDartFunctionType,
|
||||
ffiFunctionType,
|
||||
node.fileOffset,
|
||||
node.fileUri,
|
||||
final pragmaConstant = ConstantExpression(
|
||||
InstanceConstant(pragmaClass.reference, [], {
|
||||
pragmaName.fieldReference: StringConstant(vmFfiNative),
|
||||
pragmaOptions.fieldReference: InstanceConstant(
|
||||
nativeClass.reference,
|
||||
[ffiFunctionType],
|
||||
{
|
||||
nativeSymbolField.fieldReference: nativeFunctionName,
|
||||
nativeAssetField.fieldReference: assetName ??
|
||||
StringConstant(currentLibrary.importUri.toString()),
|
||||
nativeIsLeafField.fieldReference: BoolConstant(isLeaf),
|
||||
},
|
||||
)
|
||||
}),
|
||||
InterfaceType(
|
||||
pragmaClass,
|
||||
Nullability.nonNullable,
|
||||
[],
|
||||
),
|
||||
);
|
||||
|
||||
// Add field to the parent the FfiNative function belongs to.
|
||||
final possibleCompoundReturn = findCompoundReturnType(dartFunctionType);
|
||||
if (dartFunctionType == wrappedDartFunctionType &&
|
||||
node.isStatic &&
|
||||
possibleCompoundReturn == null) {
|
||||
// We are not wrapping/unwrapping arguments or return value.
|
||||
node.addAnnotation(pragmaConstant);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Introduce a new function as external with the annotation and give the
|
||||
// current function a body that does the wrapping/unwrapping.
|
||||
node.isExternal = false;
|
||||
|
||||
node.addAnnotation(ConstantExpression(
|
||||
InstanceConstant(pragmaClass.reference, [], {
|
||||
pragmaName.fieldReference: StringConstant("vm:prefer-inline"),
|
||||
pragmaOptions.fieldReference: NullConstant(),
|
||||
}),
|
||||
));
|
||||
|
||||
final parent = node.parent;
|
||||
var fileUri = currentLibrary.fileUri;
|
||||
if (parent is Class) {
|
||||
parent.addField(resolvedField);
|
||||
fileUri = parent.fileUri;
|
||||
} else if (parent is Library) {
|
||||
parent.addField(resolvedField);
|
||||
fileUri = parent.fileUri;
|
||||
}
|
||||
|
||||
int varCounter = 0;
|
||||
final nonWrappedFfiNative = Procedure(
|
||||
Name('_${node.name.text}\$${node.kind.name}\$FfiNative', currentLibrary),
|
||||
ProcedureKind.Method,
|
||||
FunctionNode(
|
||||
/*body=*/ null,
|
||||
requiredParameterCount: wrappedDartFunctionType.requiredParameterCount,
|
||||
positionalParameters: [
|
||||
for (final positionalParameter
|
||||
in wrappedDartFunctionType.positionalParameters)
|
||||
VariableDeclaration(
|
||||
/*name=*/ '#t${varCounter++}',
|
||||
type: positionalParameter,
|
||||
)..fileOffset = node.fileOffset,
|
||||
],
|
||||
returnType: wrappedDartFunctionType.returnType,
|
||||
)..fileOffset = node.fileOffset,
|
||||
fileUri: fileUri,
|
||||
isStatic: true,
|
||||
isExternal: true,
|
||||
)
|
||||
..isNonNullableByDefault = node.isNonNullableByDefault
|
||||
..fileOffset = node.fileOffset;
|
||||
nonWrappedFfiNative.addAnnotation(pragmaConstant);
|
||||
|
||||
// Add procedure to the parent the FfiNative function belongs to.
|
||||
if (parent is Class) {
|
||||
parent.addProcedure(nonWrappedFfiNative);
|
||||
} else if (parent is Library) {
|
||||
parent.addProcedure(nonWrappedFfiNative);
|
||||
} else {
|
||||
throw 'Unexpected parent of @FfiNative function. '
|
||||
throw 'Unexpected parent of @Native function. '
|
||||
'Expected Class or Library, but found ${parent}.';
|
||||
}
|
||||
|
||||
// _myFunction$FfiNative$Ptr(obj, x)
|
||||
final functionPointerInvocation = FunctionInvocation(
|
||||
FunctionAccessKind.FunctionType,
|
||||
StaticGet(resolvedField),
|
||||
final nonWrappedInvocation = StaticInvocation(
|
||||
nonWrappedFfiNative,
|
||||
Arguments(argumentList),
|
||||
functionType: wrappedDartFunctionType)
|
||||
..fileOffset = node.fileOffset;
|
||||
)..fileOffset = node.fileOffset;
|
||||
|
||||
Expression result = (wrappedDartFunctionType == dartFunctionType
|
||||
? functionPointerInvocation
|
||||
? nonWrappedInvocation
|
||||
: _wrapArgumentsAndReturn(
|
||||
invocation: functionPointerInvocation,
|
||||
invocation: nonWrappedInvocation,
|
||||
dartFunctionType: dartFunctionType,
|
||||
ffiFunctionType: ffiFunctionType,
|
||||
checkReceiverForNullptr: checkReceiverForNullptr,
|
||||
));
|
||||
if (possibleCompoundReturn != null) {
|
||||
result = invokeCompoundConstructor(result, possibleCompoundReturn);
|
||||
}
|
||||
|
||||
// => _myFunction$FfiNative$Ptr(
|
||||
// Pointer<Void>.fromAddress(_getNativeField(obj)), x)
|
||||
node.function.body = ReturnStatement(result)..parent = node.function;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Transform FfiNative instance methods.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// class MyNativeClass extends NativeFieldWrapperClass1 {
|
||||
// @FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('MyClass_MyMethod')
|
||||
// @Native<IntPtr Function(Pointer<Void>, IntPtr)>(symbol: 'MyClass_MyMethod')
|
||||
// external int myMethod(int x);
|
||||
// }
|
||||
//
|
||||
// Becomes, roughly:
|
||||
// ... {
|
||||
// static final _myMethod$FfiNative$Ptr = ...
|
||||
// static _myMethod$FfiNative(MyNativeClass self, int x)
|
||||
// => _myMethod$FfiNative$Ptr(
|
||||
// Pointer<Void>.fromAddress(_getNativeField(self)), x);
|
||||
// int myMethod(int x) => _myMethod$FfiNative(this, x);
|
||||
// }
|
||||
//
|
||||
// ... {
|
||||
// static final _myMethod$FfiNative$Ptr = ...
|
||||
// int myMethod(int x)
|
||||
// => _myMethod$FfiNative$Ptr(
|
||||
// Pointer<Void>.fromAddress(_getNativeField(this)), x);
|
||||
// @pragma(
|
||||
// 'vm:ffi:native',
|
||||
// Native<IntPtr Function(Pointer<Void>, IntPtr)>(
|
||||
// symbol: 'MyClass_MyMethod',
|
||||
// assetId: '<library uri>',
|
||||
// ),
|
||||
// )
|
||||
// external int _myFunction$FfiNative(Pointer<Void> self, int x);
|
||||
//
|
||||
// @pragma('vm:prefer-inline')
|
||||
// int myMethod(int x) => _myMethod$FfiNative(_getNativeField(this), x);
|
||||
// }
|
||||
Procedure _transformInstanceMethod(
|
||||
Procedure node,
|
||||
|
@ -609,13 +597,26 @@ class FfiNativeTransformer extends FfiTransformer {
|
|||
}
|
||||
|
||||
// Transform FfiNative static functions.
|
||||
//
|
||||
// Example:
|
||||
// @FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('MyFunction')
|
||||
//
|
||||
// @Native<IntPtr Function(Pointer<Void>, IntPtr)>(symbol: 'MyFunction')
|
||||
// external int myFunction(MyNativeClass obj, int x);
|
||||
//
|
||||
// Becomes, roughly:
|
||||
// static final _myFunction$FfiNative$Ptr = ...
|
||||
//
|
||||
// @pragma(
|
||||
// 'vm:ffi:native',
|
||||
// Native<IntPtr Function(Pointer<Void>, IntPtr)>(
|
||||
// symbol: 'MyFunction',
|
||||
// assetId: '<library uri>',
|
||||
// ),
|
||||
// )
|
||||
// external int _myFunction$FfiNative(Pointer<Void> self, int x);
|
||||
//
|
||||
// @pragma('vm:prefer-inline')
|
||||
// int myFunction(MyNativeClass obj, int x)
|
||||
// => myFunction$FfiNative$Ptr(
|
||||
// => _myFunction$FfiNative(
|
||||
// Pointer<Void>.fromAddress(_getNativeField(obj)), x);
|
||||
Procedure _transformStaticFunction(
|
||||
Procedure node,
|
||||
|
@ -648,7 +649,7 @@ class FfiNativeTransformer extends FfiTransformer {
|
|||
@override
|
||||
visitProcedure(Procedure node) {
|
||||
// Only transform functions that are external and have FfiNative annotation:
|
||||
// @FfiNative<Double Function(Double)>('Math_sqrt')
|
||||
// @Native<Double Function(Double)>(symbol: 'Math_sqrt')
|
||||
// external double _square_root(double x);
|
||||
final ffiNativeAnnotation =
|
||||
tryGetNativeAnnotation(node) ?? tryGetFfiNativeAnnotation(node);
|
||||
|
@ -661,7 +662,6 @@ class FfiNativeTransformer extends FfiTransformer {
|
|||
1, node.location?.file);
|
||||
return node;
|
||||
}
|
||||
node.isExternal = false;
|
||||
|
||||
node.annotations.remove(ffiNativeAnnotation);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,129 +1,144 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:ffi" as ffi;
|
||||
import "dart:nativewrappers" as nat;
|
||||
import "dart:ffi" as ffi;
|
||||
import "dart:_internal" as _in;
|
||||
|
||||
import "dart:ffi";
|
||||
import "dart:nativewrappers";
|
||||
|
||||
abstract class Classy extends core::Object {
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (core::int) → core::int _returnIntPtrStatic$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.unboxing-info.metadata=()->i] static method returnIntPtrStatic() → core::int
|
||||
return self::Classy::_returnIntPtrStatic$Method$FfiNative$Ptr(#C4){(core::int) → core::int};
|
||||
[@vm.unboxing-info.metadata=(i)->i] @#C6
|
||||
external static method returnIntPtrStatic([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 222)] core::int x) → core::int;
|
||||
}
|
||||
class NativeClassy extends nat::NativeFieldWrapperClass1 {
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>, core::int) → void _goodHasReceiverPointer$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::int) → void, (ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (self::NativeClassy, core::int) → void _goodHasReceiverHandle$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::NativeClassy, core::int) → void, (ffi::Handle, ffi::IntPtr) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::IntPtr) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (self::NativeClassy, ffi::Pointer<ffi::Void>) → void _goodHasReceiverHandleAndPtr$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::NativeClassy, ffi::Pointer<ffi::Void>) → void, (ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (self::NativeClassy, self::NativeClassy) → void _goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::NativeClassy, self::NativeClassy) → void, (ffi::Handle, ffi::Handle) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::Handle) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>, self::NativeClassy) → void _goodHasReceiverPtrAndHandle$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, self::NativeClassy) → void, (ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>, core::bool) → core::Object? _meh$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → core::Object?, (ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>) → core::bool _blah$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void>) → ffi::Bool>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Bool>*>(ffi::_ffi_resolver(#C1, #C5, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>) → core::bool _myField$Getter$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void>) → ffi::Bool>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Bool>*>(ffi::_ffi_resolver(#C1, #C5, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure] static final field (ffi::Pointer<ffi::Void>, core::bool) → void _myField$Setter$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → void, (ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
|
||||
synthetic constructor •() → self::NativeClassy
|
||||
: super nat::NativeFieldWrapperClass1::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method goodHasReceiverPointer() → void
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] @#C9
|
||||
method goodHasReceiverPointer() → void
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t1 = this;
|
||||
final core::int #t2 = #C7;
|
||||
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final core::int #t2 = #C10;
|
||||
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t1);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C9);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C12);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress), #t2){(ffi::Pointer<ffi::Void>, core::int) → void};
|
||||
} =>#pointerAddress), #t2);
|
||||
_in::reachabilityFence(#t1);
|
||||
} =>#t3;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] method goodHasReceiverHandle() → void
|
||||
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative$Ptr(this, #C7){(self::NativeClassy, core::int) → void};
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] method goodHasReceiverHandleAndPtr([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] @#C9
|
||||
method goodHasReceiverHandle() → void
|
||||
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative(this, #C10);
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] @#C9
|
||||
method goodHasReceiverHandleAndPtr([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
|
||||
return block {
|
||||
final self::NativeClassy #t4 = this;
|
||||
final nat::NativeFieldWrapperClass1 #t5 = v;
|
||||
final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$Method$FfiNative$Ptr(#t4, ffi::_fromAddress<ffi::Void>([@vm.inferred-type.metadata=int] nat::_getNativeField(#t5))){(self::NativeClassy, ffi::Pointer<ffi::Void>) → void};
|
||||
final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$Method$FfiNative(#t4, [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>([@vm.inferred-type.metadata=int] nat::_getNativeField(#t5)));
|
||||
_in::reachabilityFence(#t5);
|
||||
} =>#t6;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] method goodHasReceiverHandleAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
|
||||
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr(this, v){(self::NativeClassy, self::NativeClassy) → void};
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10] method goodHasReceiverPtrAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] @#C9
|
||||
method goodHasReceiverHandleAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
|
||||
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative(this, v);
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10] @#C9
|
||||
method goodHasReceiverPtrAndHandle([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t7 = this;
|
||||
final self::NativeClassy #t8 = v;
|
||||
final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$Method$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t7);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C9);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C12);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress), #t8){(ffi::Pointer<ffi::Void>, self::NativeClassy) → void};
|
||||
} =>#pointerAddress), #t8);
|
||||
_in::reachabilityFence(#t7);
|
||||
} =>#t9;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12] method meh() → core::String?
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12] @#C9
|
||||
method meh() → core::String?
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t10 = this;
|
||||
final core::bool #t11 = #C10;
|
||||
final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final core::bool #t11 = #C13;
|
||||
final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$Method$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t10);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C9);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C12);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress), #t11){(ffi::Pointer<ffi::Void>, core::bool) → core::Object?});
|
||||
} =>#pointerAddress), #t11));
|
||||
_in::reachabilityFence(#t10);
|
||||
} =>#t12;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:13,getterSelectorId:14] method blah() → core::bool
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:13,getterSelectorId:14] @#C9
|
||||
method blah() → core::bool
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t13 = this;
|
||||
final core::bool #t14 = self::NativeClassy::_blah$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final core::bool #t14 = [@vm.inferred-type.metadata=dart.core::bool] self::NativeClassy::_blah$Method$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t13);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C9);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C12);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
|
||||
} =>#pointerAddress));
|
||||
_in::reachabilityFence(#t13);
|
||||
} =>#t14;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] get myField() → core::bool
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] @#C9
|
||||
get myField() → core::bool
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t15 = this;
|
||||
final core::bool #t16 = self::NativeClassy::_myField$Getter$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final core::bool #t16 = [@vm.inferred-type.metadata=dart.core::bool] self::NativeClassy::_myField$Getter$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t15);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C9);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C12);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
|
||||
} =>#pointerAddress));
|
||||
_in::reachabilityFence(#t15);
|
||||
} =>#t16;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] set myField([@vm.inferred-arg-type.metadata=dart.core::bool] core::bool value) → void
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] @#C9
|
||||
set myField([@vm.inferred-arg-type.metadata=dart.core::bool] core::bool value) → void
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t17 = this;
|
||||
final core::bool #t18 = value;
|
||||
final void #t19 = self::NativeClassy::_myField$Setter$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final void #t19 = self::NativeClassy::_myField$Setter$FfiNative([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = [@vm.inferred-type.metadata=int] nat::_getNativeField(#t17);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C9);
|
||||
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] #pointerAddress.{core::Object::==}(#C11){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C12);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress), #t18){(ffi::Pointer<ffi::Void>, core::bool) → void};
|
||||
} =>#pointerAddress), #t18);
|
||||
_in::reachabilityFence(#t17);
|
||||
} =>#t19;
|
||||
[@vm.unboxing-info.metadata=(b,i)->b] @#C16
|
||||
external static method _goodHasReceiverPointer$Method$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 175)] core::int #t1) → void;
|
||||
[@vm.unboxing-info.metadata=(b,i)->b] @#C18
|
||||
external static method _goodHasReceiverHandle$Method$FfiNative([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t0, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 175)] core::int #t1) → void;
|
||||
@#C20
|
||||
external static method _goodHasReceiverHandleAndPtr$Method$FfiNative([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t0, [@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t1) → void;
|
||||
@#C22
|
||||
external static method _goodHasReceiverHandleAndHandle$Method$FfiNative([@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t0, [@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t1) → void;
|
||||
@#C24
|
||||
external static method _goodHasReceiverPtrAndHandle$Method$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0, [@vm.inferred-arg-type.metadata=#lib::NativeClassy] self::NativeClassy #t1) → void;
|
||||
@#C26
|
||||
external static method _meh$Method$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0, [@vm.inferred-arg-type.metadata=dart.core::bool (value: true)] core::bool #t1) → core::Object?;
|
||||
@#C28
|
||||
external static method _blah$Method$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0) → core::bool;
|
||||
@#C30
|
||||
external static method _myField$Getter$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0) → core::bool;
|
||||
@#C32
|
||||
external static method _myField$Setter$FfiNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Void> #t0, [@vm.inferred-arg-type.metadata=dart.core::bool] core::bool #t1) → void;
|
||||
}
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure]static final field (core::int) → core::int _returnIntPtr$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure]static final field (core::int) → core::int _returnIntPtrLeaf$Method$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
|
||||
[@vm.unboxing-info.metadata=()->i]static method returnIntPtr() → core::int
|
||||
return self::_returnIntPtr$Method$FfiNative$Ptr(#C11){(core::int) → core::int};
|
||||
[@vm.unboxing-info.metadata=()->i]static method returnIntPtrLeaf() → core::int
|
||||
return self::_returnIntPtrLeaf$Method$FfiNative$Ptr(#C12){(core::int) → core::int};
|
||||
[@vm.unboxing-info.metadata=(i)->i]@#C6
|
||||
external static method returnIntPtr([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 13)] core::int x) → core::int;
|
||||
[@vm.unboxing-info.metadata=(i)->i]@#C34
|
||||
external static method returnIntPtrLeaf([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 37)] core::int x) → core::int;
|
||||
static method main() → void {
|
||||
self::returnIntPtr();
|
||||
self::returnIntPtrLeaf();
|
||||
self::Classy::returnIntPtrStatic();
|
||||
self::returnIntPtr(13);
|
||||
self::returnIntPtrLeaf(37);
|
||||
self::Classy::returnIntPtrStatic(222);
|
||||
[@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverPointer] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverPointer}(){(core::int) → void};
|
||||
[@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverHandle] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandle}(){(core::int) → void};
|
||||
[@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverHandleAndPtr] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandleAndPtr}(new self::NativeClassy::•()){(self::NativeClassy) → void};
|
||||
|
@ -135,16 +150,38 @@ static method main() → void {
|
|||
[@vm.direct-call.metadata=#lib::NativeClassy.myField] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::myField} = !b;
|
||||
}
|
||||
constants {
|
||||
#C1 = "#lib"
|
||||
#C1 = "vm:ffi:native"
|
||||
#C2 = "ReturnIntPtr"
|
||||
#C3 = 1
|
||||
#C4 = 222
|
||||
#C5 = "doesntmatter"
|
||||
#C6 = 2
|
||||
#C7 = 175
|
||||
#C8 = 0
|
||||
#C9 = "A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed."
|
||||
#C10 = true
|
||||
#C11 = 13
|
||||
#C12 = 37
|
||||
#C3 = "#lib"
|
||||
#C4 = false
|
||||
#C5 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C4}
|
||||
#C6 = core::pragma {name:#C1, options:#C5}
|
||||
#C7 = "vm:prefer-inline"
|
||||
#C8 = null
|
||||
#C9 = core::pragma {name:#C7, options:#C8}
|
||||
#C10 = 175
|
||||
#C11 = 0
|
||||
#C12 = "A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed."
|
||||
#C13 = true
|
||||
#C14 = "doesntmatter"
|
||||
#C15 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
|
||||
#C16 = core::pragma {name:#C1, options:#C15}
|
||||
#C17 = ffi::Native<(ffi::Handle, ffi::IntPtr) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
|
||||
#C18 = core::pragma {name:#C1, options:#C17}
|
||||
#C19 = ffi::Native<(ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
|
||||
#C20 = core::pragma {name:#C1, options:#C19}
|
||||
#C21 = ffi::Native<(ffi::Handle, ffi::Handle) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
|
||||
#C22 = core::pragma {name:#C1, options:#C21}
|
||||
#C23 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C4}
|
||||
#C24 = core::pragma {name:#C1, options:#C23}
|
||||
#C25 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle> {symbol:#C14, assetId:#C3, isLeaf:#C4}
|
||||
#C26 = core::pragma {name:#C1, options:#C25}
|
||||
#C27 = ffi::Native<(ffi::Pointer<ffi::Void>) → ffi::Bool> {symbol:#C14, assetId:#C3, isLeaf:#C4}
|
||||
#C28 = core::pragma {name:#C1, options:#C27}
|
||||
#C29 = ffi::Native<(ffi::Pointer<ffi::Void>) → ffi::Bool> {symbol:#C14, assetId:#C3, isLeaf:#C13}
|
||||
#C30 = core::pragma {name:#C1, options:#C29}
|
||||
#C31 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void> {symbol:#C14, assetId:#C3, isLeaf:#C13}
|
||||
#C32 = core::pragma {name:#C1, options:#C31}
|
||||
#C33 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C13}
|
||||
#C34 = core::pragma {name:#C1, options:#C33}
|
||||
}
|
||||
|
|
|
@ -1,128 +1,143 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:ffi" as ffi;
|
||||
import "dart:nativewrappers" as nat;
|
||||
import "dart:ffi" as ffi;
|
||||
import "dart:_internal" as _in;
|
||||
|
||||
import "dart:ffi";
|
||||
import "dart:nativewrappers";
|
||||
|
||||
class Classy extends core::Object {
|
||||
static final field (core::int) → core::int _returnIntPtrStatic$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
synthetic constructor •() → self::Classy
|
||||
: super core::Object::•()
|
||||
;
|
||||
static method returnIntPtrStatic(core::int x) → core::int
|
||||
return self::Classy::_returnIntPtrStatic$Method$FfiNative$Ptr(x){(core::int) → core::int};
|
||||
@#C6
|
||||
external static method returnIntPtrStatic(core::int x) → core::int;
|
||||
}
|
||||
class NativeClassy extends nat::NativeFieldWrapperClass1 {
|
||||
static final field (ffi::Pointer<ffi::Void>, core::int) → void _goodHasReceiverPointer$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::int) → void, (ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static final field (self::NativeClassy, core::int) → void _goodHasReceiverHandle$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, core::int) → void, (ffi::Handle, ffi::IntPtr) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::IntPtr) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static final field (self::NativeClassy, ffi::Pointer<ffi::Void>) → void _goodHasReceiverHandleAndPtr$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, ffi::Pointer<ffi::Void>) → void, (ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static final field (self::NativeClassy, self::NativeClassy) → void _goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, self::NativeClassy) → void, (ffi::Handle, ffi::Handle) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle, ffi::Handle) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static final field (ffi::Pointer<ffi::Void>, self::NativeClassy) → void _goodHasReceiverPtrAndHandle$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, self::NativeClassy) → void, (ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static final field (ffi::Pointer<ffi::Void>, core::bool) → core::Object? _meh$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → core::Object?, (ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static final field (ffi::Pointer<ffi::Void>) → core::bool _blah$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void>) → ffi::Bool>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Bool>*>(ffi::_ffi_resolver(#C1, #C4, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static final field (ffi::Pointer<ffi::Void>) → core::bool _myField$Getter$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void>) → ffi::Bool>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Bool>*>(ffi::_ffi_resolver(#C1, #C4, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
|
||||
static final field (ffi::Pointer<ffi::Void>, core::bool) → void _myField$Setter$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → void, (ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
|
||||
synthetic constructor •() → self::NativeClassy
|
||||
: super nat::NativeFieldWrapperClass1::•()
|
||||
;
|
||||
@#C9
|
||||
method goodHasReceiverPointer(core::int v) → void
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t1 = this;
|
||||
final core::int #t2 = v;
|
||||
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final void #t3 = self::NativeClassy::_goodHasReceiverPointer$Method$FfiNative(ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = nat::_getNativeField(#t1);
|
||||
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C7);
|
||||
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C11);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress), #t2){(ffi::Pointer<ffi::Void>, core::int) → void};
|
||||
} =>#pointerAddress), #t2);
|
||||
_in::reachabilityFence(#t1);
|
||||
} =>#t3;
|
||||
@#C9
|
||||
method goodHasReceiverHandle(core::int v) → void
|
||||
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative$Ptr(this, v){(self::NativeClassy, core::int) → void};
|
||||
return self::NativeClassy::_goodHasReceiverHandle$Method$FfiNative(this, v);
|
||||
@#C9
|
||||
method goodHasReceiverHandleAndPtr(self::NativeClassy v) → void
|
||||
return block {
|
||||
final self::NativeClassy #t4 = this;
|
||||
final nat::NativeFieldWrapperClass1 #t5 = v;
|
||||
final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$Method$FfiNative$Ptr(#t4, ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t5))){(self::NativeClassy, ffi::Pointer<ffi::Void>) → void};
|
||||
final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$Method$FfiNative(#t4, ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t5)));
|
||||
_in::reachabilityFence(#t5);
|
||||
} =>#t6;
|
||||
@#C9
|
||||
method goodHasReceiverHandleAndHandle(self::NativeClassy v) → void
|
||||
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative$Ptr(this, v){(self::NativeClassy, self::NativeClassy) → void};
|
||||
return self::NativeClassy::_goodHasReceiverHandleAndHandle$Method$FfiNative(this, v);
|
||||
@#C9
|
||||
method goodHasReceiverPtrAndHandle(self::NativeClassy v) → void
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t7 = this;
|
||||
final self::NativeClassy #t8 = v;
|
||||
final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$Method$FfiNative(ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = nat::_getNativeField(#t7);
|
||||
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C7);
|
||||
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C11);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress), #t8){(ffi::Pointer<ffi::Void>, self::NativeClassy) → void};
|
||||
} =>#pointerAddress), #t8);
|
||||
_in::reachabilityFence(#t7);
|
||||
} =>#t9;
|
||||
@#C9
|
||||
method meh(core::bool blah) → core::String?
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t10 = this;
|
||||
final core::bool #t11 = blah;
|
||||
final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$Method$FfiNative(ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = nat::_getNativeField(#t10);
|
||||
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C7);
|
||||
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C11);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress), #t11){(ffi::Pointer<ffi::Void>, core::bool) → core::Object?});
|
||||
} =>#pointerAddress), #t11));
|
||||
_in::reachabilityFence(#t10);
|
||||
} =>#t12;
|
||||
@#C9
|
||||
method blah() → core::bool
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t13 = this;
|
||||
final core::bool #t14 = self::NativeClassy::_blah$Method$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final core::bool #t14 = self::NativeClassy::_blah$Method$FfiNative(ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = nat::_getNativeField(#t13);
|
||||
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C7);
|
||||
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C11);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
|
||||
} =>#pointerAddress));
|
||||
_in::reachabilityFence(#t13);
|
||||
} =>#t14;
|
||||
@#C9
|
||||
get myField() → core::bool
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t15 = this;
|
||||
final core::bool #t16 = self::NativeClassy::_myField$Getter$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final core::bool #t16 = self::NativeClassy::_myField$Getter$FfiNative(ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = nat::_getNativeField(#t15);
|
||||
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C7);
|
||||
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C11);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
|
||||
} =>#pointerAddress));
|
||||
_in::reachabilityFence(#t15);
|
||||
} =>#t16;
|
||||
@#C9
|
||||
set myField(core::bool value) → void
|
||||
return block {
|
||||
final nat::NativeFieldWrapperClass1 #t17 = this;
|
||||
final core::bool #t18 = value;
|
||||
final void #t19 = self::NativeClassy::_myField$Setter$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
|
||||
final void #t19 = self::NativeClassy::_myField$Setter$FfiNative(ffi::_fromAddress<ffi::Void>( block {
|
||||
synthesized core::int #pointerAddress = nat::_getNativeField(#t17);
|
||||
if(#pointerAddress.{core::Object::==}(#C6){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C7);
|
||||
if(#pointerAddress.{core::Object::==}(#C10){(core::Object) → core::bool})
|
||||
core::StateError::_throwNew(#C11);
|
||||
else
|
||||
;
|
||||
} =>#pointerAddress), #t18){(ffi::Pointer<ffi::Void>, core::bool) → void};
|
||||
} =>#pointerAddress), #t18);
|
||||
_in::reachabilityFence(#t17);
|
||||
} =>#t19;
|
||||
@#C14
|
||||
external static method _goodHasReceiverPointer$Method$FfiNative(ffi::Pointer<ffi::Void> #t0, core::int #t1) → void;
|
||||
@#C16
|
||||
external static method _goodHasReceiverHandle$Method$FfiNative(self::NativeClassy #t0, core::int #t1) → void;
|
||||
@#C18
|
||||
external static method _goodHasReceiverHandleAndPtr$Method$FfiNative(self::NativeClassy #t0, ffi::Pointer<ffi::Void> #t1) → void;
|
||||
@#C20
|
||||
external static method _goodHasReceiverHandleAndHandle$Method$FfiNative(self::NativeClassy #t0, self::NativeClassy #t1) → void;
|
||||
@#C22
|
||||
external static method _goodHasReceiverPtrAndHandle$Method$FfiNative(ffi::Pointer<ffi::Void> #t0, self::NativeClassy #t1) → void;
|
||||
@#C24
|
||||
external static method _meh$Method$FfiNative(ffi::Pointer<ffi::Void> #t0, core::bool #t1) → core::Object?;
|
||||
@#C26
|
||||
external static method _blah$Method$FfiNative(ffi::Pointer<ffi::Void> #t0) → core::bool;
|
||||
@#C29
|
||||
external static method _myField$Getter$FfiNative(ffi::Pointer<ffi::Void> #t0) → core::bool;
|
||||
@#C31
|
||||
external static method _myField$Setter$FfiNative(ffi::Pointer<ffi::Void> #t0, core::bool #t1) → void;
|
||||
}
|
||||
static final field (core::int) → core::int _returnIntPtr$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static final field (core::int) → core::int _returnIntPtrLeaf$Method$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr) → ffi::IntPtr>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
|
||||
static method returnIntPtr(core::int x) → core::int
|
||||
return self::_returnIntPtr$Method$FfiNative$Ptr(x){(core::int) → core::int};
|
||||
static method returnIntPtrLeaf(core::int x) → core::int
|
||||
return self::_returnIntPtrLeaf$Method$FfiNative$Ptr(x){(core::int) → core::int};
|
||||
@#C6
|
||||
external static method returnIntPtr(core::int x) → core::int;
|
||||
@#C33
|
||||
external static method returnIntPtrLeaf(core::int x) → core::int;
|
||||
static method main() → void {
|
||||
self::returnIntPtr(13);
|
||||
self::returnIntPtrLeaf(37);
|
||||
|
@ -138,11 +153,37 @@ static method main() → void {
|
|||
new self::NativeClassy::•().{self::NativeClassy::myField} = !b;
|
||||
}
|
||||
constants {
|
||||
#C1 = "#lib"
|
||||
#C1 = "vm:ffi:native"
|
||||
#C2 = "ReturnIntPtr"
|
||||
#C3 = 1
|
||||
#C4 = "doesntmatter"
|
||||
#C5 = 2
|
||||
#C6 = 0
|
||||
#C7 = "A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed."
|
||||
#C3 = "#lib"
|
||||
#C4 = false
|
||||
#C5 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C4}
|
||||
#C6 = core::pragma {name:#C1, options:#C5}
|
||||
#C7 = "vm:prefer-inline"
|
||||
#C8 = null
|
||||
#C9 = core::pragma {name:#C7, options:#C8}
|
||||
#C10 = 0
|
||||
#C11 = "A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed."
|
||||
#C12 = "doesntmatter"
|
||||
#C13 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::IntPtr) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
|
||||
#C14 = core::pragma {name:#C1, options:#C13}
|
||||
#C15 = ffi::Native<(ffi::Handle, ffi::IntPtr) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
|
||||
#C16 = core::pragma {name:#C1, options:#C15}
|
||||
#C17 = ffi::Native<(ffi::Handle, ffi::Pointer<ffi::Void>) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
|
||||
#C18 = core::pragma {name:#C1, options:#C17}
|
||||
#C19 = ffi::Native<(ffi::Handle, ffi::Handle) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
|
||||
#C20 = core::pragma {name:#C1, options:#C19}
|
||||
#C21 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Handle) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C4}
|
||||
#C22 = core::pragma {name:#C1, options:#C21}
|
||||
#C23 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Handle> {symbol:#C12, assetId:#C3, isLeaf:#C4}
|
||||
#C24 = core::pragma {name:#C1, options:#C23}
|
||||
#C25 = ffi::Native<(ffi::Pointer<ffi::Void>) → ffi::Bool> {symbol:#C12, assetId:#C3, isLeaf:#C4}
|
||||
#C26 = core::pragma {name:#C1, options:#C25}
|
||||
#C27 = true
|
||||
#C28 = ffi::Native<(ffi::Pointer<ffi::Void>) → ffi::Bool> {symbol:#C12, assetId:#C3, isLeaf:#C27}
|
||||
#C29 = core::pragma {name:#C1, options:#C28}
|
||||
#C30 = ffi::Native<(ffi::Pointer<ffi::Void>, ffi::Bool) → ffi::Void> {symbol:#C12, assetId:#C3, isLeaf:#C27}
|
||||
#C31 = core::pragma {name:#C1, options:#C30}
|
||||
#C32 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C27}
|
||||
#C33 = core::pragma {name:#C1, options:#C32}
|
||||
}
|
||||
|
|
|
@ -17,15 +17,17 @@ final class Struct1ByteInt extends ffi::Struct {
|
|||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3] method toString() → core::String
|
||||
return "(${[@vm.direct-call.metadata=#lib::Struct1ByteInt.a0] this.{self::Struct1ByteInt::a0}{core::int}})";
|
||||
}
|
||||
[@vm.inferred-type.metadata=dart.core::_Closure]static final field (core::int) → self::Struct1ByteInt _returnStruct1ByteIntNative$Method$FfiNative$Ptr = block {
|
||||
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C9)));
|
||||
} =>[@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(core::int) → self::Struct1ByteInt, (ffi::Int8) → self::Struct1ByteInt>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<ffi::NativeFunction<(ffi::Int8) → self::Struct1ByteInt>*>(ffi::_ffi_resolver(#C10, #C11, #C9){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static method main() → void {
|
||||
final self::Struct1ByteInt result = self::returnStruct1ByteIntNative([@vm.direct-call.metadata=dart.core::_IntegerImplementation.unary-] [@vm.inferred-type.metadata=int (skip check)] 1.{core::int::unary-}(){() → core::int});
|
||||
core::print("result = ${result}");
|
||||
}
|
||||
[@vm.unboxing-info.metadata=(i)->b]static method returnStruct1ByteIntNative([@vm.inferred-arg-type.metadata=int] core::int a0) → self::Struct1ByteInt
|
||||
return self::_returnStruct1ByteIntNative$Method$FfiNative$Ptr(a0){(core::int) → self::Struct1ByteInt};
|
||||
[@vm.unboxing-info.metadata=(i)->b]@#C10
|
||||
static method returnStruct1ByteIntNative([@vm.inferred-arg-type.metadata=int] core::int a0) → self::Struct1ByteInt
|
||||
return block {
|
||||
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C11)));
|
||||
} =>[@vm.inferred-type.metadata=#lib::Struct1ByteInt] self::_returnStruct1ByteIntNative$Method$FfiNative(a0);
|
||||
[@vm.unboxing-info.metadata=(i)->b]@#C17
|
||||
external static method _returnStruct1ByteIntNative$Method$FfiNative([@vm.inferred-arg-type.metadata=int] core::int #t0) → self::Struct1ByteInt;
|
||||
constants {
|
||||
#C1 = "vm:ffi:struct-fields"
|
||||
#C2 = TypeLiteralConstant(ffi::Int8)
|
||||
|
@ -35,7 +37,13 @@ constants {
|
|||
#C6 = core::pragma {name:#C1, options:#C5}
|
||||
#C7 = 0
|
||||
#C8 = <core::int*>[#C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7]
|
||||
#C9 = 1
|
||||
#C10 = "#lib"
|
||||
#C11 = "ReturnStruct1ByteInt"
|
||||
#C9 = "vm:prefer-inline"
|
||||
#C10 = core::pragma {name:#C9, options:#C4}
|
||||
#C11 = 1
|
||||
#C12 = "vm:ffi:native"
|
||||
#C13 = "ReturnStruct1ByteInt"
|
||||
#C14 = "#lib"
|
||||
#C15 = false
|
||||
#C16 = ffi::Native<(ffi::Int8) → self::Struct1ByteInt> {symbol:#C13, assetId:#C14, isLeaf:#C15}
|
||||
#C17 = core::pragma {name:#C12, options:#C16}
|
||||
}
|
||||
|
|
|
@ -27,15 +27,17 @@ final class Struct1ByteInt extends ffi::Struct {
|
|||
static get #sizeOf() → core::int*
|
||||
return #C13.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
|
||||
}
|
||||
static final field (core::int) → self::Struct1ByteInt _returnStruct1ByteIntNative$Method$FfiNative$Ptr = block {
|
||||
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase(typ::Uint8List::•(#C12)));
|
||||
} =>ffi::_asFunctionInternal<(core::int) → self::Struct1ByteInt, (ffi::Int8) → self::Struct1ByteInt>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Int8) → self::Struct1ByteInt>*>(ffi::_ffi_resolver(#C14, #C15, #C12){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
|
||||
static method main() → void {
|
||||
final self::Struct1ByteInt result = self::returnStruct1ByteIntNative(1.{core::int::unary-}(){() → core::int});
|
||||
core::print("result = ${result}");
|
||||
}
|
||||
@#C11
|
||||
static method returnStruct1ByteIntNative(core::int a0) → self::Struct1ByteInt
|
||||
return self::_returnStruct1ByteIntNative$Method$FfiNative$Ptr(a0){(core::int) → self::Struct1ByteInt};
|
||||
return block {
|
||||
_in::_nativeEffect(new self::Struct1ByteInt::#fromTypedDataBase(typ::Uint8List::•(#C12)));
|
||||
} =>self::_returnStruct1ByteIntNative$Method$FfiNative(a0);
|
||||
@#C19
|
||||
external static method _returnStruct1ByteIntNative$Method$FfiNative(core::int #t0) → self::Struct1ByteInt;
|
||||
constants {
|
||||
#C1 = "vm:ffi:struct-fields"
|
||||
#C2 = TypeLiteralConstant(ffi::Int8)
|
||||
|
@ -50,6 +52,10 @@ constants {
|
|||
#C11 = core::pragma {name:#C10, options:#C4}
|
||||
#C12 = 1
|
||||
#C13 = <core::int*>[#C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12]
|
||||
#C14 = "#lib"
|
||||
#C14 = "vm:ffi:native"
|
||||
#C15 = "ReturnStruct1ByteInt"
|
||||
#C16 = "#lib"
|
||||
#C17 = false
|
||||
#C18 = ffi::Native<(ffi::Int8) → self::Struct1ByteInt> {symbol:#C15, assetId:#C16, isLeaf:#C17}
|
||||
#C19 = core::pragma {name:#C14, options:#C18}
|
||||
}
|
||||
|
|
|
@ -22,3 +22,26 @@ Related files:
|
|||
* [pkg/vm/test/native_assets_validator_test.dart](../../../pkg/vm/test/native_assets_validator_test.dart)
|
||||
* [runtime/lib/ffi_dynamic_library.cc](../../../runtime/lib/ffi_dynamic_library.cc)
|
||||
* [runtime/vm/ffi/native_assets.cc](../../../runtime/vm/ffi/native_assets.cc)
|
||||
|
||||
## Native
|
||||
|
||||
The fully expanded version of `@Native` passed to the VM.
|
||||
|
||||
```
|
||||
@pragma(
|
||||
'vm:ffi:native',
|
||||
Native<IntPtr Function(Pointer<Void>, IntPtr)>(
|
||||
symbol: 'MyClass_MyMethod',
|
||||
assetId: '<library uri>',
|
||||
),
|
||||
)
|
||||
external int _myFunction$FfiNative(Pointer<Void> self, int x);
|
||||
```
|
||||
|
||||
This is passed as a pragma so it is treated consistently with other pragmas.
|
||||
|
||||
Related files:
|
||||
|
||||
* [runtime/vm/kernel_loader.cc](../../../runtime/vm/kernel_loader.cc)
|
||||
* [runtime/vm/object.cc](../../../runtime/vm/object.cc)
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ These pragma's are only used on AST nodes synthesized by us, so users defining t
|
|||
| Pragma | Meaning |
|
||||
| --- | --- |
|
||||
| `vm:ffi:native-assets` | [Passing a native assets mapping to the VM](compiler/ffi_pragmas.md) |
|
||||
| `vm:ffi:native`| [Passing a native arguments to the VM](compiler/ffi_pragmas.md) |
|
||||
|
||||
## Pragmas for internal testing
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/ffi_dynamic_library.h"
|
||||
|
||||
#include "platform/globals.h"
|
||||
#if defined(DART_HOST_OS_WINDOWS)
|
||||
#include <Psapi.h>
|
||||
|
@ -438,6 +440,39 @@ static void ThrowFfiResolveError(const String& symbol,
|
|||
Exceptions::ThrowArgumentError(error_message);
|
||||
}
|
||||
|
||||
intptr_t FfiResolveInternal(const String& asset,
|
||||
const String& symbol,
|
||||
uintptr_t args_n,
|
||||
char** error) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
|
||||
// Resolver resolution.
|
||||
auto resolver = GetFfiNativeResolver(thread, asset);
|
||||
if (resolver != nullptr) {
|
||||
void* ffi_native_result = FfiResolveWithFfiNativeResolver(
|
||||
thread, resolver, symbol, args_n, error);
|
||||
return reinterpret_cast<intptr_t>(ffi_native_result);
|
||||
}
|
||||
|
||||
// Native assets resolution.
|
||||
const auto& asset_location =
|
||||
Array::Handle(zone, GetAssetLocation(thread, asset));
|
||||
if (!asset_location.IsNull()) {
|
||||
void* asset_result = FfiResolveAsset(thread, asset_location, symbol, error);
|
||||
return reinterpret_cast<intptr_t>(asset_result);
|
||||
}
|
||||
|
||||
// Resolution in current process.
|
||||
#if !defined(DART_HOST_OS_WINDOWS)
|
||||
void* const result = Utils::ResolveSymbolInDynamicLibrary(
|
||||
RTLD_DEFAULT, symbol.ToCString(), error);
|
||||
#else
|
||||
void* const result = LookupSymbolInProcess(symbol.ToCString(), error);
|
||||
#endif
|
||||
return reinterpret_cast<intptr_t>(result);
|
||||
}
|
||||
|
||||
// FFI native C function pointer resolver.
|
||||
static intptr_t FfiResolve(Dart_Handle asset_handle,
|
||||
Dart_Handle symbol_handle,
|
||||
|
@ -449,40 +484,12 @@ static intptr_t FfiResolve(Dart_Handle asset_handle,
|
|||
const String& symbol = Api::UnwrapStringHandle(zone, symbol_handle);
|
||||
char* error = nullptr;
|
||||
|
||||
// Resolver resolution.
|
||||
auto resolver = GetFfiNativeResolver(thread, asset);
|
||||
if (resolver != nullptr) {
|
||||
void* ffi_native_result = FfiResolveWithFfiNativeResolver(
|
||||
thread, resolver, symbol, args_n, &error);
|
||||
const intptr_t result = FfiResolveInternal(asset, 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);
|
||||
}
|
||||
return reinterpret_cast<intptr_t>(result);
|
||||
ASSERT(result != 0x0);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Bootstrap to get the FFI Native resolver through a `native` call.
|
||||
|
|
20
runtime/lib/ffi_dynamic_library.h
Normal file
20
runtime/lib/ffi_dynamic_library.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_
|
||||
#define RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_
|
||||
|
||||
#include "platform/globals.h"
|
||||
#include "vm/object.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
intptr_t FfiResolveInternal(const String& asset,
|
||||
const String& symbol,
|
||||
uintptr_t args_n,
|
||||
char** error);
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_LIB_FFI_DYNAMIC_LIBRARY_H_
|
|
@ -1377,7 +1377,7 @@ const char* Precompiler::MustRetainFunction(const Function& function) {
|
|||
// * Selector matches a symbol used in Resolver::ResolveDynamic calls
|
||||
// in dart_entry.cc or dart_api_impl.cc.
|
||||
// * _Closure.call (used in async stack handling)
|
||||
if (function.is_native()) {
|
||||
if (function.is_old_native()) {
|
||||
return "native function";
|
||||
}
|
||||
|
||||
|
@ -1648,7 +1648,7 @@ void Precompiler::AddAnnotatedRoots() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (function.is_native()) {
|
||||
if (function.is_old_native()) {
|
||||
// The embedder will need to lookup this library to provide the native
|
||||
// resolver, even if there are no embedder calls into the library.
|
||||
AddApiUse(lib);
|
||||
|
@ -2193,7 +2193,7 @@ void Precompiler::DropFunctions() {
|
|||
// FFI trampolines may be dynamically called.
|
||||
return AddRetainReason(sig, RetainReasons::kFfiTrampolineSignature);
|
||||
}
|
||||
if (function.is_native()) {
|
||||
if (function.is_old_native()) {
|
||||
return AddRetainReason(sig, RetainReasons::kNativeSignature);
|
||||
}
|
||||
if (function.HasRequiredNamedParameters()) {
|
||||
|
@ -2983,7 +2983,7 @@ void Precompiler::DiscardCodeObjects() {
|
|||
if (functions_to_retain_.ContainsKey(function_)) {
|
||||
// Retain Code objects corresponding to native functions
|
||||
// (to find native implementation).
|
||||
if (function_.is_native()) {
|
||||
if (function_.is_old_native()) {
|
||||
++codes_with_native_function_;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3427,6 +3427,66 @@ void FlowGraphCompiler::EmitMoveFromNative(
|
|||
}
|
||||
}
|
||||
|
||||
void FlowGraphCompiler::EmitMoveConst(const compiler::ffi::NativeLocation& dst,
|
||||
Location src,
|
||||
Representation src_type,
|
||||
TemporaryRegisterAllocator* temp) {
|
||||
ASSERT(src.IsConstant() || src.IsPairLocation());
|
||||
const auto& dst_type = dst.payload_type();
|
||||
Register scratch = kNoRegister;
|
||||
if (dst.IsExpressibleAsLocation() &&
|
||||
dst_type.IsExpressibleAsRepresentation() &&
|
||||
dst_type.AsRepresentationOverApprox(zone_) == src_type) {
|
||||
// We can directly emit the const in the right place and representation.
|
||||
const Location dst_loc = dst.AsLocation();
|
||||
assembler()->Comment("dst.IsExpressibleAsLocation() %s",
|
||||
dst_loc.ToCString());
|
||||
EmitMove(dst_loc, src, temp);
|
||||
} else {
|
||||
// We need an intermediate location.
|
||||
Location intermediate;
|
||||
if (dst_type.IsInt()) {
|
||||
if (TMP == kNoRegister) {
|
||||
scratch = temp->AllocateTemporary();
|
||||
Location::RegisterLocation(scratch);
|
||||
} else {
|
||||
intermediate = Location::RegisterLocation(TMP);
|
||||
}
|
||||
} else {
|
||||
ASSERT(dst_type.IsFloat());
|
||||
intermediate = Location::FpuRegisterLocation(FpuTMP);
|
||||
}
|
||||
assembler()->Comment("constant using intermediate: %s",
|
||||
intermediate.ToCString());
|
||||
|
||||
if (src.IsPairLocation()) {
|
||||
for (intptr_t i : {0, 1}) {
|
||||
const Representation src_type_split =
|
||||
compiler::ffi::NativeType::FromUnboxedRepresentation(zone_,
|
||||
src_type)
|
||||
.Split(zone_, i)
|
||||
.AsRepresentation();
|
||||
const auto& intermediate_native =
|
||||
compiler::ffi::NativeLocation::FromLocation(zone_, intermediate,
|
||||
src_type_split);
|
||||
EmitMove(intermediate, src.AsPairLocation()->At(i), temp);
|
||||
EmitNativeMove(dst.Split(zone_, 2, i), intermediate_native, temp);
|
||||
}
|
||||
} else {
|
||||
const auto& intermediate_native =
|
||||
compiler::ffi::NativeLocation::FromLocation(zone_, intermediate,
|
||||
src_type);
|
||||
EmitMove(intermediate, src, temp);
|
||||
EmitNativeMove(dst, intermediate_native, temp);
|
||||
}
|
||||
|
||||
if (scratch != kNoRegister) {
|
||||
temp->ReleaseTemporary();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool FlowGraphCompiler::CanPcRelativeCall(const Function& target) const {
|
||||
return FLAG_precompiled_mode && (LoadingUnit::LoadingUnitOf(function()) ==
|
||||
LoadingUnit::LoadingUnitOf(target));
|
||||
|
|
|
@ -520,6 +520,12 @@ class FlowGraphCompiler : public ValueObject {
|
|||
const compiler::ffi::NativeLocation& src,
|
||||
TemporaryRegisterAllocator* temp);
|
||||
|
||||
// Helper method to move a Dart const to a native location.
|
||||
void EmitMoveConst(const compiler::ffi::NativeLocation& dst,
|
||||
Location src,
|
||||
Representation src_type,
|
||||
TemporaryRegisterAllocator* temp);
|
||||
|
||||
bool CheckAssertAssignableTypeTestingABILocations(
|
||||
const LocationSummary& locs);
|
||||
|
||||
|
|
|
@ -7407,15 +7407,18 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
|
|||
|
||||
ConstantTemporaryAllocator temp_alloc(temp0);
|
||||
if (origin.IsConstant()) {
|
||||
// Can't occur because we currently don't inline FFI trampolines (see
|
||||
// http://dartbug.com/45055), which means all incoming arguments
|
||||
// originate from parameters and thus are non-constant.
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
__ 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.
|
||||
if (marshaller_.IsHandle(arg_index)) {
|
||||
ASSERT(compiler::target::LocalHandle::ptr_offset() == 0);
|
||||
ASSERT(compiler::target::LocalHandle::InstanceSize() ==
|
||||
compiler::target::kWordSize);
|
||||
|
|
|
@ -259,6 +259,7 @@ template <>
|
|||
void FlowGraphSerializer::WriteTrait<const compiler::ffi::CallMarshaller&>::
|
||||
Write(FlowGraphSerializer* s, const compiler::ffi::CallMarshaller& x) {
|
||||
s->Write<const Function&>(x.dart_signature());
|
||||
s->Write<const FunctionType&>(x.c_signature());
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -266,9 +267,10 @@ const compiler::ffi::CallMarshaller&
|
|||
FlowGraphDeserializer::ReadTrait<const compiler::ffi::CallMarshaller&>::Read(
|
||||
FlowGraphDeserializer* d) {
|
||||
const Function& dart_signature = d->Read<const Function&>();
|
||||
const FunctionType& c_signature = d->Read<const FunctionType&>();
|
||||
const char* error = nullptr;
|
||||
return *compiler::ffi::CallMarshaller::FromFunction(d->zone(), dart_signature,
|
||||
&error);
|
||||
c_signature, &error);
|
||||
}
|
||||
|
||||
template <>
|
||||
|
|
|
@ -1209,8 +1209,11 @@ ISOLATE_UNIT_TEST_CASE(IRTest_FfiCallInstrLeafDoesntSpill) {
|
|||
// Construct the FFICallInstr from the trampoline matching our native
|
||||
// function.
|
||||
const char* error = nullptr;
|
||||
auto* const zone = thread->zone();
|
||||
const auto& c_signature =
|
||||
FunctionType::ZoneHandle(zone, ffi_trampoline.FfiCSignature());
|
||||
const auto marshaller_ptr = compiler::ffi::CallMarshaller::FromFunction(
|
||||
thread->zone(), ffi_trampoline, &error);
|
||||
zone, ffi_trampoline, c_signature, &error);
|
||||
RELEASE_ASSERT(error == nullptr);
|
||||
RELEASE_ASSERT(marshaller_ptr != nullptr);
|
||||
const auto& marshaller = *marshaller_ptr;
|
||||
|
|
|
@ -87,10 +87,10 @@ const NativeFunctionType* NativeFunctionTypeFromFunctionType(
|
|||
|
||||
CallMarshaller* CallMarshaller::FromFunction(Zone* zone,
|
||||
const Function& function,
|
||||
const FunctionType& c_signature,
|
||||
const char** error) {
|
||||
DEBUG_ASSERT(function.IsNotTemporaryScopedHandle());
|
||||
const auto& c_signature =
|
||||
FunctionType::ZoneHandle(zone, function.FfiCSignature());
|
||||
DEBUG_ASSERT(c_signature.IsNotTemporaryScopedHandle());
|
||||
const auto native_function_signature =
|
||||
NativeFunctionTypeFromFunctionType(zone, c_signature, error);
|
||||
if (*error != nullptr) {
|
||||
|
@ -169,7 +169,7 @@ bool BaseMarshaller::IsCompound(intptr_t arg_index) const {
|
|||
}
|
||||
|
||||
bool BaseMarshaller::ContainsHandles() const {
|
||||
return dart_signature_.FfiCSignatureContainsHandles();
|
||||
return c_signature_.ContainsHandles();
|
||||
}
|
||||
|
||||
intptr_t BaseMarshaller::NumDefinitions() const {
|
||||
|
|
|
@ -138,6 +138,7 @@ class BaseMarshaller : public ZoneAllocated {
|
|||
}
|
||||
|
||||
const Function& dart_signature() const { return dart_signature_; }
|
||||
const FunctionType& c_signature() const { return c_signature_; }
|
||||
StringPtr function_name() const { return dart_signature_.name(); }
|
||||
|
||||
protected:
|
||||
|
@ -164,6 +165,7 @@ class CallMarshaller : public BaseMarshaller {
|
|||
public:
|
||||
static CallMarshaller* FromFunction(Zone* zone,
|
||||
const Function& function,
|
||||
const FunctionType& c_signature,
|
||||
const char** error);
|
||||
|
||||
CallMarshaller(Zone* zone,
|
||||
|
|
|
@ -678,8 +678,10 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionBody(
|
|||
|
||||
const bool has_body = ReadTag() == kSomething; // read first part of body.
|
||||
|
||||
if (dart_function.is_native()) {
|
||||
if (dart_function.is_old_native()) {
|
||||
body += B->NativeFunctionBody(dart_function, first_parameter);
|
||||
} else if (dart_function.is_ffi_native()) {
|
||||
body += B->FfiNativeFunctionBody(dart_function);
|
||||
} else if (dart_function.is_external()) {
|
||||
body += ThrowNoSuchMethodError(TokenPosition::kNoSource, dart_function,
|
||||
/*incompatible_arguments=*/false);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "lib/ffi_dynamic_library.h"
|
||||
#include "platform/assert.h"
|
||||
#include "platform/globals.h"
|
||||
#include "vm/class_id.h"
|
||||
|
@ -593,7 +594,7 @@ Fragment FlowGraphBuilder::Return(TokenPosition position,
|
|||
|
||||
// Emit a type check of the return type in checked mode for all functions
|
||||
// and in strong mode for native functions.
|
||||
if (!omit_result_type_check && function.is_native()) {
|
||||
if (!omit_result_type_check && function.is_old_native()) {
|
||||
const AbstractType& return_type =
|
||||
AbstractType::Handle(Z, function.result_type());
|
||||
instructions += CheckAssignable(return_type, Symbols::FunctionResult());
|
||||
|
@ -857,7 +858,7 @@ FlowGraph* FlowGraphBuilder::BuildGraph() {
|
|||
|
||||
Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
||||
LocalVariable* first_parameter) {
|
||||
ASSERT(function.is_native());
|
||||
ASSERT(function.is_old_native());
|
||||
ASSERT(!IsRecognizedMethodForFlowGraph(function));
|
||||
|
||||
Fragment body;
|
||||
|
@ -4984,33 +4985,133 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
|||
case FfiFunctionKind::kAsyncCallback:
|
||||
return BuildGraphOfAsyncFfiCallback(function);
|
||||
case FfiFunctionKind::kCall:
|
||||
return BuildGraphOfFfiNative(function);
|
||||
return BuildGraphOfFfiCall(function);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
||||
Fragment FlowGraphBuilder::FfiNativeLookupAddress(const Function& function) {
|
||||
ASSERT(function.is_ffi_native());
|
||||
ASSERT(!IsRecognizedMethodForFlowGraph(function));
|
||||
auto const& native_instance =
|
||||
Instance::Handle(function.GetNativeAnnotation());
|
||||
const auto& native_class = Class::Handle(Z, native_instance.clazz());
|
||||
ASSERT(String::Handle(Z, native_class.UserVisibleName())
|
||||
.Equals(Symbols::FfiNative()));
|
||||
const auto& native_class_fields = Array::Handle(Z, native_class.fields());
|
||||
ASSERT(native_class_fields.Length() == 3);
|
||||
const auto& symbol_field =
|
||||
Field::Handle(Z, Field::RawCast(native_class_fields.At(0)));
|
||||
const auto& asset_id_field =
|
||||
Field::Handle(Z, Field::RawCast(native_class_fields.At(1)));
|
||||
const auto& symbol = String::ZoneHandle(
|
||||
Z, String::RawCast(native_instance.GetField(symbol_field)));
|
||||
const auto& asset_id = String::ZoneHandle(
|
||||
Z, String::RawCast(native_instance.GetField(asset_id_field)));
|
||||
const auto& type_args =
|
||||
TypeArguments::Handle(Z, native_instance.GetTypeArguments());
|
||||
ASSERT(type_args.Length() == 1);
|
||||
const auto& native_type =
|
||||
FunctionType::Cast(AbstractType::ZoneHandle(Z, type_args.TypeAt(0)));
|
||||
const intptr_t arg_n =
|
||||
native_type.NumParameters() - native_type.num_implicit_parameters();
|
||||
const auto& ffi_resolver =
|
||||
Function::ZoneHandle(Z, IG->object_store()->ffi_resolver_function());
|
||||
|
||||
#if !defined(TARGET_ARCH_IA32)
|
||||
// Access to the pool, use cacheable static call.
|
||||
Fragment body;
|
||||
body += Constant(asset_id);
|
||||
body += Constant(symbol);
|
||||
body += Constant(Smi::ZoneHandle(Smi::New(arg_n)));
|
||||
body += CachableIdempotentCall(TokenPosition::kNoSource, ffi_resolver,
|
||||
/*argument_count=*/3,
|
||||
/*argument_names=*/Array::null_array(),
|
||||
/*type_args_count=*/0);
|
||||
return body;
|
||||
#else
|
||||
// IA32 only has JIT and no pool. This function will only be compiled if
|
||||
// immediately run afterwards, so do the lookup here.
|
||||
char* error = nullptr;
|
||||
#if !defined(DART_PRECOMPILER) || defined(TESTING)
|
||||
const uintptr_t function_address =
|
||||
FfiResolveInternal(asset_id, symbol, arg_n, &error);
|
||||
#else
|
||||
const uintptr_t function_address = 0;
|
||||
UNREACHABLE(); // JIT runtime should not contain AOT code
|
||||
#endif
|
||||
if (error == nullptr) {
|
||||
Fragment body;
|
||||
body += UnboxedIntConstant(function_address, kUnboxedFfiIntPtr);
|
||||
return body;
|
||||
} else {
|
||||
free(error);
|
||||
// Lookup failed, we want to throw an error consistent with AOT, just
|
||||
// compile into a lookup so that we can throw the error from the same
|
||||
// error path.
|
||||
Fragment body;
|
||||
body += Constant(asset_id);
|
||||
body += Constant(symbol);
|
||||
body += Constant(Smi::ZoneHandle(Smi::New(arg_n)));
|
||||
// Non-cacheable call, this is IA32.
|
||||
body += StaticCall(TokenPosition::kNoSource, ffi_resolver,
|
||||
/*argument_count=*/3, ICData::kStatic);
|
||||
body += UnboxTruncate(kUnboxedFfiIntPtr);
|
||||
return body;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::FfiCallLookupAddress(const Function& function) {
|
||||
ASSERT(function.IsFfiTrampoline());
|
||||
const intptr_t kClosureParameterOffset = 0;
|
||||
const intptr_t kFirstArgumentParameterOffset = kClosureParameterOffset + 1;
|
||||
Fragment body;
|
||||
// Push the function pointer, which is stored (as Pointer object) in the
|
||||
// first slot of the context.
|
||||
body +=
|
||||
LoadLocal(parsed_function_->ParameterVariable(kClosureParameterOffset));
|
||||
body += LoadNativeField(Slot::Closure_context());
|
||||
body += LoadNativeField(Slot::GetContextVariableSlotFor(
|
||||
thread_, *MakeImplicitClosureScope(
|
||||
Z, Class::Handle(IG->object_store()->ffi_pointer_class()))
|
||||
->context_variables()[0]));
|
||||
|
||||
graph_entry_ =
|
||||
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
|
||||
// This can only be Pointer, so it is always safe to LoadUntagged.
|
||||
body += LoadUntagged(compiler::target::PointerBase::data_offset());
|
||||
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
|
||||
return body;
|
||||
}
|
||||
|
||||
auto normal_entry = BuildFunctionEntry(graph_entry_);
|
||||
graph_entry_->set_normal_entry(normal_entry);
|
||||
Fragment FlowGraphBuilder::FfiNativeFunctionBody(const Function& function) {
|
||||
ASSERT(function.is_ffi_native());
|
||||
ASSERT(!IsRecognizedMethodForFlowGraph(function));
|
||||
|
||||
PrologueInfo prologue_info(-1, -1);
|
||||
const auto& c_signature =
|
||||
FunctionType::ZoneHandle(Z, function.FfiCSignature());
|
||||
|
||||
BlockEntryInstr* instruction_cursor =
|
||||
BuildPrologue(normal_entry, &prologue_info);
|
||||
Fragment body;
|
||||
body += FfiNativeLookupAddress(function);
|
||||
body += FfiCallFunctionBody(function, c_signature);
|
||||
return body;
|
||||
}
|
||||
|
||||
Fragment function_body(instruction_cursor);
|
||||
function_body += CheckStackOverflowInPrologue(function.token_pos());
|
||||
Fragment FlowGraphBuilder::FfiCallFunctionBody(
|
||||
const Function& function,
|
||||
const FunctionType& c_signature) {
|
||||
ASSERT(function.is_ffi_native() || function.IsFfiTrampoline());
|
||||
const bool is_ffi_native = function.is_ffi_native();
|
||||
const intptr_t kClosureParameterOffset = 0;
|
||||
const intptr_t first_argument_parameter_offset =
|
||||
is_ffi_native ? 0 : kClosureParameterOffset + 1;
|
||||
|
||||
LocalVariable* address = MakeTemporary("address");
|
||||
|
||||
Fragment body;
|
||||
|
||||
const char* error = nullptr;
|
||||
const auto marshaller_ptr =
|
||||
compiler::ffi::CallMarshaller::FromFunction(Z, function, &error);
|
||||
const auto marshaller_ptr = compiler::ffi::CallMarshaller::FromFunction(
|
||||
Z, function, c_signature, &error);
|
||||
// AbiSpecific integers can be incomplete causing us to not know the calling
|
||||
// convention. However, this is caught in asFunction in both JIT/AOT.
|
||||
RELEASE_ASSERT(error == nullptr);
|
||||
|
@ -5030,21 +5131,20 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
|||
if (marshaller.IsHandle(i)) {
|
||||
continue;
|
||||
}
|
||||
function_body += LoadLocal(
|
||||
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i));
|
||||
body += LoadLocal(parsed_function_->ParameterVariable(
|
||||
first_argument_parameter_offset + i));
|
||||
// TODO(http://dartbug.com/47486): Support entry without checking for null.
|
||||
// Check for 'null'.
|
||||
function_body += CheckNullOptimized(
|
||||
body += CheckNullOptimized(
|
||||
String::ZoneHandle(
|
||||
Z, function.ParameterNameAt(kFirstArgumentParameterOffset + i)),
|
||||
Z, function.ParameterNameAt(first_argument_parameter_offset + i)),
|
||||
CheckNullInstr::kArgumentError);
|
||||
function_body += StoreLocal(
|
||||
TokenPosition::kNoSource,
|
||||
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i));
|
||||
function_body += Drop();
|
||||
body += StoreLocal(TokenPosition::kNoSource,
|
||||
parsed_function_->ParameterVariable(
|
||||
first_argument_parameter_offset + i));
|
||||
body += Drop();
|
||||
}
|
||||
|
||||
Fragment body;
|
||||
intptr_t try_handler_index = -1;
|
||||
if (signature_contains_handles) {
|
||||
// Wrap in Try catch to transition from Native to Generated on a throw from
|
||||
|
@ -5072,12 +5172,12 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
|||
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
|
||||
if (marshaller.IsCompound(i)) {
|
||||
body += FfiCallConvertCompoundArgumentToNative(
|
||||
parsed_function_->ParameterVariable(kFirstArgumentParameterOffset +
|
||||
parsed_function_->ParameterVariable(first_argument_parameter_offset +
|
||||
i),
|
||||
marshaller, i);
|
||||
} else {
|
||||
body += LoadLocal(parsed_function_->ParameterVariable(
|
||||
kFirstArgumentParameterOffset + i));
|
||||
first_argument_parameter_offset + i));
|
||||
// FfiCallInstr specifies all handle locations as Stack, and will pass a
|
||||
// pointer to the stack slot as the native handle argument.
|
||||
// Therefore we do not need to wrap handles.
|
||||
|
@ -5087,20 +5187,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
|||
}
|
||||
}
|
||||
|
||||
// Push the function pointer, which is stored (as Pointer object) in the
|
||||
// first slot of the context.
|
||||
body +=
|
||||
LoadLocal(parsed_function_->ParameterVariable(kClosureParameterOffset));
|
||||
body += LoadNativeField(Slot::Closure_context());
|
||||
body += LoadNativeField(Slot::GetContextVariableSlotFor(
|
||||
thread_, *MakeImplicitClosureScope(
|
||||
Z, Class::Handle(IG->object_store()->ffi_pointer_class()))
|
||||
->context_variables()[0]));
|
||||
|
||||
// This can only be Pointer, so it is safe to load the data field.
|
||||
body += LoadNativeField(Slot::PointerBase_data(),
|
||||
InnerPointerAccess::kCannotBeInnerPointer);
|
||||
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
|
||||
body += LoadLocal(address);
|
||||
|
||||
if (marshaller.PassTypedData()) {
|
||||
body += LoadLocal(typed_data);
|
||||
|
@ -5111,7 +5198,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
|||
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
|
||||
if (marshaller.IsPointer(i)) {
|
||||
body += LoadLocal(parsed_function_->ParameterVariable(
|
||||
kFirstArgumentParameterOffset + i));
|
||||
first_argument_parameter_offset + i));
|
||||
body += ReachabilityFence();
|
||||
}
|
||||
}
|
||||
|
@ -5119,12 +5206,12 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
|||
const intptr_t num_defs = marshaller.NumReturnDefinitions();
|
||||
ASSERT(num_defs >= 1);
|
||||
auto defs = new (Z) ZoneGrowableArray<LocalVariable*>(Z, num_defs);
|
||||
LocalVariable* def = MakeTemporary();
|
||||
LocalVariable* def = MakeTemporary("ffi call result");
|
||||
defs->Add(def);
|
||||
|
||||
if (marshaller.PassTypedData()) {
|
||||
// Drop call result, typed data with contents is already on the stack.
|
||||
body += Drop();
|
||||
body += DropTemporary(&def);
|
||||
}
|
||||
|
||||
if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
|
||||
|
@ -5141,14 +5228,13 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
|||
body += ExitHandleScope();
|
||||
}
|
||||
|
||||
body += DropTempsPreserveTop(1); // Drop address.
|
||||
body += Return(TokenPosition::kNoSource);
|
||||
|
||||
if (signature_contains_handles) {
|
||||
--try_depth_;
|
||||
}
|
||||
|
||||
function_body += body;
|
||||
|
||||
if (signature_contains_handles) {
|
||||
++catch_depth_;
|
||||
Fragment catch_body =
|
||||
|
@ -5167,6 +5253,30 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
|||
--catch_depth_;
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCall(const Function& function) {
|
||||
graph_entry_ =
|
||||
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
|
||||
|
||||
auto normal_entry = BuildFunctionEntry(graph_entry_);
|
||||
graph_entry_->set_normal_entry(normal_entry);
|
||||
|
||||
PrologueInfo prologue_info(-1, -1);
|
||||
|
||||
BlockEntryInstr* instruction_cursor =
|
||||
BuildPrologue(normal_entry, &prologue_info);
|
||||
|
||||
Fragment function_body(instruction_cursor);
|
||||
function_body += CheckStackOverflowInPrologue(function.token_pos());
|
||||
|
||||
const auto& c_signature =
|
||||
FunctionType::ZoneHandle(Z, function.FfiCSignature());
|
||||
|
||||
function_body += FfiCallLookupAddress(function);
|
||||
function_body += FfiCallFunctionBody(function, c_signature);
|
||||
|
||||
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
|
||||
prologue_info);
|
||||
}
|
||||
|
|
|
@ -137,8 +137,14 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
|||
FlowGraph* BuildGraphOfFfiTrampoline(const Function& function);
|
||||
FlowGraph* BuildGraphOfSyncFfiCallback(const Function& function);
|
||||
FlowGraph* BuildGraphOfAsyncFfiCallback(const Function& function);
|
||||
FlowGraph* BuildGraphOfFfiNative(const Function& function);
|
||||
FlowGraph* BuildGraphOfFfiCall(const Function& function);
|
||||
|
||||
Fragment FfiCallLookupAddress(const Function& function);
|
||||
Fragment FfiNativeLookupAddress(const Function& function);
|
||||
// Expects target address on stack.
|
||||
Fragment FfiCallFunctionBody(const Function& function,
|
||||
const FunctionType& c_signature);
|
||||
Fragment FfiNativeFunctionBody(const Function& function);
|
||||
Fragment NativeFunctionBody(const Function& function,
|
||||
LocalVariable* first_parameter);
|
||||
Fragment LoadNativeArg(const compiler::ffi::CallbackMarshaller& marshaller,
|
||||
|
|
|
@ -155,7 +155,19 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
|
|||
// NOTE: FunctionNode is read further below the if.
|
||||
|
||||
intptr_t pos = 0;
|
||||
if (function.IsClosureFunction()) {
|
||||
if (function.is_ffi_native()) {
|
||||
needs_expr_temp_ = true;
|
||||
// Calls with handles need try/catch variables.
|
||||
if (function.FfiCSignatureContainsHandles()) {
|
||||
++depth_.try_;
|
||||
AddTryVariables();
|
||||
--depth_.try_;
|
||||
++depth_.catch_;
|
||||
AddCatchVariables();
|
||||
FinalizeCatchVariables();
|
||||
--depth_.catch_;
|
||||
}
|
||||
} else if (function.IsClosureFunction()) {
|
||||
LocalVariable* closure_parameter = MakeVariable(
|
||||
TokenPosition::kNoSource, TokenPosition::kNoSource,
|
||||
Symbols::ClosureParameter(), AbstractType::dynamic_type());
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1749,6 +1749,9 @@ void KernelLoader::ReadVMAnnotations(const Library& library,
|
|||
"vm:isolate-unsendable")) {
|
||||
*pragma_bits = IsolateUnsendablePragma::update(true, *pragma_bits);
|
||||
}
|
||||
if (constant_reader.IsStringConstant(name_index, "vm:ffi:native")) {
|
||||
*pragma_bits = FfiNativePragma::update(true, *pragma_bits);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helper_.SkipExpression();
|
||||
|
@ -1798,13 +1801,14 @@ void KernelLoader::LoadProcedure(const Library& library,
|
|||
// they are not reachable anymore and we never look them up by name.
|
||||
const bool register_function = !name.Equals(Symbols::DebugProcedureName());
|
||||
|
||||
const bool is_ffi_native = FfiNativePragma::decode(pragma_bits);
|
||||
const FunctionType& signature = FunctionType::Handle(Z, FunctionType::New());
|
||||
const Function& function = Function::ZoneHandle(
|
||||
Z, Function::New(signature, name, kind,
|
||||
!is_method, // is_static
|
||||
false, // is_const
|
||||
is_abstract, is_external,
|
||||
!native_name.IsNull(), // is_native
|
||||
!native_name.IsNull() || is_ffi_native, // is_native
|
||||
script_class, procedure_helper.start_position_));
|
||||
function.set_has_pragma(HasPragma::decode(pragma_bits));
|
||||
function.set_end_token_pos(procedure_helper.end_position_);
|
||||
|
|
|
@ -220,6 +220,8 @@ class KernelLoader : public ValueObject {
|
|||
BitField<uint32_t, bool, ExternalNamePragma::kNextBit, 1>;
|
||||
using IsolateUnsendablePragma =
|
||||
BitField<uint32_t, bool, InvisibleFunctionPragma::kNextBit, 1>;
|
||||
using FfiNativePragma =
|
||||
BitField<uint32_t, bool, IsolateUnsendablePragma::kNextBit, 1>;
|
||||
|
||||
void FinishTopLevelClassLoading(const Class& toplevel_class,
|
||||
const Library& library,
|
||||
|
|
|
@ -175,7 +175,7 @@ class NativeArguments {
|
|||
}
|
||||
|
||||
static intptr_t ParameterCountForResolution(const Function& function) {
|
||||
ASSERT(function.is_native());
|
||||
ASSERT(function.is_old_native());
|
||||
ASSERT(!function.IsGenerativeConstructor()); // Not supported.
|
||||
intptr_t count = function.NumParameters();
|
||||
if (function.is_static() && function.IsClosureFunction()) {
|
||||
|
@ -189,7 +189,7 @@ class NativeArguments {
|
|||
}
|
||||
|
||||
static int ComputeArgcTag(const Function& function) {
|
||||
ASSERT(function.is_native());
|
||||
ASSERT(function.is_old_native());
|
||||
ASSERT(!function.IsGenerativeConstructor()); // Not supported.
|
||||
int argc = function.NumParameters();
|
||||
int function_bits = 0;
|
||||
|
|
|
@ -8358,7 +8358,7 @@ void Function::set_implicit_closure_function(const Function& value) const {
|
|||
IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
|
||||
ASSERT(!IsClosureFunction());
|
||||
const Object& old_data = Object::Handle(data());
|
||||
if (is_native()) {
|
||||
if (is_old_native()) {
|
||||
ASSERT(old_data.IsArray());
|
||||
const auto& pair = Array::Cast(old_data);
|
||||
ASSERT(pair.AtAcquire(NativeFunctionData::kTearOff) == Object::null() ||
|
||||
|
@ -8378,26 +8378,38 @@ void Function::SetFfiCSignature(const FunctionType& sig) const {
|
|||
}
|
||||
|
||||
FunctionTypePtr Function::FfiCSignature() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
auto* const zone = Thread::Current()->zone();
|
||||
if (IsFfiTrampoline()) {
|
||||
const Object& obj = Object::Handle(zone, data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).c_signature();
|
||||
}
|
||||
ASSERT(is_ffi_native());
|
||||
auto const& native_instance = Instance::Handle(GetNativeAnnotation());
|
||||
const auto& type_args =
|
||||
TypeArguments::Handle(zone, native_instance.GetTypeArguments());
|
||||
ASSERT(type_args.Length() == 1);
|
||||
const auto& native_type =
|
||||
FunctionType::Cast(AbstractType::ZoneHandle(zone, type_args.TypeAt(0)));
|
||||
return native_type.ptr();
|
||||
}
|
||||
|
||||
bool Function::FfiCSignatureContainsHandles() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const FunctionType& c_signature = FunctionType::Handle(FfiCSignature());
|
||||
const intptr_t num_params = c_signature.num_fixed_parameters();
|
||||
return c_signature.ContainsHandles();
|
||||
}
|
||||
|
||||
bool FunctionType::ContainsHandles() const {
|
||||
const intptr_t num_params = num_fixed_parameters();
|
||||
for (intptr_t i = 0; i < num_params; i++) {
|
||||
const bool is_handle =
|
||||
AbstractType::Handle(c_signature.ParameterTypeAt(i)).type_class_id() ==
|
||||
AbstractType::Handle(ParameterTypeAt(i)).type_class_id() ==
|
||||
kFfiHandleCid;
|
||||
if (is_handle) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return AbstractType::Handle(c_signature.result_type()).type_class_id() ==
|
||||
kFfiHandleCid;
|
||||
return AbstractType::Handle(result_type()).type_class_id() == kFfiHandleCid;
|
||||
}
|
||||
|
||||
// Keep consistent with BaseMarshaller::IsCompound.
|
||||
|
@ -8453,11 +8465,23 @@ void Function::AssignFfiCallbackId(int32_t callback_id) const {
|
|||
}
|
||||
|
||||
bool Function::FfiIsLeaf() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
if (IsFfiTrampoline()) {
|
||||
const Object& obj = Object::Handle(untag()->data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).is_leaf();
|
||||
}
|
||||
ASSERT(is_ffi_native());
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
auto const& native_instance = Instance::Handle(GetNativeAnnotation());
|
||||
const auto& native_class = Class::Handle(zone, native_instance.clazz());
|
||||
const auto& native_class_fields = Array::Handle(zone, native_class.fields());
|
||||
ASSERT(native_class_fields.Length() == 3);
|
||||
const auto& is_leaf_field =
|
||||
Field::Handle(zone, Field::RawCast(native_class_fields.At(2)));
|
||||
return Bool::Handle(zone,
|
||||
Bool::RawCast(native_instance.GetField(is_leaf_field)))
|
||||
.value();
|
||||
}
|
||||
|
||||
void Function::SetFfiIsLeaf(bool is_leaf) const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
|
@ -8609,6 +8633,32 @@ void Function::set_native_name(const String& value) const {
|
|||
pair.SetAt(NativeFunctionData::kNativeName, value);
|
||||
}
|
||||
|
||||
InstancePtr Function::GetNativeAnnotation() const {
|
||||
ASSERT(is_ffi_native());
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
auto& pragma_value = Object::Handle(zone);
|
||||
Library::FindPragma(dart::Thread::Current(), /*only_core=*/false,
|
||||
Object::Handle(zone, ptr()),
|
||||
String::Handle(zone, Symbols::vm_ffi_native().ptr()),
|
||||
/*multiple=*/false, &pragma_value);
|
||||
auto const& native_instance = Instance::Cast(pragma_value);
|
||||
ASSERT(!native_instance.IsNull());
|
||||
#if defined(DEBUG)
|
||||
const auto& native_class = Class::Handle(zone, native_instance.clazz());
|
||||
ASSERT(String::Handle(zone, native_class.UserVisibleName())
|
||||
.Equals(Symbols::FfiNative()));
|
||||
#endif
|
||||
return native_instance.ptr();
|
||||
}
|
||||
|
||||
bool Function::is_old_native() const {
|
||||
return is_native() && !is_external();
|
||||
}
|
||||
|
||||
bool Function::is_ffi_native() const {
|
||||
return is_native() && is_external();
|
||||
}
|
||||
|
||||
void Function::SetSignature(const FunctionType& value) const {
|
||||
set_signature(value);
|
||||
ASSERT(NumImplicitParameters() == value.num_implicit_parameters());
|
||||
|
@ -8998,7 +9048,7 @@ bool Function::IsOptimizable() const {
|
|||
return true;
|
||||
}
|
||||
if (ForceOptimize()) return true;
|
||||
if (is_native()) {
|
||||
if (is_old_native()) {
|
||||
// Native methods don't need to be optimized.
|
||||
return false;
|
||||
}
|
||||
|
@ -9081,7 +9131,7 @@ static bool InVmTests(const Function& function) {
|
|||
}
|
||||
|
||||
bool Function::ForceOptimize() const {
|
||||
if (RecognizedKindForceOptimize() || IsFfiTrampoline() ||
|
||||
if (RecognizedKindForceOptimize() || IsFfiTrampoline() || is_ffi_native() ||
|
||||
IsTypedDataViewFactory() || IsUnmodifiableTypedDataViewFactory()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -9196,7 +9246,7 @@ bool Function::RecognizedKindForceOptimize() const {
|
|||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
bool Function::CanBeInlined() const {
|
||||
if (ForceOptimize()) {
|
||||
if (IsFfiTrampoline()) {
|
||||
if (IsFfiTrampoline() || is_ffi_native()) {
|
||||
// We currently don't support inlining FFI trampolines. Some of them
|
||||
// are naturally non-inlinable because they contain a try/catch block,
|
||||
// but this condition is broader than strictly necessary.
|
||||
|
@ -10295,7 +10345,7 @@ FunctionPtr Function::New(const FunctionType& signature,
|
|||
const FfiTrampolineData& data =
|
||||
FfiTrampolineData::Handle(FfiTrampolineData::New());
|
||||
result.set_data(data);
|
||||
} else if (is_native) {
|
||||
} else if (result.is_old_native()) {
|
||||
const auto& data =
|
||||
Array::Handle(Array::New(NativeFunctionData::kLength, Heap::kOld));
|
||||
result.set_data(data);
|
||||
|
|
|
@ -2979,8 +2979,7 @@ class Function : public Object {
|
|||
// Can only be used on FFI trampolines.
|
||||
void SetFfiCSignature(const FunctionType& sig) const;
|
||||
|
||||
// Retrieves the "C signature" for an FFI trampoline.
|
||||
// Can only be used on FFI trampolines.
|
||||
// Retrieves the "C signature" for an FFI trampoline or FFI native.
|
||||
FunctionTypePtr FfiCSignature() const;
|
||||
|
||||
bool FfiCSignatureContainsHandles() const;
|
||||
|
@ -3073,6 +3072,10 @@ class Function : public Object {
|
|||
StringPtr native_name() const;
|
||||
void set_native_name(const String& name) const;
|
||||
|
||||
InstancePtr GetNativeAnnotation() const;
|
||||
bool is_ffi_native() const;
|
||||
bool is_old_native() const;
|
||||
|
||||
AbstractTypePtr result_type() const {
|
||||
return signature()->untag()->result_type();
|
||||
}
|
||||
|
@ -9663,6 +9666,8 @@ class FunctionType : public AbstractType {
|
|||
|
||||
static FunctionTypePtr Clone(const FunctionType& orig, Heap::Space space);
|
||||
|
||||
bool ContainsHandles() const;
|
||||
|
||||
private:
|
||||
static FunctionTypePtr New(Heap::Space space);
|
||||
|
||||
|
|
|
@ -543,6 +543,16 @@ void ObjectStore::LazyInitFfiMembers() {
|
|||
ASSERT(!function.IsNull());
|
||||
handle_native_finalizer_message_function_.store(function.ptr());
|
||||
|
||||
const auto& lib = Library::Handle(zone, Library::FfiLibrary());
|
||||
const Class& klass = Class::ZoneHandle(zone, lib.toplevel_class());
|
||||
ASSERT(!klass.IsNull());
|
||||
error = klass.EnsureIsFinalized(thread);
|
||||
ASSERT(error.IsNull());
|
||||
function = klass.LookupStaticFunctionAllowPrivate(
|
||||
Symbols::_ffi_resolver_function());
|
||||
ASSERT(!function.IsNull());
|
||||
ffi_resolver_function_.store(function.ptr());
|
||||
|
||||
cls = ffi_lib.LookupClass(Symbols::VarArgs());
|
||||
ASSERT(!cls.IsNull());
|
||||
varargs_class_.store(cls.ptr());
|
||||
|
|
|
@ -58,6 +58,7 @@ class ObjectPointerVisitor;
|
|||
LAZY_INTERNAL(Class, symbol_class) \
|
||||
LAZY_INTERNAL(Field, symbol_name_field) \
|
||||
LAZY_FFI(Class, varargs_class) \
|
||||
LAZY_FFI(Function, ffi_resolver_function) \
|
||||
LAZY_FFI(Function, handle_finalizer_message_function) \
|
||||
LAZY_FFI(Function, handle_native_finalizer_message_function) \
|
||||
LAZY_ASYNC(Type, non_nullable_future_never_type) \
|
||||
|
|
|
@ -116,6 +116,7 @@ class ObjectPointerVisitor;
|
|||
V(FfiInt8, "Int8") \
|
||||
V(FfiIntPtr, "IntPtr") \
|
||||
V(FfiIsolateLocalCallback, "_FfiIsolateLocalCallback") \
|
||||
V(FfiNative, "Native") \
|
||||
V(FfiNativeFunction, "NativeFunction") \
|
||||
V(FfiNativeType, "NativeType") \
|
||||
V(FfiNativeTypes, "nativeTypes") \
|
||||
|
@ -426,6 +427,7 @@ class ObjectPointerVisitor;
|
|||
V(_checkSetRangeArguments, "_checkSetRangeArguments") \
|
||||
V(_current, "_current") \
|
||||
V(_ensureScheduleImmediate, "_ensureScheduleImmediate") \
|
||||
V(_ffi_resolver_function, "_ffi_resolver_function") \
|
||||
V(future, "future") \
|
||||
V(_future, "_future") \
|
||||
V(_getRegisters, "_getRegisters") \
|
||||
|
@ -525,6 +527,7 @@ class ObjectPointerVisitor;
|
|||
V(vm_exact_result_type, "vm:exact-result-type") \
|
||||
V(vm_external_name, "vm:external-name") \
|
||||
V(vm_ffi_abi_specific_mapping, "vm:ffi:abi-specific-mapping") \
|
||||
V(vm_ffi_native, "vm:ffi:native") \
|
||||
V(vm_ffi_native_assets, "vm:ffi:native-assets") \
|
||||
V(vm_ffi_struct_fields, "vm:ffi:struct-fields") \
|
||||
V(vm_force_optimize, "vm:force-optimize") \
|
||||
|
|
|
@ -1355,3 +1355,11 @@ final class _ArraySize<T extends NativeType> implements Array<T> {
|
|||
Object get _typedDataBase =>
|
||||
throw UnsupportedError('_ArraySize._typedDataBase');
|
||||
}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class FfiNative<T> {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Native<T> {}
|
||||
|
|
|
@ -1347,3 +1347,6 @@ external Pointer<NativeFunction<IntPtr Function(Handle, Handle, IntPtr)>>
|
|||
final _ffi_resolver = _get_ffi_native_resolver<
|
||||
NativeFunction<IntPtr Function(Handle, Handle, IntPtr)>>()
|
||||
.asFunction<int Function(Object, Object, int)>();
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
int _ffi_resolver_function(Object a, Object s, int n) => _ffi_resolver(a, s, n);
|
||||
|
|
Loading…
Reference in a new issue