mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:59:47 +00:00
Revert "[vm/ffi] Express FFI call closures explicitly in AST"
This reverts commitfd2e9b9f1a
. Revert "[vm/ffi] Cleanup FFI call trampolines" This reverts commitc20f9eaf6f
. Reason: https://github.com/flutter/flutter/issues/140074 TEST=ci CoreLibraryReviewExempt: Implementation change only. Change-Id: Ib193d8f015fc66461fe3cc90550a92e1ba65f236 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341620 Commit-Queue: Alexander Markov <alexmarkov@google.com> Auto-Submit: Alexander Markov <alexmarkov@google.com> Reviewed-by: Siva Annamalai <asiva@google.com>
This commit is contained in:
parent
fbcae44b96
commit
1800039c2a
|
@ -230,7 +230,7 @@ class FfiTransformer extends Transformer {
|
|||
final Procedure abiSpecificIntegerArrayElemAt;
|
||||
final Procedure abiSpecificIntegerArraySetElemAt;
|
||||
final Procedure asFunctionMethod;
|
||||
final Procedure ffiCallMethod;
|
||||
final Procedure asFunctionInternal;
|
||||
final Procedure sizeOfMethod;
|
||||
final Procedure lookupFunctionMethod;
|
||||
final Procedure fromFunctionMethod;
|
||||
|
@ -283,8 +283,6 @@ class FfiTransformer extends Transformer {
|
|||
final Field nativeCallablePointerField;
|
||||
final Procedure nativeAddressOf;
|
||||
final Procedure nativePrivateAddressOf;
|
||||
final Class ffiCallClass;
|
||||
final Field ffiCallIsLeafField;
|
||||
|
||||
late final InterfaceType nativeFieldWrapperClass1Type;
|
||||
late final InterfaceType voidType;
|
||||
|
@ -465,7 +463,8 @@ class FfiTransformer extends Transformer {
|
|||
index.getProcedure('dart:ffi', 'AbiSpecificIntegerArray', '[]='),
|
||||
asFunctionMethod = index.getProcedure(
|
||||
'dart:ffi', 'NativeFunctionPointer', 'asFunction'),
|
||||
ffiCallMethod = index.getTopLevelProcedure('dart:ffi', '_ffiCall'),
|
||||
asFunctionInternal =
|
||||
index.getTopLevelProcedure('dart:ffi', '_asFunctionInternal'),
|
||||
sizeOfMethod = index.getTopLevelProcedure('dart:ffi', 'sizeOf'),
|
||||
lookupFunctionMethod = index.getProcedure(
|
||||
'dart:ffi', 'DynamicLibraryExtension', 'lookupFunction'),
|
||||
|
@ -572,9 +571,7 @@ class FfiTransformer extends Transformer {
|
|||
nativeAddressOf =
|
||||
index.getMember('dart:ffi', 'Native', 'addressOf') as Procedure,
|
||||
nativePrivateAddressOf =
|
||||
index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure,
|
||||
ffiCallClass = index.getClass('dart:ffi', '_FfiCall'),
|
||||
ffiCallIsLeafField = index.getField('dart:ffi', '_FfiCall', 'isLeaf') {
|
||||
index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure {
|
||||
nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
|
||||
coreTypes, Nullability.nonNullable);
|
||||
voidType = nativeTypesClasses[NativeType.kVoid]!
|
||||
|
@ -1202,6 +1199,37 @@ class FfiTransformer extends Transformer {
|
|||
..fileOffset = nestedExpression.fileOffset;
|
||||
}
|
||||
|
||||
/// Creates an invocation to asFunctionInternal.
|
||||
///
|
||||
/// Adds a native effect invoking a compound constructors if this is used
|
||||
/// as return type.
|
||||
Expression buildAsFunctionInternal({
|
||||
required Expression functionPointer,
|
||||
required DartType nativeSignature,
|
||||
required DartType dartSignature,
|
||||
required bool isLeaf,
|
||||
required int fileOffset,
|
||||
}) {
|
||||
final asFunctionInternalInvocation = StaticInvocation(
|
||||
asFunctionInternal,
|
||||
Arguments([
|
||||
functionPointer,
|
||||
BoolLiteral(isLeaf),
|
||||
], types: [
|
||||
dartSignature,
|
||||
nativeSignature,
|
||||
]))
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
final possibleCompoundReturn = findCompoundReturnType(dartSignature);
|
||||
if (possibleCompoundReturn != null) {
|
||||
return invokeCompoundConstructor(
|
||||
asFunctionInternalInvocation, possibleCompoundReturn);
|
||||
}
|
||||
|
||||
return asFunctionInternalInvocation;
|
||||
}
|
||||
|
||||
/// Returns the compound [Class] if a compound is returned, otherwise `null`.
|
||||
Class? findCompoundReturnType(DartType dartSignature) {
|
||||
if (dartSignature is! FunctionType) {
|
||||
|
|
|
@ -113,14 +113,9 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
// callback.
|
||||
int callbackCount = 0;
|
||||
|
||||
// Used to create private top-level trampoline methods with unique names
|
||||
// for each call.
|
||||
int callCount = 0;
|
||||
|
||||
@override
|
||||
TreeNode visitLibrary(Library node) {
|
||||
callbackCount = 0;
|
||||
callCount = 0;
|
||||
return super.visitLibrary(node);
|
||||
}
|
||||
|
||||
|
@ -354,12 +349,10 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
);
|
||||
final DartType nativeSignature = nativeType.typeArguments[0];
|
||||
|
||||
return _replaceAsFunction(
|
||||
return buildAsFunctionInternal(
|
||||
functionPointer: node.arguments.positional[0],
|
||||
pointerType: InterfaceType(
|
||||
pointerClass, Nullability.nonNullable, [nativeType]),
|
||||
nativeSignature: nativeSignature,
|
||||
dartSignature: dartType as FunctionType,
|
||||
dartSignature: dartType,
|
||||
isLeaf: isLeaf,
|
||||
fileOffset: node.fileOffset,
|
||||
);
|
||||
|
@ -435,84 +428,6 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
return node;
|
||||
}
|
||||
|
||||
/// Create Dart function which calls native code.
|
||||
///
|
||||
/// Adds a native effect invoking a compound constructors if this is used
|
||||
/// as return type.
|
||||
Expression _replaceAsFunction({
|
||||
required Expression functionPointer,
|
||||
required DartType pointerType,
|
||||
required DartType nativeSignature,
|
||||
required FunctionType dartSignature,
|
||||
required bool isLeaf,
|
||||
required int fileOffset,
|
||||
}) {
|
||||
assert(dartSignature.namedParameters.isEmpty);
|
||||
final functionPointerVarName = '#ffiTarget$callCount';
|
||||
final closureName = '#ffiClosure$callCount';
|
||||
++callCount;
|
||||
|
||||
final pointerVar = VariableDeclaration(functionPointerVarName,
|
||||
initializer: functionPointer, type: pointerType, isSynthesized: true);
|
||||
|
||||
final positionalParameters = [
|
||||
for (int i = 0; i < dartSignature.positionalParameters.length; ++i)
|
||||
VariableDeclaration(
|
||||
'arg${i + 1}',
|
||||
type: dartSignature.positionalParameters[i],
|
||||
)
|
||||
];
|
||||
|
||||
final closure = FunctionDeclaration(
|
||||
VariableDeclaration(closureName,
|
||||
type: dartSignature, isSynthesized: true)
|
||||
..addAnnotation(ConstantExpression(
|
||||
InstanceConstant(coreTypes.pragmaClass.reference, [], {
|
||||
coreTypes.pragmaName.fieldReference:
|
||||
StringConstant('vm:ffi:call-closure'),
|
||||
coreTypes.pragmaOptions.fieldReference: InstanceConstant(
|
||||
ffiCallClass.reference,
|
||||
[nativeSignature],
|
||||
{
|
||||
ffiCallIsLeafField.fieldReference: BoolConstant(isLeaf),
|
||||
},
|
||||
),
|
||||
}))),
|
||||
FunctionNode(
|
||||
Block([
|
||||
for (final param in positionalParameters)
|
||||
ExpressionStatement(StaticInvocation(
|
||||
nativeEffectMethod, Arguments([VariableGet(param)]))),
|
||||
ReturnStatement(StaticInvocation(
|
||||
ffiCallMethod,
|
||||
Arguments([
|
||||
VariableGet(pointerVar),
|
||||
], types: [
|
||||
dartSignature.returnType,
|
||||
]))
|
||||
..fileOffset = fileOffset),
|
||||
]),
|
||||
positionalParameters: positionalParameters,
|
||||
requiredParameterCount: dartSignature.requiredParameterCount,
|
||||
returnType: dartSignature.returnType)
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
final result = BlockExpression(
|
||||
Block([
|
||||
pointerVar,
|
||||
closure,
|
||||
]),
|
||||
VariableGet(closure.variable));
|
||||
|
||||
final possibleCompoundReturn = findCompoundReturnType(dartSignature);
|
||||
if (possibleCompoundReturn != null) {
|
||||
return invokeCompoundConstructor(result, possibleCompoundReturn);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Expression invokeCompoundConstructors(
|
||||
Expression nestedExpression, List<Class> compoundClasses) =>
|
||||
compoundClasses
|
||||
|
@ -547,6 +462,10 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
// 'lookupFunction' are constants, so by inlining the call to 'asFunction' at
|
||||
// the call-site, we ensure that there are no generic calls to 'asFunction'.
|
||||
Expression _replaceLookupFunction(StaticInvocation node) {
|
||||
// The generated code looks like:
|
||||
//
|
||||
// _asFunctionInternal<DS, NS>(lookup<NativeFunction<NS>>(symbolName),
|
||||
// isLeaf)
|
||||
final DartType nativeSignature = node.arguments.types[0];
|
||||
final DartType dartSignature = node.arguments.types[1];
|
||||
|
||||
|
@ -559,19 +478,21 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
final FunctionType lookupFunctionType =
|
||||
libraryLookupMethod.getterType as FunctionType;
|
||||
|
||||
final lookupResult = InstanceInvocation(InstanceAccessKind.Instance,
|
||||
node.arguments.positional[0], libraryLookupMethod.name, lookupArgs,
|
||||
final Expression lookupResult = InstanceInvocation(
|
||||
InstanceAccessKind.Instance,
|
||||
node.arguments.positional[0],
|
||||
libraryLookupMethod.name,
|
||||
lookupArgs,
|
||||
interfaceTarget: libraryLookupMethod,
|
||||
functionType: FunctionTypeInstantiator.instantiate(
|
||||
lookupFunctionType, lookupTypeArgs));
|
||||
|
||||
final isLeaf = getIsLeafBoolean(node) ?? false;
|
||||
|
||||
return _replaceAsFunction(
|
||||
return buildAsFunctionInternal(
|
||||
functionPointer: lookupResult,
|
||||
pointerType: lookupResult.functionType.returnType,
|
||||
nativeSignature: nativeSignature,
|
||||
dartSignature: dartSignature as FunctionType,
|
||||
dartSignature: dartSignature,
|
||||
isLeaf: isLeaf,
|
||||
fileOffset: node.fileOffset,
|
||||
);
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Tests for NativeFunctionPointer.asFunction transformation.
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
testVoidNoArg() {
|
||||
final pointer =
|
||||
Pointer<NativeFunction<Void Function()>>.fromAddress(0xdeadbeef);
|
||||
final function = pointer.asFunction<void Function()>();
|
||||
function();
|
||||
}
|
||||
|
||||
testIntInt() {
|
||||
final pointer =
|
||||
Pointer<NativeFunction<Int32 Function(Int64)>>.fromAddress(0xdeadbeef);
|
||||
final function = pointer.asFunction<int Function(int)>();
|
||||
return function(42);
|
||||
}
|
||||
|
||||
testLeaf5Args() {
|
||||
final pointer = Pointer<
|
||||
NativeFunction<
|
||||
Int32 Function(
|
||||
Int32, Int32, Int32, Int32, Int32)>>.fromAddress(0xdeadbeef);
|
||||
final function =
|
||||
pointer.asFunction<int Function(int, int, int, int, int)>(isLeaf: true);
|
||||
return function(1, 2, 3, 4, 5);
|
||||
}
|
||||
|
||||
void main() {
|
||||
testVoidNoArg();
|
||||
testIntInt();
|
||||
testLeaf5Args();
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:ffi" as ffi;
|
||||
import "dart:core" as core;
|
||||
import "dart:_internal" as _in;
|
||||
|
||||
import "dart:ffi";
|
||||
|
||||
static method testVoidNoArg() → dynamic {
|
||||
final ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<() → ffi::Void>>(3735928559);
|
||||
final () → void function = block {
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> #ffiTarget0 = pointer;
|
||||
@#C4
|
||||
function #ffiClosure0() → void {
|
||||
return ffi::_ffiCall<void>(#ffiTarget0);
|
||||
}
|
||||
} =>#ffiClosure0;
|
||||
function(){() → void};
|
||||
}
|
||||
[@vm.unboxing-info.metadata=()->i]static method testIntInt() → dynamic {
|
||||
final ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>>(3735928559);
|
||||
final (core::int) → core::int function = block {
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> #ffiTarget1 = pointer;
|
||||
@#C6
|
||||
function #ffiClosure1(core::int arg1) → core::int {
|
||||
_in::_nativeEffect(arg1);
|
||||
return ffi::_ffiCall<core::int>(#ffiTarget1);
|
||||
}
|
||||
} =>#ffiClosure1;
|
||||
return function(42){(core::int) → core::int};
|
||||
}
|
||||
[@vm.unboxing-info.metadata=()->i]static method testLeaf5Args() → dynamic {
|
||||
final ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>>(3735928559);
|
||||
final (core::int, core::int, core::int, core::int, core::int) → core::int function = block {
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> #ffiTarget2 = pointer;
|
||||
@#C9
|
||||
function #ffiClosure2(core::int arg1, core::int arg2, core::int arg3, core::int arg4, core::int arg5) → core::int {
|
||||
_in::_nativeEffect(arg1);
|
||||
_in::_nativeEffect(arg2);
|
||||
_in::_nativeEffect(arg3);
|
||||
_in::_nativeEffect(arg4);
|
||||
_in::_nativeEffect(arg5);
|
||||
return ffi::_ffiCall<core::int>(#ffiTarget2);
|
||||
}
|
||||
} =>#ffiClosure2;
|
||||
return function(1, 2, 3, 4, 5){(core::int, core::int, core::int, core::int, core::int) → core::int};
|
||||
}
|
||||
static method main() → void {
|
||||
self::testVoidNoArg();
|
||||
self::testIntInt();
|
||||
self::testLeaf5Args();
|
||||
}
|
||||
constants {
|
||||
#C1 = "vm:ffi:call-closure"
|
||||
#C2 = false
|
||||
#C3 = ffi::_FfiCall<() → ffi::Void> {isLeaf:#C2}
|
||||
#C4 = core::pragma {name:#C1, options:#C3}
|
||||
#C5 = ffi::_FfiCall<(ffi::Int64) → ffi::Int32> {isLeaf:#C2}
|
||||
#C6 = core::pragma {name:#C1, options:#C5}
|
||||
#C7 = true
|
||||
#C8 = ffi::_FfiCall<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32> {isLeaf:#C7}
|
||||
#C9 = core::pragma {name:#C1, options:#C8}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:ffi" as ffi;
|
||||
import "dart:core" as core;
|
||||
import "dart:_internal" as _in;
|
||||
|
||||
import "dart:ffi";
|
||||
|
||||
static method testVoidNoArg() → dynamic {
|
||||
final ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> pointer = ffi::Pointer::fromAddress<ffi::NativeFunction<() → ffi::Void>>(3735928559);
|
||||
final () → void function = block {
|
||||
synthesized ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> #ffiTarget0 = pointer;
|
||||
@#C4
|
||||
function #ffiClosure0() → void {
|
||||
return ffi::_ffiCall<void>(#ffiTarget0);
|
||||
}
|
||||
} =>#ffiClosure0;
|
||||
function(){() → void};
|
||||
}
|
||||
static method testIntInt() → dynamic {
|
||||
final ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> pointer = ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>>(3735928559);
|
||||
final (core::int) → core::int function = block {
|
||||
synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> #ffiTarget1 = pointer;
|
||||
@#C6
|
||||
function #ffiClosure1(core::int arg1) → core::int {
|
||||
_in::_nativeEffect(arg1);
|
||||
return ffi::_ffiCall<core::int>(#ffiTarget1);
|
||||
}
|
||||
} =>#ffiClosure1;
|
||||
return function(42){(core::int) → core::int};
|
||||
}
|
||||
static method testLeaf5Args() → dynamic {
|
||||
final ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> pointer = ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>>(3735928559);
|
||||
final (core::int, core::int, core::int, core::int, core::int) → core::int function = block {
|
||||
synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> #ffiTarget2 = pointer;
|
||||
@#C9
|
||||
function #ffiClosure2(core::int arg1, core::int arg2, core::int arg3, core::int arg4, core::int arg5) → core::int {
|
||||
_in::_nativeEffect(arg1);
|
||||
_in::_nativeEffect(arg2);
|
||||
_in::_nativeEffect(arg3);
|
||||
_in::_nativeEffect(arg4);
|
||||
_in::_nativeEffect(arg5);
|
||||
return ffi::_ffiCall<core::int>(#ffiTarget2);
|
||||
}
|
||||
} =>#ffiClosure2;
|
||||
return function(1, 2, 3, 4, 5){(core::int, core::int, core::int, core::int, core::int) → core::int};
|
||||
}
|
||||
static method main() → void {
|
||||
self::testVoidNoArg();
|
||||
self::testIntInt();
|
||||
self::testLeaf5Args();
|
||||
}
|
||||
constants {
|
||||
#C1 = "vm:ffi:call-closure"
|
||||
#C2 = false
|
||||
#C3 = ffi::_FfiCall<() → ffi::Void> {isLeaf:#C2}
|
||||
#C4 = core::pragma {name:#C1, options:#C3}
|
||||
#C5 = ffi::_FfiCall<(ffi::Int64) → ffi::Int32> {isLeaf:#C2}
|
||||
#C6 = core::pragma {name:#C1, options:#C5}
|
||||
#C7 = true
|
||||
#C8 = ffi::_FfiCall<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32> {isLeaf:#C7}
|
||||
#C9 = core::pragma {name:#C1, options:#C8}
|
||||
}
|
|
@ -67,13 +67,7 @@ static method testLookupFunctionReturn() → void {
|
|||
final ffi::DynamicLibrary dylib = [@vm.inferred-type.metadata=dart.ffi::DynamicLibrary] ffi::DynamicLibrary::executable();
|
||||
final () → self::Struct1 function1 = block {
|
||||
_in::_nativeEffect(new self::Struct1::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C18)));
|
||||
} => block {
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<() → self::Struct1>> #ffiTarget0 = [@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<() → self::Struct1>>("function1"){(core::String) → ffi::Pointer<ffi::NativeFunction<() → self::Struct1>>};
|
||||
@#C22
|
||||
function #ffiClosure0() → self::Struct1 {
|
||||
return ffi::_ffiCall<self::Struct1>(#ffiTarget0);
|
||||
}
|
||||
} =>#ffiClosure0;
|
||||
} =>ffi::_asFunctionInternal<() → self::Struct1, () → self::Struct1>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<() → self::Struct1>>("function1"){(core::String) → ffi::Pointer<ffi::NativeFunction<() → self::Struct1>>}, false);
|
||||
final self::Struct1 struct1 = function1(){() → self::Struct1};
|
||||
core::print(struct1);
|
||||
}
|
||||
|
@ -81,13 +75,7 @@ static method testAsFunctionReturn() → void {
|
|||
final ffi::Pointer<ffi::NativeFunction<() → self::Struct2>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<() → self::Struct2>>(3735928559);
|
||||
final () → self::Struct2 function2 = block {
|
||||
_in::_nativeEffect(new self::Struct2::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C18)));
|
||||
} => block {
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<() → self::Struct2>> #ffiTarget1 = pointer;
|
||||
@#C24
|
||||
function #ffiClosure1() → self::Struct2 {
|
||||
return ffi::_ffiCall<self::Struct2>(#ffiTarget1);
|
||||
}
|
||||
} =>#ffiClosure1;
|
||||
} =>ffi::_asFunctionInternal<() → self::Struct2, () → self::Struct2>(pointer, false);
|
||||
final self::Struct2 struct2 = function2(){() → self::Struct2};
|
||||
core::print(struct2);
|
||||
}
|
||||
|
@ -102,26 +90,12 @@ static method testFromFunctionArgument() → void {
|
|||
}
|
||||
static method testLookupFunctionArgument() → void {
|
||||
final ffi::DynamicLibrary dylib = [@vm.inferred-type.metadata=dart.ffi::DynamicLibrary] ffi::DynamicLibrary::executable();
|
||||
final (self::Struct5) → void function5 = block {
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(self::Struct5) → ffi::Void>> #ffiTarget2 = [@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<(self::Struct5) → ffi::Void>>("function5"){(core::String) → ffi::Pointer<ffi::NativeFunction<(self::Struct5) → ffi::Void>>};
|
||||
@#C26
|
||||
function #ffiClosure2(self::Struct5 arg1) → void {
|
||||
_in::_nativeEffect(arg1);
|
||||
return ffi::_ffiCall<void>(#ffiTarget2);
|
||||
}
|
||||
} =>#ffiClosure2;
|
||||
final (self::Struct5) → void function5 = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::Struct5) → void, (self::Struct5) → ffi::Void>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<(self::Struct5) → ffi::Void>>("function5"){(core::String) → ffi::Pointer<ffi::NativeFunction<(self::Struct5) → ffi::Void>>}, false);
|
||||
core::print(function5);
|
||||
}
|
||||
static method testAsFunctionArgument() → void {
|
||||
final ffi::Pointer<ffi::NativeFunction<(self::Struct6) → ffi::Void>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<(self::Struct6) → ffi::Void>>(3735928559);
|
||||
final (self::Struct6) → void function6 = block {
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(self::Struct6) → ffi::Void>> #ffiTarget3 = pointer;
|
||||
@#C28
|
||||
function #ffiClosure3(self::Struct6 arg1) → void {
|
||||
_in::_nativeEffect(arg1);
|
||||
return ffi::_ffiCall<void>(#ffiTarget3);
|
||||
}
|
||||
} =>#ffiClosure3;
|
||||
final (self::Struct6) → void function6 = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::Struct6) → void, (self::Struct6) → ffi::Void>(pointer, false);
|
||||
core::print(function6);
|
||||
}
|
||||
static method returnStruct7() → self::Struct7 {
|
||||
|
@ -161,14 +135,4 @@ constants {
|
|||
#C16 = static-tearoff self::useStruct3
|
||||
#C17 = static-tearoff self::returnStruct7
|
||||
#C18 = 1
|
||||
#C19 = "vm:ffi:call-closure"
|
||||
#C20 = false
|
||||
#C21 = ffi::_FfiCall<() → self::Struct1> {isLeaf:#C20}
|
||||
#C22 = core::pragma {name:#C19, options:#C21}
|
||||
#C23 = ffi::_FfiCall<() → self::Struct2> {isLeaf:#C20}
|
||||
#C24 = core::pragma {name:#C19, options:#C23}
|
||||
#C25 = ffi::_FfiCall<(self::Struct5) → ffi::Void> {isLeaf:#C20}
|
||||
#C26 = core::pragma {name:#C19, options:#C25}
|
||||
#C27 = ffi::_FfiCall<(self::Struct6) → ffi::Void> {isLeaf:#C20}
|
||||
#C28 = core::pragma {name:#C19, options:#C27}
|
||||
}
|
||||
|
|
|
@ -45,11 +45,3 @@ Related files:
|
|||
* [runtime/vm/kernel_loader.cc](../../../runtime/vm/kernel_loader.cc)
|
||||
* [runtime/vm/object.cc](../../../runtime/vm/object.cc)
|
||||
|
||||
## FFI Calls
|
||||
|
||||
This pragma is used to mark Dart closures which perform FFI calls:
|
||||
|
||||
```
|
||||
@pragma('vm:ffi:call-closure', _FfiCall<Int32 Function(Int32)>(isLeaf: false))
|
||||
int #ffiCall0(int arg1) => _ffiCall<int>(target);
|
||||
```
|
||||
|
|
|
@ -47,7 +47,6 @@ These pragma's are only used on AST nodes synthesized by us, so users defining t
|
|||
|
||||
| Pragma | Meaning |
|
||||
| --- | --- |
|
||||
| `vm:ffi:call-closure`| [Closure performing FFI calls](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) |
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#include "vm/compiler/assembler/assembler.h"
|
||||
#include "vm/compiler/ffi/call.h"
|
||||
#include "vm/compiler/ffi/callback.h"
|
||||
#include "vm/compiler/ffi/marshaller.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
|
@ -27,6 +28,11 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
// Static invocations to this method are translated directly in streaming FGB.
|
||||
DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 2) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_createNativeCallableListener, 1, 2) {
|
||||
const auto& send_function =
|
||||
Function::CheckedHandle(zone, arguments->NativeArg0());
|
||||
|
|
|
@ -321,6 +321,7 @@ namespace dart {
|
|||
V(VMService_DecodeAssets, 1) \
|
||||
V(VMService_AddUserTagsToStreamableSampleList, 1) \
|
||||
V(VMService_RemoveUserTagsFromStreamableSampleList, 1) \
|
||||
V(Ffi_asFunctionInternal, 2) \
|
||||
V(Ffi_createNativeCallableListener, 2) \
|
||||
V(Ffi_createNativeCallableIsolateLocal, 3) \
|
||||
V(Ffi_deleteNativeCallable, 1) \
|
||||
|
|
|
@ -425,7 +425,7 @@ struct CanonicalFfiCallbackFunctionTraits {
|
|||
f1.FfiCSignature() == f2.FfiCSignature() &&
|
||||
f1.FfiCallbackExceptionalReturn() ==
|
||||
f2.FfiCallbackExceptionalReturn() &&
|
||||
f1.GetFfiCallbackKind() == f2.GetFfiCallbackKind());
|
||||
f1.GetFfiFunctionKind() == f2.GetFfiFunctionKind());
|
||||
}
|
||||
static bool ReportStats() { return false; }
|
||||
};
|
||||
|
|
|
@ -1044,7 +1044,7 @@ void Precompiler::AddCalleesOfHelper(const Object& entry,
|
|||
// Local closure function.
|
||||
const auto& target = Function::Cast(entry);
|
||||
AddFunction(target, RetainReasons::kLocalClosure);
|
||||
if (target.IsFfiCallbackTrampoline()) {
|
||||
if (target.IsFfiTrampoline()) {
|
||||
const auto& callback_target =
|
||||
Function::Handle(Z, target.FfiCallbackTarget());
|
||||
if (!callback_target.IsNull()) {
|
||||
|
@ -1117,7 +1117,7 @@ void Precompiler::AddTypesOf(const Function& function) {
|
|||
const Class& owner = Class::Handle(Z, function.Owner());
|
||||
AddTypesOf(owner);
|
||||
|
||||
if (function.IsFfiCallbackTrampoline()) {
|
||||
if (function.IsFfiTrampoline()) {
|
||||
AddType(FunctionType::Handle(Z, function.FfiCSignature()));
|
||||
}
|
||||
|
||||
|
@ -2030,7 +2030,7 @@ void Precompiler::TraceForRetainedFunctions() {
|
|||
// Ffi trampoline functions are not reachable from program structure,
|
||||
// they are referenced only from code (object pool).
|
||||
if (!functions_to_retain_.ContainsKey(function) &&
|
||||
!function.IsFfiCallbackTrampoline()) {
|
||||
!function.IsFfiTrampoline()) {
|
||||
FATAL("Function %s was not traced in TraceForRetainedFunctions\n",
|
||||
function.ToFullyQualifiedCString());
|
||||
}
|
||||
|
@ -2189,7 +2189,7 @@ void Precompiler::DropFunctions() {
|
|||
// which need the signature.
|
||||
return AddRetainReason(sig, RetainReasons::kClosureSignature);
|
||||
}
|
||||
if (function.IsFfiCallbackTrampoline()) {
|
||||
if (function.IsFfiTrampoline()) {
|
||||
// FFI trampolines may be dynamically called.
|
||||
return AddRetainReason(sig, RetainReasons::kFfiTrampolineSignature);
|
||||
}
|
||||
|
@ -3015,7 +3015,7 @@ void Precompiler::DiscardCodeObjects() {
|
|||
}
|
||||
|
||||
// Retain Code objects corresponding to FFI trampolines.
|
||||
if (function_.IsFfiCallbackTrampoline()) {
|
||||
if (function_.IsFfiTrampoline()) {
|
||||
++codes_with_ffi_trampoline_function_;
|
||||
return;
|
||||
}
|
||||
|
@ -3455,7 +3455,8 @@ void PrecompileParsedFunctionHelper::FinalizeCompilation(
|
|||
function.AttachCode(code);
|
||||
}
|
||||
|
||||
if (function.IsFfiCallbackTrampoline()) {
|
||||
if (function.IsFfiTrampoline() &&
|
||||
function.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
|
||||
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ void FlowGraph::ReplaceCurrentInstruction(ForwardInstructionIterator* iterator,
|
|||
bool FlowGraph::ShouldReorderBlocks(const Function& function,
|
||||
bool is_optimized) {
|
||||
return is_optimized && FLAG_reorder_basic_blocks &&
|
||||
!function.is_intrinsic() && !function.IsFfiCallbackTrampoline();
|
||||
!function.is_intrinsic() && !function.IsFfiTrampoline();
|
||||
}
|
||||
|
||||
GrowableArray<BlockEntryInstr*>* FlowGraph::CodegenBlockOrder(
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "vm/compiler/backend/flow_graph.h"
|
||||
#include "vm/compiler/backend/il.h"
|
||||
#include "vm/compiler/backend/range_analysis.h"
|
||||
#include "vm/compiler/ffi/call.h"
|
||||
#include "vm/compiler/frontend/flow_graph_builder.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/parser.h"
|
||||
|
@ -836,12 +837,20 @@ void FlowGraphSerializer::WriteTrait<const Function&>::Write(
|
|||
return;
|
||||
}
|
||||
case UntaggedFunction::kFfiTrampoline: {
|
||||
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiCallbackKind()));
|
||||
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiFunctionKind()));
|
||||
s->Write<const FunctionType&>(
|
||||
FunctionType::Handle(zone, x.FfiCSignature()));
|
||||
s->Write<const Function&>(Function::Handle(zone, x.FfiCallbackTarget()));
|
||||
s->Write<const Instance&>(
|
||||
Instance::Handle(zone, x.FfiCallbackExceptionalReturn()));
|
||||
if (x.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
|
||||
s->Write<const Function&>(
|
||||
Function::Handle(zone, x.FfiCallbackTarget()));
|
||||
s->Write<const Instance&>(
|
||||
Instance::Handle(zone, x.FfiCallbackExceptionalReturn()));
|
||||
} else {
|
||||
s->Write<const String&>(String::Handle(zone, x.name()));
|
||||
s->Write<const FunctionType&>(
|
||||
FunctionType::Handle(zone, x.signature()));
|
||||
s->Write<bool>(x.FfiIsLeaf());
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
|
@ -918,14 +927,23 @@ const Function& FlowGraphDeserializer::ReadTrait<const Function&>::Read(
|
|||
target.GetDynamicInvocationForwarder(name));
|
||||
}
|
||||
case UntaggedFunction::kFfiTrampoline: {
|
||||
const FfiCallbackKind kind =
|
||||
static_cast<FfiCallbackKind>(d->Read<uint8_t>());
|
||||
const FfiFunctionKind kind =
|
||||
static_cast<FfiFunctionKind>(d->Read<uint8_t>());
|
||||
const FunctionType& c_signature = d->Read<const FunctionType&>();
|
||||
const Function& callback_target = d->Read<const Function&>();
|
||||
const Instance& exceptional_return = d->Read<const Instance&>();
|
||||
return Function::ZoneHandle(
|
||||
zone, compiler::ffi::NativeCallbackFunction(
|
||||
c_signature, callback_target, exceptional_return, kind));
|
||||
if (kind != FfiFunctionKind::kCall) {
|
||||
const Function& callback_target = d->Read<const Function&>();
|
||||
const Instance& exceptional_return = d->Read<const Instance&>();
|
||||
return Function::ZoneHandle(
|
||||
zone, compiler::ffi::NativeCallbackFunction(
|
||||
c_signature, callback_target, exceptional_return, kind));
|
||||
} else {
|
||||
const String& name = d->Read<const String&>();
|
||||
const FunctionType& signature = d->Read<const FunctionType&>();
|
||||
const bool is_leaf = d->Read<bool>();
|
||||
return Function::ZoneHandle(
|
||||
zone, compiler::ffi::TrampolineFunction(name, signature,
|
||||
c_signature, is_leaf));
|
||||
}
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
|
|
|
@ -100,6 +100,8 @@ compiler_sources = [
|
|||
"compiler_timings.h",
|
||||
"ffi/abi.cc",
|
||||
"ffi/abi.h",
|
||||
"ffi/call.cc",
|
||||
"ffi/call.h",
|
||||
"ffi/callback.cc",
|
||||
"ffi/callback.h",
|
||||
"ffi/frame_rebase.cc",
|
||||
|
|
83
runtime/vm/compiler/ffi/call.cc
Normal file
83
runtime/vm/compiler/ffi/call.cc
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
#include "vm/compiler/ffi/call.h"
|
||||
|
||||
#include "vm/class_finalizer.h"
|
||||
#include "vm/symbols.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
namespace compiler {
|
||||
|
||||
namespace ffi {
|
||||
|
||||
// TODO(dartbug.com/36607): Cache the trampolines.
|
||||
FunctionPtr TrampolineFunction(const String& name,
|
||||
const FunctionType& signature,
|
||||
const FunctionType& c_signature,
|
||||
bool is_leaf) {
|
||||
ASSERT(signature.num_implicit_parameters() == 1);
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
const Library& lib = Library::Handle(zone, Library::FfiLibrary());
|
||||
const Class& owner_class = Class::Handle(zone, lib.toplevel_class());
|
||||
Function& function = Function::Handle(
|
||||
zone, Function::New(signature, name, UntaggedFunction::kFfiTrampoline,
|
||||
/*is_static=*/true,
|
||||
/*is_const=*/false,
|
||||
/*is_abstract=*/false,
|
||||
/*is_external=*/false,
|
||||
/*is_native=*/false, owner_class,
|
||||
TokenPosition::kMinSource));
|
||||
function.set_is_debuggable(false);
|
||||
|
||||
// Create unique names for the parameters, as they are used in scope building
|
||||
// and error messages.
|
||||
if (signature.num_fixed_parameters() > 0) {
|
||||
function.CreateNameArray();
|
||||
function.SetParameterNameAt(0, Symbols::ClosureParameter());
|
||||
auto& param_name = String::Handle(zone);
|
||||
for (intptr_t i = 1, n = signature.num_fixed_parameters(); i < n; ++i) {
|
||||
param_name = Symbols::NewFormatted(thread, ":ffi_param%" Pd, i);
|
||||
function.SetParameterNameAt(i, param_name);
|
||||
}
|
||||
}
|
||||
|
||||
function.SetFfiCSignature(c_signature);
|
||||
function.SetFfiIsLeaf(is_leaf);
|
||||
function.SetFfiFunctionKind(FfiFunctionKind::kCall);
|
||||
|
||||
return function.ptr();
|
||||
}
|
||||
|
||||
FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
|
||||
const FunctionType& c_signature,
|
||||
bool is_leaf,
|
||||
const String& function_name) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
String& name =
|
||||
String::Handle(zone, Symbols::NewFormatted(thread, "FfiTrampoline_%s",
|
||||
function_name.ToCString()));
|
||||
|
||||
// Trampolines have no optional arguments.
|
||||
FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
|
||||
const intptr_t num_fixed = dart_signature.num_fixed_parameters();
|
||||
signature.set_num_implicit_parameters(1);
|
||||
signature.set_num_fixed_parameters(num_fixed);
|
||||
signature.set_result_type(
|
||||
AbstractType::Handle(zone, dart_signature.result_type()));
|
||||
signature.set_parameter_types(
|
||||
Array::Handle(zone, dart_signature.parameter_types()));
|
||||
signature ^= ClassFinalizer::FinalizeType(signature);
|
||||
|
||||
return TrampolineFunction(name, signature, c_signature, is_leaf);
|
||||
}
|
||||
|
||||
} // namespace ffi
|
||||
|
||||
} // namespace compiler
|
||||
|
||||
} // namespace dart
|
38
runtime/vm/compiler/ffi/call.h
Normal file
38
runtime/vm/compiler/ffi/call.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2020, 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_VM_COMPILER_FFI_CALL_H_
|
||||
#define RUNTIME_VM_COMPILER_FFI_CALL_H_
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#error "AOT runtime should not use compiler sources (including header files)"
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#include <platform/globals.h>
|
||||
|
||||
#include "vm/raw_object.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
namespace compiler {
|
||||
|
||||
namespace ffi {
|
||||
|
||||
FunctionPtr TrampolineFunction(const String& name,
|
||||
const FunctionType& signature,
|
||||
const FunctionType& c_signature,
|
||||
bool is_leaf);
|
||||
|
||||
FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
|
||||
const FunctionType& c_signature,
|
||||
bool is_leaf,
|
||||
const String& function_name);
|
||||
|
||||
} // namespace ffi
|
||||
|
||||
} // namespace compiler
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_COMPILER_FFI_CALL_H_
|
|
@ -18,13 +18,13 @@ namespace ffi {
|
|||
const String& NativeCallbackFunctionName(Thread* thread,
|
||||
Zone* zone,
|
||||
const Function& dart_target,
|
||||
FfiCallbackKind kind) {
|
||||
FfiFunctionKind kind) {
|
||||
switch (kind) {
|
||||
case FfiCallbackKind::kAsyncCallback:
|
||||
case FfiFunctionKind::kAsyncCallback:
|
||||
return Symbols::FfiAsyncCallback();
|
||||
case FfiCallbackKind::kIsolateLocalClosureCallback:
|
||||
case FfiFunctionKind::kIsolateLocalClosureCallback:
|
||||
return Symbols::FfiIsolateLocalCallback();
|
||||
case FfiCallbackKind::kIsolateLocalStaticCallback:
|
||||
case FfiFunctionKind::kIsolateLocalStaticCallback:
|
||||
return String::Handle(
|
||||
zone, Symbols::FromConcat(thread, Symbols::FfiCallback(),
|
||||
String::Handle(zone, dart_target.name())));
|
||||
|
@ -36,7 +36,7 @@ const String& NativeCallbackFunctionName(Thread* thread,
|
|||
FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
||||
const Function& dart_target,
|
||||
const Instance& exceptional_return,
|
||||
FfiCallbackKind kind) {
|
||||
FfiFunctionKind kind) {
|
||||
Thread* const thread = Thread::Current();
|
||||
Zone* const zone = thread->zone();
|
||||
Function& function = Function::Handle(zone);
|
||||
|
@ -64,7 +64,7 @@ FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
|||
// the body.
|
||||
function.SetFfiCSignature(c_signature);
|
||||
function.SetFfiCallbackTarget(dart_target);
|
||||
function.SetFfiCallbackKind(kind);
|
||||
function.SetFfiFunctionKind(kind);
|
||||
|
||||
// We need to load the exceptional return value as a constant in the generated
|
||||
// function. Even though the FE ensures that it is a constant, it could still
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace ffi {
|
|||
FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
||||
const Function& dart_target,
|
||||
const Instance& exceptional_return,
|
||||
FfiCallbackKind kind);
|
||||
FfiFunctionKind kind);
|
||||
|
||||
// Builds a mapping from `callback-id` to code object / ...
|
||||
//
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "vm/compiler/backend/range_analysis.h" // For Range.
|
||||
#include "vm/compiler/ffi/call.h"
|
||||
#include "vm/compiler/frontend/flow_graph_builder.h" // For InlineExitCollector.
|
||||
#include "vm/compiler/jit/compiler.h" // For Compiler::IsBackgroundCompilation().
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
|
@ -1024,6 +1025,65 @@ Fragment BaseFlowGraphBuilder::Box(Representation from) {
|
|||
return Fragment(box);
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall(
|
||||
const TypeArguments& signatures,
|
||||
bool is_leaf) {
|
||||
ASSERT(signatures.Length() == 2);
|
||||
const auto& sig0 = AbstractType::Handle(signatures.TypeAt(0));
|
||||
const auto& sig1 = AbstractType::Handle(signatures.TypeAt(1));
|
||||
|
||||
if (!signatures.IsInstantiated() || !sig0.IsFunctionType() ||
|
||||
!sig1.IsFunctionType()) {
|
||||
const auto& msg = String::Handle(String::NewFormatted(
|
||||
"Invalid type arguments passed to dart:ffi _asFunctionInternal: %s",
|
||||
String::Handle(signatures.UserVisibleName()).ToCString()));
|
||||
const auto& language_error =
|
||||
Error::Handle(LanguageError::New(msg, Report::kError, Heap::kOld));
|
||||
Report::LongJump(language_error);
|
||||
}
|
||||
|
||||
const auto& dart_type = FunctionType::Cast(sig0);
|
||||
const auto& native_type = FunctionType::Cast(sig1);
|
||||
|
||||
// AbiSpecificTypes can have an incomplete mapping.
|
||||
const char* error = nullptr;
|
||||
compiler::ffi::NativeFunctionTypeFromFunctionType(zone_, native_type, &error);
|
||||
if (error != nullptr) {
|
||||
const auto& language_error = Error::Handle(
|
||||
LanguageError::New(String::Handle(String::New(error, Heap::kOld)),
|
||||
Report::kError, Heap::kOld));
|
||||
Report::LongJump(language_error);
|
||||
}
|
||||
|
||||
const auto& name =
|
||||
String::Handle(parsed_function_->function().UserVisibleName());
|
||||
const Function& target = Function::ZoneHandle(
|
||||
compiler::ffi::TrampolineFunction(dart_type, native_type, is_leaf, name));
|
||||
|
||||
Fragment code;
|
||||
// Store the pointer in the context, we cannot load the untagged address
|
||||
// here as these can be unoptimized call sites.
|
||||
LocalVariable* pointer = MakeTemporary();
|
||||
|
||||
code += Constant(target);
|
||||
|
||||
auto& context_slots = CompilerState::Current().GetDummyContextSlots(
|
||||
/*context_id=*/0, /*num_variables=*/1);
|
||||
code += AllocateContext(context_slots);
|
||||
LocalVariable* context = MakeTemporary();
|
||||
|
||||
code += LoadLocal(context);
|
||||
code += LoadLocal(pointer);
|
||||
code += StoreNativeField(*context_slots[0]);
|
||||
|
||||
code += AllocateClosure();
|
||||
|
||||
// Drop address.
|
||||
code += DropTempsPreserveTop(1);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::DebugStepCheck(TokenPosition position) {
|
||||
#ifdef PRODUCT
|
||||
return Fragment();
|
||||
|
|
|
@ -405,6 +405,12 @@ class BaseFlowGraphBuilder {
|
|||
return stack_ == nullptr ? 0 : stack_->definition()->temp_index() + 1;
|
||||
}
|
||||
|
||||
// Builds the graph for an invocation of '_asFunctionInternal'.
|
||||
//
|
||||
// 'signatures' contains the pair [<dart signature>, <native signature>].
|
||||
Fragment BuildFfiAsFunctionInternalCall(const TypeArguments& signatures,
|
||||
bool is_leaf);
|
||||
|
||||
Fragment AllocateObject(TokenPosition position,
|
||||
const Class& klass,
|
||||
intptr_t argument_count);
|
||||
|
|
|
@ -3348,18 +3348,18 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(TokenPosition* p) {
|
|||
return BuildNativeEffect();
|
||||
case MethodRecognizer::kReachabilityFence:
|
||||
return BuildReachabilityFence();
|
||||
case MethodRecognizer::kFfiCall:
|
||||
return BuildFfiCall();
|
||||
case MethodRecognizer::kFfiAsFunctionInternal:
|
||||
return BuildFfiAsFunctionInternal();
|
||||
case MethodRecognizer::kFfiNativeCallbackFunction:
|
||||
return BuildFfiNativeCallbackFunction(
|
||||
FfiCallbackKind::kIsolateLocalStaticCallback);
|
||||
FfiFunctionKind::kIsolateLocalStaticCallback);
|
||||
case MethodRecognizer::kFfiNativeAddressOf:
|
||||
return BuildFfiNativeAddressOf();
|
||||
case MethodRecognizer::kFfiNativeIsolateLocalCallbackFunction:
|
||||
return BuildFfiNativeCallbackFunction(
|
||||
FfiCallbackKind::kIsolateLocalClosureCallback);
|
||||
FfiFunctionKind::kIsolateLocalClosureCallback);
|
||||
case MethodRecognizer::kFfiNativeAsyncCallbackFunction:
|
||||
return BuildFfiNativeCallbackFunction(FfiCallbackKind::kAsyncCallback);
|
||||
return BuildFfiNativeCallbackFunction(FfiFunctionKind::kAsyncCallback);
|
||||
case MethodRecognizer::kFfiLoadAbiSpecificInt:
|
||||
return BuildLoadAbiSpecificInt(/*at_index=*/false);
|
||||
case MethodRecognizer::kFfiLoadAbiSpecificIntAtIndex:
|
||||
|
@ -6221,46 +6221,34 @@ Fragment StreamingFlowGraphBuilder::BuildStoreAbiSpecificInt(bool at_index) {
|
|||
return code;
|
||||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::BuildFfiCall() {
|
||||
Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
|
||||
const intptr_t argc = ReadUInt(); // Read argument count.
|
||||
ASSERT(argc == 1); // Target pointer.
|
||||
ASSERT(argc == 2); // Pointer, isLeaf.
|
||||
const intptr_t list_length = ReadListLength(); // Read types list length.
|
||||
T.BuildTypeArguments(list_length); // Read types.
|
||||
ASSERT(list_length == 2); // Dart signature, then native signature
|
||||
// Read types.
|
||||
const TypeArguments& type_arguments = T.BuildTypeArguments(list_length);
|
||||
Fragment code;
|
||||
// Read positional argument count.
|
||||
const intptr_t positional_count = ReadListLength();
|
||||
ASSERT(positional_count == argc);
|
||||
ASSERT(positional_count == 2);
|
||||
code += BuildExpression(); // Build first positional argument (pointer).
|
||||
|
||||
Fragment code;
|
||||
// Push the target function pointer passed as Pointer object.
|
||||
code += BuildExpression();
|
||||
// This can only be Pointer, so it is always safe to LoadUntagged.
|
||||
code += B->LoadUntagged(compiler::target::PointerBase::data_offset());
|
||||
code += B->ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
|
||||
// The second argument, `isLeaf`, is only used internally and dictates whether
|
||||
// we can do a lightweight leaf function call.
|
||||
bool is_leaf = false;
|
||||
Fragment frag = BuildExpression();
|
||||
ASSERT(frag.entry->IsConstant());
|
||||
if (frag.entry->AsConstant()->value().ptr() == Object::bool_true().ptr()) {
|
||||
is_leaf = true;
|
||||
}
|
||||
Pop();
|
||||
|
||||
// Skip (empty) named arguments list.
|
||||
const intptr_t named_args_len = ReadListLength();
|
||||
ASSERT(named_args_len == 0);
|
||||
|
||||
const auto& native_type = FunctionType::ZoneHandle(
|
||||
Z, parsed_function()->function().FfiCSignature());
|
||||
|
||||
// AbiSpecificTypes can have an incomplete mapping.
|
||||
const char* error = nullptr;
|
||||
compiler::ffi::NativeFunctionTypeFromFunctionType(Z, native_type, &error);
|
||||
if (error != nullptr) {
|
||||
const auto& language_error = Error::Handle(
|
||||
LanguageError::New(String::Handle(String::New(error, Heap::kOld)),
|
||||
Report::kError, Heap::kOld));
|
||||
Report::LongJump(language_error);
|
||||
}
|
||||
|
||||
code += B->FfiCallFunctionBody(parsed_function()->function(), native_type,
|
||||
/*first_argument_parameter_offset=*/1);
|
||||
|
||||
ASSERT(code.is_closed());
|
||||
|
||||
NullConstant(); // Maintain stack balance.
|
||||
|
||||
code += B->BuildFfiAsFunctionInternalCall(type_arguments, is_leaf);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
@ -6325,23 +6313,23 @@ Fragment StreamingFlowGraphBuilder::BuildCachableIdempotentCall(
|
|||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction(
|
||||
FfiCallbackKind kind) {
|
||||
FfiFunctionKind kind) {
|
||||
// The call-site must look like this (guaranteed by the FE which inserts it):
|
||||
//
|
||||
// FfiCallbackKind::kIsolateLocalStaticCallback:
|
||||
// FfiFunctionKind::kIsolateLocalStaticCallback:
|
||||
// _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn)
|
||||
//
|
||||
// FfiCallbackKind::kAsyncCallback:
|
||||
// FfiFunctionKind::kAsyncCallback:
|
||||
// _nativeAsyncCallbackFunction<NativeSignatureType>()
|
||||
//
|
||||
// FfiCallbackKind::kIsolateLocalClosureCallback:
|
||||
// FfiFunctionKind::kIsolateLocalClosureCallback:
|
||||
// _nativeIsolateLocalCallbackFunction<NativeSignatureType>(
|
||||
// exceptionalReturn)
|
||||
//
|
||||
// The FE also guarantees that the arguments are constants.
|
||||
|
||||
const bool has_target = kind == FfiCallbackKind::kIsolateLocalStaticCallback;
|
||||
const bool has_exceptional_return = kind != FfiCallbackKind::kAsyncCallback;
|
||||
const bool has_target = kind == FfiFunctionKind::kIsolateLocalStaticCallback;
|
||||
const bool has_exceptional_return = kind != FfiFunctionKind::kAsyncCallback;
|
||||
const intptr_t expected_argc =
|
||||
static_cast<int>(has_target) + static_cast<int>(has_exceptional_return);
|
||||
|
||||
|
|
|
@ -387,12 +387,13 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
|||
Fragment BuildLoadAbiSpecificInt(bool at_index);
|
||||
Fragment BuildStoreAbiSpecificInt(bool at_index);
|
||||
|
||||
// Build FG for FFI call.
|
||||
Fragment BuildFfiCall();
|
||||
// Build FG for '_asFunctionInternal'. Reads an Arguments from the
|
||||
// Kernel buffer and pushes the resulting closure.
|
||||
Fragment BuildFfiAsFunctionInternal();
|
||||
|
||||
// Build FG for '_nativeCallbackFunction'. Reads an Arguments from the
|
||||
// Kernel buffer and pushes the resulting Function object.
|
||||
Fragment BuildFfiNativeCallbackFunction(FfiCallbackKind kind);
|
||||
Fragment BuildFfiNativeCallbackFunction(FfiFunctionKind kind);
|
||||
|
||||
Fragment BuildFfiNativeAddressOf();
|
||||
|
||||
|
|
|
@ -400,12 +400,11 @@ Fragment FlowGraphBuilder::InstanceCall(
|
|||
}
|
||||
|
||||
Fragment FlowGraphBuilder::FfiCall(
|
||||
const compiler::ffi::CallMarshaller& marshaller,
|
||||
bool is_leaf) {
|
||||
const compiler::ffi::CallMarshaller& marshaller) {
|
||||
Fragment body;
|
||||
|
||||
FfiCallInstr* const call =
|
||||
new (Z) FfiCallInstr(GetNextDeoptId(), marshaller, is_leaf);
|
||||
FfiCallInstr* const call = new (Z) FfiCallInstr(
|
||||
GetNextDeoptId(), marshaller, parsed_function_->function().FfiIsLeaf());
|
||||
|
||||
for (intptr_t i = call->InputCount() - 1; i >= 0; --i) {
|
||||
call->SetInputAt(i, Pop());
|
||||
|
@ -5031,12 +5030,14 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToNative(
|
|||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
||||
const Function& function) {
|
||||
switch (function.GetFfiCallbackKind()) {
|
||||
case FfiCallbackKind::kIsolateLocalStaticCallback:
|
||||
case FfiCallbackKind::kIsolateLocalClosureCallback:
|
||||
switch (function.GetFfiFunctionKind()) {
|
||||
case FfiFunctionKind::kIsolateLocalStaticCallback:
|
||||
case FfiFunctionKind::kIsolateLocalClosureCallback:
|
||||
return BuildGraphOfSyncFfiCallback(function);
|
||||
case FfiCallbackKind::kAsyncCallback:
|
||||
case FfiFunctionKind::kAsyncCallback:
|
||||
return BuildGraphOfAsyncFfiCallback(function);
|
||||
case FfiFunctionKind::kCall:
|
||||
return BuildGraphOfFfiCall(function);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
|
@ -5121,6 +5122,26 @@ Fragment FlowGraphBuilder::FfiNativeLookupAddress(const Function& function) {
|
|||
return FfiNativeLookupAddress(native_instance);
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::FfiCallLookupAddress(const Function& function) {
|
||||
ASSERT(function.IsFfiTrampoline());
|
||||
const intptr_t kClosureParameterOffset = 0;
|
||||
Fragment body;
|
||||
// Push the function pointer, which is stored (as Pointer object) in the
|
||||
// first slot of the context.
|
||||
body +=
|
||||
LoadLocal(parsed_function_->ParameterVariable(kClosureParameterOffset));
|
||||
body += LoadNativeField(Slot::Closure_context());
|
||||
body += LoadNativeField(Slot::GetContextVariableSlotFor(
|
||||
thread_, *MakeImplicitClosureScope(
|
||||
Z, Class::Handle(IG->object_store()->ffi_pointer_class()))
|
||||
->context_variables()[0]));
|
||||
|
||||
// This can only be Pointer, so it is always safe to LoadUntagged.
|
||||
body += LoadUntagged(compiler::target::PointerBase::data_offset());
|
||||
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
|
||||
return body;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::FfiNativeFunctionBody(const Function& function) {
|
||||
ASSERT(function.is_ffi_native());
|
||||
ASSERT(!IsRecognizedMethodForFlowGraph(function));
|
||||
|
@ -5130,16 +5151,18 @@ Fragment FlowGraphBuilder::FfiNativeFunctionBody(const Function& function) {
|
|||
|
||||
Fragment body;
|
||||
body += FfiNativeLookupAddress(function);
|
||||
body += FfiCallFunctionBody(function, c_signature,
|
||||
/*first_argument_parameter_offset=*/0);
|
||||
body += FfiCallFunctionBody(function, c_signature);
|
||||
return body;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::FfiCallFunctionBody(
|
||||
const Function& function,
|
||||
const FunctionType& c_signature,
|
||||
intptr_t first_argument_parameter_offset) {
|
||||
ASSERT(function.is_ffi_native() || function.IsFfiCallClosure());
|
||||
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");
|
||||
|
||||
|
@ -5229,7 +5252,7 @@ Fragment FlowGraphBuilder::FfiCallFunctionBody(
|
|||
body += LoadLocal(return_compound_typed_data);
|
||||
}
|
||||
|
||||
body += FfiCall(marshaller, function.FfiIsLeaf());
|
||||
body += FfiCall(marshaller);
|
||||
|
||||
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
|
||||
if (marshaller.IsPointer(i)) {
|
||||
|
@ -5292,6 +5315,31 @@ Fragment FlowGraphBuilder::FfiCallFunctionBody(
|
|||
return body;
|
||||
}
|
||||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCall(const Function& function) {
|
||||
graph_entry_ =
|
||||
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
|
||||
|
||||
auto normal_entry = BuildFunctionEntry(graph_entry_);
|
||||
graph_entry_->set_normal_entry(normal_entry);
|
||||
|
||||
PrologueInfo prologue_info(-1, -1);
|
||||
|
||||
BlockEntryInstr* instruction_cursor =
|
||||
BuildPrologue(normal_entry, &prologue_info);
|
||||
|
||||
Fragment function_body(instruction_cursor);
|
||||
function_body += CheckStackOverflowInPrologue(function.token_pos());
|
||||
|
||||
const auto& c_signature =
|
||||
FunctionType::ZoneHandle(Z, function.FfiCSignature());
|
||||
|
||||
function_body += FfiCallLookupAddress(function);
|
||||
function_body += FfiCallFunctionBody(function, c_signature);
|
||||
|
||||
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
|
||||
prologue_info);
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::LoadNativeArg(
|
||||
const compiler::ffi::CallbackMarshaller& marshaller,
|
||||
intptr_t arg_index) {
|
||||
|
@ -5327,8 +5375,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfSyncFfiCallback(
|
|||
RELEASE_ASSERT(error == nullptr);
|
||||
RELEASE_ASSERT(marshaller_ptr != nullptr);
|
||||
const auto& marshaller = *marshaller_ptr;
|
||||
const bool is_closure = function.GetFfiCallbackKind() ==
|
||||
FfiCallbackKind::kIsolateLocalClosureCallback;
|
||||
const bool is_closure = function.GetFfiFunctionKind() ==
|
||||
FfiFunctionKind::kIsolateLocalClosureCallback;
|
||||
|
||||
graph_entry_ =
|
||||
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
|
||||
|
|
|
@ -137,6 +137,8 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
|||
FlowGraph* BuildGraphOfFfiTrampoline(const Function& function);
|
||||
FlowGraph* BuildGraphOfSyncFfiCallback(const Function& function);
|
||||
FlowGraph* BuildGraphOfAsyncFfiCallback(const Function& function);
|
||||
FlowGraph* BuildGraphOfFfiCall(const Function& function);
|
||||
Fragment FfiCallLookupAddress(const Function& function);
|
||||
|
||||
// Resolves the address of a native symbol from the constant data of a
|
||||
// vm:ffi:native pragma.
|
||||
|
@ -148,8 +150,7 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
|||
Fragment FfiNativeLookupAddress(const Function& function);
|
||||
// Expects target address on stack.
|
||||
Fragment FfiCallFunctionBody(const Function& function,
|
||||
const FunctionType& c_signature,
|
||||
intptr_t first_argument_parameter_offset);
|
||||
const FunctionType& c_signature);
|
||||
Fragment FfiNativeFunctionBody(const Function& function);
|
||||
Fragment NativeFunctionBody(const Function& function,
|
||||
LocalVariable* first_parameter);
|
||||
|
@ -203,8 +204,7 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
|||
bool receiver_is_not_smi = false,
|
||||
bool is_call_on_this = false);
|
||||
|
||||
Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller,
|
||||
bool is_leaf);
|
||||
Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller);
|
||||
|
||||
Fragment CCall(
|
||||
const compiler::ffi::NativeCallingConvention& native_calling_convention);
|
||||
|
|
|
@ -154,7 +154,8 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
|
|||
FunctionNodeHelper::kPositionalParameters);
|
||||
// NOTE: FunctionNode is read further below the if.
|
||||
|
||||
if (function.is_ffi_native() || function.IsFfiCallClosure()) {
|
||||
intptr_t pos = 0;
|
||||
if (function.is_ffi_native()) {
|
||||
needs_expr_temp_ = true;
|
||||
// Calls with handles need try/catch variables.
|
||||
if (function.FfiCSignatureContainsHandles()) {
|
||||
|
@ -166,9 +167,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
|
|||
FinalizeCatchVariables();
|
||||
--depth_.catch_;
|
||||
}
|
||||
}
|
||||
intptr_t pos = 0;
|
||||
if (function.IsClosureFunction()) {
|
||||
} else if (function.IsClosureFunction()) {
|
||||
LocalVariable* closure_parameter = MakeVariable(
|
||||
TokenPosition::kNoSource, TokenPosition::kNoSource,
|
||||
Symbols::ClosureParameter(), AbstractType::dynamic_type());
|
||||
|
@ -407,14 +406,17 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
|
|||
}
|
||||
case UntaggedFunction::kFfiTrampoline: {
|
||||
needs_expr_temp_ = true;
|
||||
// Callbacks need try/catch variables.
|
||||
++depth_.try_;
|
||||
AddTryVariables();
|
||||
--depth_.try_;
|
||||
++depth_.catch_;
|
||||
AddCatchVariables();
|
||||
FinalizeCatchVariables();
|
||||
--depth_.catch_;
|
||||
// Callbacks and calls with handles need try/catch variables.
|
||||
if ((function.GetFfiFunctionKind() != FfiFunctionKind::kCall ||
|
||||
function.FfiCSignatureContainsHandles())) {
|
||||
++depth_.try_;
|
||||
AddTryVariables();
|
||||
--depth_.try_;
|
||||
++depth_.catch_;
|
||||
AddCatchVariables();
|
||||
FinalizeCatchVariables();
|
||||
--depth_.catch_;
|
||||
}
|
||||
FALL_THROUGH;
|
||||
}
|
||||
case UntaggedFunction::kInvokeFieldDispatcher: {
|
||||
|
@ -435,7 +437,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
|
|||
LocalVariable* variable = MakeVariable(
|
||||
TokenPosition::kNoSource, TokenPosition::kNoSource,
|
||||
String::ZoneHandle(Z, function.ParameterNameAt(i)),
|
||||
AbstractType::ZoneHandle(Z, function.IsFfiCallbackTrampoline()
|
||||
AbstractType::ZoneHandle(Z, function.IsFfiTrampoline()
|
||||
? function.ParameterTypeAt(i)
|
||||
: Object::dynamic_type().ptr()));
|
||||
bool added = scope_->InsertParameterAt(i, variable);
|
||||
|
|
|
@ -476,7 +476,8 @@ CodePtr CompileParsedFunctionHelper::FinalizeCompilation(
|
|||
}
|
||||
}
|
||||
|
||||
if (function.IsFfiCallbackTrampoline()) {
|
||||
if (function.IsFfiTrampoline() &&
|
||||
function.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
|
||||
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
|
||||
}
|
||||
|
||||
|
|
|
@ -272,7 +272,7 @@ namespace dart {
|
|||
V(_WeakReference, get:target, WeakReference_getTarget, 0xc98185aa) \
|
||||
V(_WeakReference, set:_target, WeakReference_setTarget, 0xc71add9a) \
|
||||
V(::, _abi, FfiAbi, 0x7c3c2b95) \
|
||||
V(::, _ffiCall, FfiCall, 0x6118e962) \
|
||||
V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x630c8491) \
|
||||
V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3fe722bc) \
|
||||
V(::, _nativeAsyncCallbackFunction, FfiNativeAsyncCallbackFunction, \
|
||||
0xbec4b7b9) \
|
||||
|
|
|
@ -275,13 +275,13 @@ FfiCallbackMetadata::CreateIsolateLocalFfiCallback(Isolate* isolate,
|
|||
if (closure.IsNull()) {
|
||||
// If the closure is null, it means the target is a static function, so is
|
||||
// baked into the trampoline and is an ordinary sync callback.
|
||||
ASSERT(function.GetFfiCallbackKind() ==
|
||||
FfiCallbackKind::kIsolateLocalStaticCallback);
|
||||
ASSERT(function.GetFfiFunctionKind() ==
|
||||
FfiFunctionKind::kIsolateLocalStaticCallback);
|
||||
return CreateSyncFfiCallbackImpl(isolate, zone, function, nullptr,
|
||||
list_head);
|
||||
} else {
|
||||
ASSERT(function.GetFfiCallbackKind() ==
|
||||
FfiCallbackKind::kIsolateLocalClosureCallback);
|
||||
ASSERT(function.GetFfiFunctionKind() ==
|
||||
FfiFunctionKind::kIsolateLocalClosureCallback);
|
||||
return CreateSyncFfiCallbackImpl(isolate, zone, function,
|
||||
CreatePersistentHandle(isolate, closure),
|
||||
list_head);
|
||||
|
@ -319,7 +319,7 @@ FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateAsyncFfiCallback(
|
|||
const Function& send_function,
|
||||
Dart_Port send_port,
|
||||
Metadata** list_head) {
|
||||
ASSERT(send_function.GetFfiCallbackKind() == FfiCallbackKind::kAsyncCallback);
|
||||
ASSERT(send_function.GetFfiFunctionKind() == FfiFunctionKind::kAsyncCallback);
|
||||
return CreateMetadataEntry(isolate, TrampolineType::kAsync,
|
||||
GetEntryPoint(zone, send_function),
|
||||
static_cast<uint64_t>(send_port), list_head);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
FunctionPtr CreateTestFunction(FfiCallbackKind kind) {
|
||||
FunctionPtr CreateTestFunction(FfiFunctionKind kind) {
|
||||
const auto& ffi_lib = Library::Handle(Library::FfiLibrary());
|
||||
const auto& ffi_void = Class::Handle(ffi_lib.LookupClass(Symbols::FfiVoid()));
|
||||
const auto& ffi_void_type =
|
||||
|
@ -92,7 +92,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateSyncFfiCallback) {
|
|||
auto* zone = thread->zone();
|
||||
|
||||
const auto& func = Function::Handle(
|
||||
CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
|
||||
const auto& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
|
@ -185,7 +185,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateAsyncFfiCallback) {
|
|||
auto* zone = thread->zone();
|
||||
|
||||
const Function& func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kAsyncCallback));
|
||||
Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
|
@ -280,13 +280,13 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateIsolateLocalFfiCallback) {
|
|||
auto* zone = thread->zone();
|
||||
|
||||
const Function& func = Function::Handle(
|
||||
CreateTestFunction(FfiCallbackKind::kIsolateLocalClosureCallback));
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalClosureCallback));
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
// Using a FfiCallbackKind::kSync function as a dummy closure.
|
||||
// Using a FfiFunctionKind::kSync function as a dummy closure.
|
||||
const Function& closure_func = Function::Handle(
|
||||
CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
|
||||
const Context& context = Context::Handle(Context::null());
|
||||
const Closure& closure1 = Closure::Handle(
|
||||
Closure::New(Object::null_type_arguments(),
|
||||
|
@ -373,7 +373,7 @@ ISOLATE_UNIT_TEST_CASE(FfiCallbackMetadata_TrampolineRecycling) {
|
|||
auto* fcm = FfiCallbackMetadata::Instance();
|
||||
|
||||
const Function& func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kAsyncCallback));
|
||||
Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
|
@ -440,7 +440,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_DeleteTrampolines) {
|
|||
FfiCallbackMetadata::Metadata* list_head = nullptr;
|
||||
|
||||
const auto& sync_func = Function::Handle(
|
||||
CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
|
||||
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
|
||||
EXPECT(!sync_code.IsNull());
|
||||
|
||||
|
@ -521,11 +521,11 @@ static void RunBigRandomMultithreadedTest(uint64_t seed) {
|
|||
FfiCallbackMetadata::Metadata* list_head = nullptr;
|
||||
|
||||
const Function& async_func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kAsyncCallback));
|
||||
Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
|
||||
const Code& async_code = Code::Handle(async_func.EnsureHasCode());
|
||||
EXPECT(!async_code.IsNull());
|
||||
const Function& sync_func = Function::Handle(
|
||||
CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
|
||||
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
|
||||
EXPECT(!sync_code.IsNull());
|
||||
|
||||
|
|
|
@ -8336,8 +8336,7 @@ FunctionPtr Function::GetOutermostFunction() const {
|
|||
|
||||
FunctionPtr Function::implicit_closure_function() const {
|
||||
if (IsClosureFunction() || IsDispatcherOrImplicitAccessor() ||
|
||||
IsFieldInitializer() || IsFfiCallbackTrampoline() ||
|
||||
IsMethodExtractor()) {
|
||||
IsFieldInitializer() || IsFfiTrampoline() || IsMethodExtractor()) {
|
||||
return Function::null();
|
||||
}
|
||||
const Object& obj = Object::Handle(data());
|
||||
|
@ -8372,7 +8371,7 @@ void Function::set_implicit_closure_function(const Function& value) const {
|
|||
}
|
||||
|
||||
void Function::SetFfiCSignature(const FunctionType& sig) const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
FfiTrampolineData::Cast(obj).set_c_signature(sig);
|
||||
|
@ -8380,21 +8379,15 @@ void Function::SetFfiCSignature(const FunctionType& sig) const {
|
|||
|
||||
FunctionTypePtr Function::FfiCSignature() const {
|
||||
auto* const zone = Thread::Current()->zone();
|
||||
if (IsFfiCallbackTrampoline()) {
|
||||
if (IsFfiTrampoline()) {
|
||||
const Object& obj = Object::Handle(zone, data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).c_signature();
|
||||
}
|
||||
auto& pragma_value = Instance::Handle(zone);
|
||||
if (is_ffi_native()) {
|
||||
pragma_value = GetNativeAnnotation();
|
||||
} else if (IsFfiCallClosure()) {
|
||||
pragma_value = GetFfiCallClosurePragmaValue();
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
ASSERT(is_ffi_native());
|
||||
auto const& native_instance = Instance::Handle(GetNativeAnnotation());
|
||||
const auto& type_args =
|
||||
TypeArguments::Handle(zone, pragma_value.GetTypeArguments());
|
||||
TypeArguments::Handle(zone, native_instance.GetTypeArguments());
|
||||
ASSERT(type_args.Length() == 1);
|
||||
const auto& native_type =
|
||||
FunctionType::Cast(AbstractType::ZoneHandle(zone, type_args.TypeAt(0)));
|
||||
|
@ -8421,7 +8414,7 @@ bool FunctionType::ContainsHandles() const {
|
|||
|
||||
// Keep consistent with BaseMarshaller::IsCompound.
|
||||
bool Function::FfiCSignatureReturnsStruct() const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
ASSERT(IsFfiTrampoline());
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const auto& c_signature = FunctionType::Handle(zone, FfiCSignature());
|
||||
const auto& type = AbstractType::Handle(zone, c_signature.result_type());
|
||||
|
@ -8447,7 +8440,8 @@ bool Function::FfiCSignatureReturnsStruct() const {
|
|||
}
|
||||
|
||||
int32_t Function::FfiCallbackId() const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
ASSERT(IsFfiTrampoline());
|
||||
ASSERT(GetFfiFunctionKind() != FfiFunctionKind::kCall);
|
||||
|
||||
const auto& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
|
@ -8459,7 +8453,8 @@ int32_t Function::FfiCallbackId() const {
|
|||
}
|
||||
|
||||
void Function::AssignFfiCallbackId(int32_t callback_id) const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
ASSERT(IsFfiTrampoline());
|
||||
ASSERT(GetFfiFunctionKind() != FfiFunctionKind::kCall);
|
||||
|
||||
const auto& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
|
@ -8470,64 +8465,69 @@ void Function::AssignFfiCallbackId(int32_t callback_id) const {
|
|||
}
|
||||
|
||||
bool Function::FfiIsLeaf() const {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
auto& pragma_value = Instance::Handle(zone);
|
||||
if (is_ffi_native()) {
|
||||
pragma_value = GetNativeAnnotation();
|
||||
} else if (IsFfiCallClosure()) {
|
||||
pragma_value = GetFfiCallClosurePragmaValue();
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
if (IsFfiTrampoline()) {
|
||||
const Object& obj = Object::Handle(untag()->data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).is_leaf();
|
||||
}
|
||||
const auto& pragma_value_class = Class::Handle(zone, pragma_value.clazz());
|
||||
const auto& pragma_value_fields =
|
||||
Array::Handle(zone, pragma_value_class.fields());
|
||||
ASSERT(pragma_value_fields.Length() >= 1);
|
||||
const auto& is_leaf_field = Field::Handle(
|
||||
zone,
|
||||
Field::RawCast(pragma_value_fields.At(pragma_value_fields.Length() - 1)));
|
||||
ASSERT(is_leaf_field.name() == Symbols::isLeaf().ptr());
|
||||
return Bool::Handle(zone, Bool::RawCast(pragma_value.GetField(is_leaf_field)))
|
||||
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() == 4);
|
||||
const auto& is_leaf_field =
|
||||
Field::Handle(zone, Field::RawCast(native_class_fields.At(3)));
|
||||
ASSERT(!is_leaf_field.is_static());
|
||||
return Bool::Handle(zone,
|
||||
Bool::RawCast(native_instance.GetField(is_leaf_field)))
|
||||
.value();
|
||||
}
|
||||
|
||||
void Function::SetFfiIsLeaf(bool is_leaf) const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(untag()->data());
|
||||
ASSERT(!obj.IsNull());
|
||||
FfiTrampolineData::Cast(obj).set_is_leaf(is_leaf);
|
||||
}
|
||||
|
||||
FunctionPtr Function::FfiCallbackTarget() const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).callback_target();
|
||||
}
|
||||
|
||||
void Function::SetFfiCallbackTarget(const Function& target) const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
FfiTrampolineData::Cast(obj).set_callback_target(target);
|
||||
}
|
||||
|
||||
InstancePtr Function::FfiCallbackExceptionalReturn() const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).callback_exceptional_return();
|
||||
}
|
||||
|
||||
void Function::SetFfiCallbackExceptionalReturn(const Instance& value) const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
FfiTrampolineData::Cast(obj).set_callback_exceptional_return(value);
|
||||
}
|
||||
|
||||
FfiCallbackKind Function::GetFfiCallbackKind() const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
FfiFunctionKind Function::GetFfiFunctionKind() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).ffi_function_kind();
|
||||
}
|
||||
|
||||
void Function::SetFfiCallbackKind(FfiCallbackKind value) const {
|
||||
ASSERT(IsFfiCallbackTrampoline());
|
||||
void Function::SetFfiFunctionKind(FfiFunctionKind value) const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
FfiTrampolineData::Cast(obj).set_ffi_function_kind(value);
|
||||
|
@ -9132,8 +9132,7 @@ static bool InVmTests(const Function& function) {
|
|||
}
|
||||
|
||||
bool Function::ForceOptimize() const {
|
||||
if (RecognizedKindForceOptimize() || IsFfiCallClosure() ||
|
||||
IsFfiCallbackTrampoline() || is_ffi_native() ||
|
||||
if (RecognizedKindForceOptimize() || IsFfiTrampoline() || is_ffi_native() ||
|
||||
IsTypedDataViewFactory() || IsUnmodifiableTypedDataViewFactory()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -9174,25 +9173,6 @@ bool Function::IsCachableIdempotent() const {
|
|||
return InVmTests(*this);
|
||||
}
|
||||
|
||||
bool Function::IsFfiCallClosure() const {
|
||||
if (!IsNonImplicitClosureFunction()) return false;
|
||||
if (!has_pragma()) return false;
|
||||
return Library::FindPragma(Thread::Current(), /*only_core=*/false, *this,
|
||||
Symbols::vm_ffi_call_closure());
|
||||
}
|
||||
|
||||
InstancePtr Function::GetFfiCallClosurePragmaValue() const {
|
||||
ASSERT(IsFfiCallClosure());
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
auto& pragma_value = Object::Handle(zone);
|
||||
Library::FindPragma(thread, /*only_core=*/false, *this,
|
||||
Symbols::vm_ffi_call_closure(),
|
||||
/*multiple=*/false, &pragma_value);
|
||||
ASSERT(!pragma_value.IsNull());
|
||||
return Instance::Cast(pragma_value).ptr();
|
||||
}
|
||||
|
||||
bool Function::RecognizedKindForceOptimize() const {
|
||||
switch (recognized_kind()) {
|
||||
// Uses unboxed/untagged data not supported in unoptimized.
|
||||
|
@ -9267,7 +9247,7 @@ bool Function::RecognizedKindForceOptimize() const {
|
|||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
bool Function::CanBeInlined() const {
|
||||
if (ForceOptimize()) {
|
||||
if (IsFfiCallClosure() || IsFfiCallbackTrampoline() || is_ffi_native()) {
|
||||
if (IsFfiTrampoline() || is_ffi_native()) {
|
||||
// We currently don't support inlining FFI trampolines. Some of them
|
||||
// are naturally non-inlinable because they contain a try/catch block,
|
||||
// but this condition is broader than strictly necessary.
|
||||
|
@ -11019,7 +10999,7 @@ intptr_t Function::KernelLibraryOffset() const {
|
|||
|
||||
intptr_t Function::KernelLibraryIndex() const {
|
||||
if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher() ||
|
||||
IsFfiCallbackTrampoline()) {
|
||||
IsFfiTrampoline()) {
|
||||
return -1;
|
||||
}
|
||||
if (is_eval_function()) {
|
||||
|
@ -11782,12 +11762,16 @@ void FfiTrampolineData::set_callback_id(int32_t callback_id) const {
|
|||
StoreNonPointer(&untag()->callback_id_, callback_id);
|
||||
}
|
||||
|
||||
void FfiTrampolineData::set_is_leaf(bool is_leaf) const {
|
||||
StoreNonPointer(&untag()->is_leaf_, is_leaf);
|
||||
}
|
||||
|
||||
void FfiTrampolineData::set_callback_exceptional_return(
|
||||
const Instance& value) const {
|
||||
untag()->set_callback_exceptional_return(value.ptr());
|
||||
}
|
||||
|
||||
void FfiTrampolineData::set_ffi_function_kind(FfiCallbackKind kind) const {
|
||||
void FfiTrampolineData::set_ffi_function_kind(FfiFunctionKind kind) const {
|
||||
StoreNonPointer(&untag()->ffi_function_kind_, static_cast<uint8_t>(kind));
|
||||
}
|
||||
|
||||
|
|
|
@ -2949,7 +2949,8 @@ struct NameFormattingParams {
|
|||
}
|
||||
};
|
||||
|
||||
enum class FfiCallbackKind : uint8_t {
|
||||
enum class FfiFunctionKind : uint8_t {
|
||||
kCall,
|
||||
kIsolateLocalStaticCallback,
|
||||
kIsolateLocalClosureCallback,
|
||||
kAsyncCallback,
|
||||
|
@ -2985,31 +2986,37 @@ class Function : public Object {
|
|||
bool FfiCSignatureReturnsStruct() const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
// -1 for Dart -> native calls.
|
||||
int32_t FfiCallbackId() const;
|
||||
|
||||
// Should be called when ffi trampoline function object is created.
|
||||
void AssignFfiCallbackId(int32_t callback_id) const;
|
||||
|
||||
// Can only be called on FFI natives and FFI call closures.
|
||||
// Can only be called on FFI trampolines.
|
||||
bool FfiIsLeaf() const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
void SetFfiIsLeaf(bool is_leaf) const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
// Null for Dart -> native calls.
|
||||
FunctionPtr FfiCallbackTarget() const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
void SetFfiCallbackTarget(const Function& target) const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
// Null for Dart -> native calls.
|
||||
InstancePtr FfiCallbackExceptionalReturn() const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
void SetFfiCallbackExceptionalReturn(const Instance& value) const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
FfiCallbackKind GetFfiCallbackKind() const;
|
||||
FfiFunctionKind GetFfiFunctionKind() const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
void SetFfiCallbackKind(FfiCallbackKind value) const;
|
||||
void SetFfiFunctionKind(FfiFunctionKind value) const;
|
||||
|
||||
// Return the signature of this function.
|
||||
PRECOMPILER_WSR_FIELD_DECLARATION(FunctionType, signature);
|
||||
|
@ -3895,22 +3902,15 @@ class Function : public Object {
|
|||
}
|
||||
|
||||
// Returns true if this function represents an ffi trampoline.
|
||||
bool IsFfiCallbackTrampoline() const {
|
||||
bool IsFfiTrampoline() const {
|
||||
return kind() == UntaggedFunction::kFfiTrampoline;
|
||||
}
|
||||
static bool IsFfiCallbackTrampoline(FunctionPtr function) {
|
||||
static bool IsFfiTrampoline(FunctionPtr function) {
|
||||
NoSafepointScope no_safepoint;
|
||||
return function->untag()->kind_tag_.Read<KindBits>() ==
|
||||
UntaggedFunction::kFfiTrampoline;
|
||||
}
|
||||
|
||||
// Returns true if this function is a closure function
|
||||
// used to represent ffi call.
|
||||
bool IsFfiCallClosure() const;
|
||||
|
||||
// Returns value of vm:ffi:call-closure pragma.
|
||||
InstancePtr GetFfiCallClosurePragmaValue() const;
|
||||
|
||||
// Returns true for functions which execution can be suspended
|
||||
// using Suspend/Resume stubs. Such functions have an artificial
|
||||
// :suspend_state local variable at the fixed location of the frame.
|
||||
|
@ -4347,14 +4347,17 @@ class FfiTrampolineData : public Object {
|
|||
}
|
||||
void set_callback_exceptional_return(const Instance& value) const;
|
||||
|
||||
FfiCallbackKind ffi_function_kind() const {
|
||||
return static_cast<FfiCallbackKind>(untag()->ffi_function_kind_);
|
||||
FfiFunctionKind ffi_function_kind() const {
|
||||
return static_cast<FfiFunctionKind>(untag()->ffi_function_kind_);
|
||||
}
|
||||
void set_ffi_function_kind(FfiCallbackKind kind) const;
|
||||
void set_ffi_function_kind(FfiFunctionKind kind) const;
|
||||
|
||||
int32_t callback_id() const { return untag()->callback_id_; }
|
||||
void set_callback_id(int32_t value) const;
|
||||
|
||||
bool is_leaf() const { return untag()->is_leaf_; }
|
||||
void set_is_leaf(bool value) const;
|
||||
|
||||
static FfiTrampolineDataPtr New();
|
||||
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(FfiTrampolineData, Object);
|
||||
|
|
|
@ -1501,7 +1501,10 @@ class UntaggedFfiTrampolineData : public UntaggedObject {
|
|||
// Check 'callback_target_' to determine if this is a callback or not.
|
||||
int32_t callback_id_;
|
||||
|
||||
// The kind of trampoline this is. See FfiCallbackKind.
|
||||
// Whether this is a leaf call - i.e. one that doesn't call back into Dart.
|
||||
bool is_leaf_;
|
||||
|
||||
// The kind of trampoline this is. See FfiFunctionKind.
|
||||
uint8_t ffi_function_kind_;
|
||||
};
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ static FunctionPtr ResolveDynamicForReceiverClassWithCustomLookup(
|
|||
// FfiTrampolines are the only functions that can still be called
|
||||
// dynamically without going through a dynamic invocation forwarder.
|
||||
RELEASE_ASSERT(!Function::IsDynamicInvocationForwarderName(function_name) &&
|
||||
!function.IsFfiCallbackTrampoline());
|
||||
!function.IsFfiTrampoline());
|
||||
// The signature for this function was dropped in the precompiler, which
|
||||
// means it is not a possible target for a dynamic call in the program.
|
||||
// That means we're resolving an UnlinkedCall for an InstanceCall to
|
||||
|
|
|
@ -501,7 +501,6 @@ class ObjectPointerVisitor;
|
|||
V(from, "from") \
|
||||
V(get, "get") \
|
||||
V(index_temp, ":index_temp") \
|
||||
V(isLeaf, "isLeaf") \
|
||||
V(isPaused, "isPaused") \
|
||||
V(match_end_index, ":match_end_index") \
|
||||
V(match_start_index, ":match_start_index") \
|
||||
|
@ -530,7 +529,6 @@ class ObjectPointerVisitor;
|
|||
V(vm_exact_result_type, "vm:exact-result-type") \
|
||||
V(vm_external_name, "vm:external-name") \
|
||||
V(vm_ffi_abi_specific_mapping, "vm:ffi:abi-specific-mapping") \
|
||||
V(vm_ffi_call_closure, "vm:ffi:call-closure") \
|
||||
V(vm_ffi_native, "vm:ffi:native") \
|
||||
V(vm_ffi_native_assets, "vm:ffi:native-assets") \
|
||||
V(vm_ffi_struct_fields, "vm:ffi:struct-fields") \
|
||||
|
|
|
@ -82,21 +82,13 @@ int sizeOf<T extends NativeType>() {
|
|||
@pragma("vm:idempotent")
|
||||
external Pointer<T> _fromAddress<T extends NativeType>(int ptr);
|
||||
|
||||
/// Argument for vm:ffi:call-closure pragma describing FFI call.
|
||||
final class _FfiCall<NativeSignature> {
|
||||
// Implementation note: VM hardcodes the layout of this class (number and
|
||||
// order of its fields), so adding/removing/changing fields requires
|
||||
// updating the VM code (see Function::GetFfiCallClosurePragmaValue()).
|
||||
final bool isLeaf;
|
||||
const _FfiCall({this.isLeaf = false});
|
||||
}
|
||||
|
||||
// Helper function to perform FFI call.
|
||||
// Inserted by FFI kernel transformation into the FFI call closures.
|
||||
// Implemented in BuildFfiCall
|
||||
// in runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc.
|
||||
// The real implementation of this function (for interface calls) lives in
|
||||
// BuildFfiAsFunctionInternal in the Kernel frontend. No calls can actually
|
||||
// reach this function.
|
||||
@pragma("vm:recognized", "other")
|
||||
external ReturnType _ffiCall<ReturnType>(Pointer<NativeFunction> target);
|
||||
@pragma("vm:external-name", "Ffi_asFunctionInternal")
|
||||
external DS _asFunctionInternal<DS extends Function, NS extends Function>(
|
||||
Pointer<NativeFunction<NS>> ptr, bool isLeaf);
|
||||
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:idempotent")
|
||||
|
|
|
@ -69,7 +69,7 @@ final class Pointer<T extends NativeType> extends NativeType {
|
|||
/// On 32-bit systems, the upper 32-bits of the result are 0.
|
||||
external int get address;
|
||||
|
||||
/// Cast Pointer<T> to a Pointer<U>.
|
||||
/// Cast Pointer<T> to a Pointer<V>.
|
||||
external Pointer<U> cast<U extends NativeType>();
|
||||
|
||||
/// Equality for Pointers only depends on their address.
|
||||
|
@ -1157,10 +1157,6 @@ abstract final class NativeApi {
|
|||
/// NOTE: This is an experimental feature and may change in the future.
|
||||
@Since('2.19')
|
||||
final class Native<T> {
|
||||
// Implementation note: VM hardcodes the layout of this class (number and
|
||||
// order of its fields), so adding/removing/changing fields requires
|
||||
// updating the VM code (see Function::GetNativeAnnotation()).
|
||||
|
||||
/// The native symbol to be resolved, if not using the default.
|
||||
///
|
||||
/// If not specified, the default symbol used for native function lookup
|
||||
|
|
|
@ -116,27 +116,21 @@ void testAsFunction() {
|
|||
Expect.throws(() {
|
||||
nullptr
|
||||
.cast<NativeFunction<Int32 Function(Incomplete)>>()
|
||||
.asFunction<int Function(int)>()
|
||||
.call(42);
|
||||
.asFunction<int Function(int)>();
|
||||
});
|
||||
Expect.throws(() {
|
||||
nullptr
|
||||
.cast<NativeFunction<Incomplete Function(Int32)>>()
|
||||
.asFunction<int Function(int)>()
|
||||
.call(42);
|
||||
.asFunction<int Function(int)>();
|
||||
});
|
||||
final p = calloc<Int64>(100).cast<IncompleteArrayStruct>();
|
||||
Expect.throws(() {
|
||||
nullptr
|
||||
.cast<NativeFunction<Int32 Function(IncompleteArrayStruct)>>()
|
||||
.asFunction<int Function(IncompleteArrayStruct)>()
|
||||
.call(p.ref);
|
||||
.asFunction<int Function(IncompleteArrayStruct)>();
|
||||
});
|
||||
calloc.free(p);
|
||||
Expect.throws(() {
|
||||
nullptr
|
||||
.cast<NativeFunction<IncompleteArrayStruct Function()>>()
|
||||
.asFunction<IncompleteArrayStruct Function()>()
|
||||
.call();
|
||||
.asFunction<IncompleteArrayStruct Function()>();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -459,12 +459,6 @@ void testNoArgs() {
|
|||
Expect.approxEquals(1337.0, result);
|
||||
}
|
||||
|
||||
// Returns a possibly ofuscated 'arg2' identifier.
|
||||
String get arg2ObfuscatedName {
|
||||
final str = (arg2: 0).toString();
|
||||
return str.substring('('.length, str.length - ': 0)'.length);
|
||||
}
|
||||
|
||||
void testNativeFunctionNullableInt() {
|
||||
final sumPlus42 = ffiTestFunctions.lookupFunction<
|
||||
Int32 Function(Int32, Int32), int Function(int, int?)>("SumPlus42");
|
||||
|
@ -473,7 +467,7 @@ void testNativeFunctionNullableInt() {
|
|||
sumPlus42(3, null);
|
||||
} catch (e) {
|
||||
// TODO(http://dartbug.com/47098): Save param names to dwarf.
|
||||
Expect.isTrue(e.toString().contains(arg2ObfuscatedName) ||
|
||||
Expect.isTrue(e.toString().contains('ffi_param2') ||
|
||||
e.toString().contains('<optimized out>'));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue