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