Reland "[vm/ffi] Express FFI call closures explicitly in AST"

This reverts commit 1800039c2a.

The changes fd2e9b9f1a and
c20f9eaf6f are relanded as is.

Reason for revert was fixed separately in
https://dart-review.googlesource.com/c/sdk/+/341621

TEST=ci

CoreLibraryReviewExempt: Implementation change only.
Issue: https://github.com/dart-lang/sdk/issues/54172
Issue: https://github.com/dart-lang/sdk/issues/39692
Change-Id: I1a2324768502e5ffbce328127938c0d3c96c38ba
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341642
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2023-12-15 15:25:29 +00:00 committed by Commit Queue
parent a32d37ef84
commit dcdfcc2b8d
39 changed files with 552 additions and 512 deletions

View file

@ -236,7 +236,7 @@ class FfiTransformer extends Transformer {
final Procedure abiSpecificIntegerArrayElemAt;
final Procedure abiSpecificIntegerArraySetElemAt;
final Procedure asFunctionMethod;
final Procedure asFunctionInternal;
final Procedure ffiCallMethod;
final Procedure sizeOfMethod;
final Procedure lookupFunctionMethod;
final Procedure fromFunctionMethod;
@ -289,6 +289,8 @@ 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;
@ -481,8 +483,7 @@ class FfiTransformer extends Transformer {
index.getProcedure('dart:ffi', 'AbiSpecificIntegerArray', '[]='),
asFunctionMethod = index.getProcedure(
'dart:ffi', 'NativeFunctionPointer', 'asFunction'),
asFunctionInternal =
index.getTopLevelProcedure('dart:ffi', '_asFunctionInternal'),
ffiCallMethod = index.getTopLevelProcedure('dart:ffi', '_ffiCall'),
sizeOfMethod = index.getTopLevelProcedure('dart:ffi', 'sizeOf'),
lookupFunctionMethod = index.getProcedure(
'dart:ffi', 'DynamicLibraryExtension', 'lookupFunction'),
@ -589,7 +590,9 @@ class FfiTransformer extends Transformer {
nativeAddressOf =
index.getMember('dart:ffi', 'Native', 'addressOf') as Procedure,
nativePrivateAddressOf =
index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure {
index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure,
ffiCallClass = index.getClass('dart:ffi', '_FfiCall'),
ffiCallIsLeafField = index.getField('dart:ffi', '_FfiCall', 'isLeaf') {
nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
coreTypes, Nullability.nonNullable);
voidType = nativeTypesClasses[NativeType.kVoid]!
@ -1269,37 +1272,6 @@ 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) {

View file

@ -106,9 +106,14 @@ 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);
}
@ -360,10 +365,12 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
);
final DartType nativeSignature = nativeType.typeArguments[0];
return buildAsFunctionInternal(
return _replaceAsFunction(
functionPointer: node.arguments.positional[0],
pointerType: InterfaceType(
pointerClass, Nullability.nonNullable, [nativeType]),
nativeSignature: nativeSignature,
dartSignature: dartType,
dartSignature: dartType as FunctionType,
isLeaf: isLeaf,
fileOffset: node.fileOffset,
);
@ -439,6 +446,84 @@ 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
@ -452,10 +537,6 @@ 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];
@ -468,21 +549,19 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
final FunctionType lookupFunctionType =
libraryLookupMethod.getterType as FunctionType;
final Expression lookupResult = InstanceInvocation(
InstanceAccessKind.Instance,
node.arguments.positional[0],
libraryLookupMethod.name,
lookupArgs,
final lookupResult = InstanceInvocation(InstanceAccessKind.Instance,
node.arguments.positional[0], libraryLookupMethod.name, lookupArgs,
interfaceTarget: libraryLookupMethod,
functionType: FunctionTypeInstantiator.instantiate(
lookupFunctionType, lookupTypeArgs));
final isLeaf = getIsLeafBoolean(node) ?? false;
return buildAsFunctionInternal(
return _replaceAsFunction(
functionPointer: lookupResult,
pointerType: lookupResult.functionType.returnType,
nativeSignature: nativeSignature,
dartSignature: dartSignature,
dartSignature: dartSignature as FunctionType,
isLeaf: isLeaf,
fileOffset: node.fileOffset,
);

View file

@ -0,0 +1,37 @@
// 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();
}

View file

@ -0,0 +1,63 @@
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}
}

View file

@ -0,0 +1,63 @@
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}
}

View file

@ -67,7 +67,13 @@ 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)));
} =>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);
} => 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;
final self::Struct1 struct1 = function1(){() → self::Struct1};
core::print(struct1);
}
@ -75,7 +81,13 @@ 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)));
} =>ffi::_asFunctionInternal<() → self::Struct2, () → self::Struct2>(pointer, false);
} => 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;
final self::Struct2 struct2 = function2(){() → self::Struct2};
core::print(struct2);
}
@ -90,12 +102,26 @@ 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 = [@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);
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;
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 = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::Struct6) → void, (self::Struct6) → ffi::Void>(pointer, false);
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;
core::print(function6);
}
static method returnStruct7() → self::Struct7 {
@ -135,4 +161,14 @@ 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}
}

View file

@ -45,3 +45,11 @@ 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);
```

View file

@ -47,6 +47,7 @@ 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) |

View file

@ -20,7 +20,6 @@
#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"
@ -28,11 +27,6 @@
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());

View file

@ -321,7 +321,6 @@ 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) \

View file

@ -425,7 +425,7 @@ struct CanonicalFfiCallbackFunctionTraits {
f1.FfiCSignature() == f2.FfiCSignature() &&
f1.FfiCallbackExceptionalReturn() ==
f2.FfiCallbackExceptionalReturn() &&
f1.GetFfiFunctionKind() == f2.GetFfiFunctionKind());
f1.GetFfiCallbackKind() == f2.GetFfiCallbackKind());
}
static bool ReportStats() { return false; }
};

View file

@ -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.IsFfiTrampoline()) {
if (target.IsFfiCallbackTrampoline()) {
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.IsFfiTrampoline()) {
if (function.IsFfiCallbackTrampoline()) {
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.IsFfiTrampoline()) {
!function.IsFfiCallbackTrampoline()) {
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.IsFfiTrampoline()) {
if (function.IsFfiCallbackTrampoline()) {
// 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_.IsFfiTrampoline()) {
if (function_.IsFfiCallbackTrampoline()) {
++codes_with_ffi_trampoline_function_;
return;
}
@ -3455,8 +3455,7 @@ void PrecompileParsedFunctionHelper::FinalizeCompilation(
function.AttachCode(code);
}
if (function.IsFfiTrampoline() &&
function.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
if (function.IsFfiCallbackTrampoline()) {
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
}
}

View file

@ -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.IsFfiTrampoline();
!function.is_intrinsic() && !function.IsFfiCallbackTrampoline();
}
GrowableArray<BlockEntryInstr*>* FlowGraph::CodegenBlockOrder(

View file

@ -11,7 +11,6 @@
#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"
@ -837,20 +836,12 @@ void FlowGraphSerializer::WriteTrait<const Function&>::Write(
return;
}
case UntaggedFunction::kFfiTrampoline: {
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiFunctionKind()));
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiCallbackKind()));
s->Write<const FunctionType&>(
FunctionType::Handle(zone, x.FfiCSignature()));
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());
}
s->Write<const Function&>(Function::Handle(zone, x.FfiCallbackTarget()));
s->Write<const Instance&>(
Instance::Handle(zone, x.FfiCallbackExceptionalReturn()));
return;
}
default:
@ -927,23 +918,14 @@ const Function& FlowGraphDeserializer::ReadTrait<const Function&>::Read(
target.GetDynamicInvocationForwarder(name));
}
case UntaggedFunction::kFfiTrampoline: {
const FfiFunctionKind kind =
static_cast<FfiFunctionKind>(d->Read<uint8_t>());
const FfiCallbackKind kind =
static_cast<FfiCallbackKind>(d->Read<uint8_t>());
const FunctionType& c_signature = d->Read<const FunctionType&>();
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));
}
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));
}
default:
UNIMPLEMENTED();

View file

@ -100,8 +100,6 @@ 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",

View file

@ -1,83 +0,0 @@
// 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

View file

@ -1,38 +0,0 @@
// 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_

View file

@ -18,13 +18,13 @@ namespace ffi {
const String& NativeCallbackFunctionName(Thread* thread,
Zone* zone,
const Function& dart_target,
FfiFunctionKind kind) {
FfiCallbackKind kind) {
switch (kind) {
case FfiFunctionKind::kAsyncCallback:
case FfiCallbackKind::kAsyncCallback:
return Symbols::FfiAsyncCallback();
case FfiFunctionKind::kIsolateLocalClosureCallback:
case FfiCallbackKind::kIsolateLocalClosureCallback:
return Symbols::FfiIsolateLocalCallback();
case FfiFunctionKind::kIsolateLocalStaticCallback:
case FfiCallbackKind::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,
FfiFunctionKind kind) {
FfiCallbackKind 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.SetFfiFunctionKind(kind);
function.SetFfiCallbackKind(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

View file

@ -23,7 +23,7 @@ namespace ffi {
FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
const Function& dart_target,
const Instance& exceptional_return,
FfiFunctionKind kind);
FfiCallbackKind kind);
// Builds a mapping from `callback-id` to code object / ...
//

View file

@ -7,7 +7,6 @@
#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"
@ -1025,65 +1024,6 @@ 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();

View file

@ -405,12 +405,6 @@ 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);

View file

@ -3348,18 +3348,18 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(TokenPosition* p) {
return BuildNativeEffect();
case MethodRecognizer::kReachabilityFence:
return BuildReachabilityFence();
case MethodRecognizer::kFfiAsFunctionInternal:
return BuildFfiAsFunctionInternal();
case MethodRecognizer::kFfiCall:
return BuildFfiCall();
case MethodRecognizer::kFfiNativeCallbackFunction:
return BuildFfiNativeCallbackFunction(
FfiFunctionKind::kIsolateLocalStaticCallback);
FfiCallbackKind::kIsolateLocalStaticCallback);
case MethodRecognizer::kFfiNativeAddressOf:
return BuildFfiNativeAddressOf();
case MethodRecognizer::kFfiNativeIsolateLocalCallbackFunction:
return BuildFfiNativeCallbackFunction(
FfiFunctionKind::kIsolateLocalClosureCallback);
FfiCallbackKind::kIsolateLocalClosureCallback);
case MethodRecognizer::kFfiNativeAsyncCallbackFunction:
return BuildFfiNativeCallbackFunction(FfiFunctionKind::kAsyncCallback);
return BuildFfiNativeCallbackFunction(FfiCallbackKind::kAsyncCallback);
case MethodRecognizer::kFfiLoadAbiSpecificInt:
return BuildLoadAbiSpecificInt(/*at_index=*/false);
case MethodRecognizer::kFfiLoadAbiSpecificIntAtIndex:
@ -6221,34 +6221,46 @@ Fragment StreamingFlowGraphBuilder::BuildStoreAbiSpecificInt(bool at_index) {
return code;
}
Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
Fragment StreamingFlowGraphBuilder::BuildFfiCall() {
const intptr_t argc = ReadUInt(); // Read argument count.
ASSERT(argc == 2); // Pointer, isLeaf.
ASSERT(argc == 1); // Target pointer.
const intptr_t list_length = ReadListLength(); // Read types list length.
ASSERT(list_length == 2); // Dart signature, then native signature
// Read types.
const TypeArguments& type_arguments = T.BuildTypeArguments(list_length);
Fragment code;
T.BuildTypeArguments(list_length); // Read types.
// Read positional argument count.
const intptr_t positional_count = ReadListLength();
ASSERT(positional_count == 2);
code += BuildExpression(); // Build first positional argument (pointer).
ASSERT(positional_count == argc);
// 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();
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);
// Skip (empty) named arguments list.
const intptr_t named_args_len = ReadListLength();
ASSERT(named_args_len == 0);
code += B->BuildFfiAsFunctionInternalCall(type_arguments, is_leaf);
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.
return code;
}
@ -6313,23 +6325,23 @@ Fragment StreamingFlowGraphBuilder::BuildCachableIdempotentCall(
}
Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction(
FfiFunctionKind kind) {
FfiCallbackKind kind) {
// The call-site must look like this (guaranteed by the FE which inserts it):
//
// FfiFunctionKind::kIsolateLocalStaticCallback:
// FfiCallbackKind::kIsolateLocalStaticCallback:
// _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn)
//
// FfiFunctionKind::kAsyncCallback:
// FfiCallbackKind::kAsyncCallback:
// _nativeAsyncCallbackFunction<NativeSignatureType>()
//
// FfiFunctionKind::kIsolateLocalClosureCallback:
// FfiCallbackKind::kIsolateLocalClosureCallback:
// _nativeIsolateLocalCallbackFunction<NativeSignatureType>(
// exceptionalReturn)
//
// The FE also guarantees that the arguments are constants.
const bool has_target = kind == FfiFunctionKind::kIsolateLocalStaticCallback;
const bool has_exceptional_return = kind != FfiFunctionKind::kAsyncCallback;
const bool has_target = kind == FfiCallbackKind::kIsolateLocalStaticCallback;
const bool has_exceptional_return = kind != FfiCallbackKind::kAsyncCallback;
const intptr_t expected_argc =
static_cast<int>(has_target) + static_cast<int>(has_exceptional_return);

View file

@ -387,13 +387,12 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
Fragment BuildLoadAbiSpecificInt(bool at_index);
Fragment BuildStoreAbiSpecificInt(bool at_index);
// Build FG for '_asFunctionInternal'. Reads an Arguments from the
// Kernel buffer and pushes the resulting closure.
Fragment BuildFfiAsFunctionInternal();
// Build FG for FFI call.
Fragment BuildFfiCall();
// Build FG for '_nativeCallbackFunction'. Reads an Arguments from the
// Kernel buffer and pushes the resulting Function object.
Fragment BuildFfiNativeCallbackFunction(FfiFunctionKind kind);
Fragment BuildFfiNativeCallbackFunction(FfiCallbackKind kind);
Fragment BuildFfiNativeAddressOf();

View file

@ -400,11 +400,12 @@ Fragment FlowGraphBuilder::InstanceCall(
}
Fragment FlowGraphBuilder::FfiCall(
const compiler::ffi::CallMarshaller& marshaller) {
const compiler::ffi::CallMarshaller& marshaller,
bool is_leaf) {
Fragment body;
FfiCallInstr* const call = new (Z) FfiCallInstr(
GetNextDeoptId(), marshaller, parsed_function_->function().FfiIsLeaf());
FfiCallInstr* const call =
new (Z) FfiCallInstr(GetNextDeoptId(), marshaller, is_leaf);
for (intptr_t i = call->InputCount() - 1; i >= 0; --i) {
call->SetInputAt(i, Pop());
@ -5030,14 +5031,12 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToNative(
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
const Function& function) {
switch (function.GetFfiFunctionKind()) {
case FfiFunctionKind::kIsolateLocalStaticCallback:
case FfiFunctionKind::kIsolateLocalClosureCallback:
switch (function.GetFfiCallbackKind()) {
case FfiCallbackKind::kIsolateLocalStaticCallback:
case FfiCallbackKind::kIsolateLocalClosureCallback:
return BuildGraphOfSyncFfiCallback(function);
case FfiFunctionKind::kAsyncCallback:
case FfiCallbackKind::kAsyncCallback:
return BuildGraphOfAsyncFfiCallback(function);
case FfiFunctionKind::kCall:
return BuildGraphOfFfiCall(function);
}
UNREACHABLE();
return nullptr;
@ -5128,26 +5127,6 @@ 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));
@ -5157,18 +5136,16 @@ Fragment FlowGraphBuilder::FfiNativeFunctionBody(const Function& function) {
Fragment body;
body += FfiNativeLookupAddress(function);
body += FfiCallFunctionBody(function, c_signature);
body += FfiCallFunctionBody(function, c_signature,
/*first_argument_parameter_offset=*/0);
return body;
}
Fragment FlowGraphBuilder::FfiCallFunctionBody(
const Function& function,
const FunctionType& c_signature) {
ASSERT(function.is_ffi_native() || function.IsFfiTrampoline());
const bool is_ffi_native = function.is_ffi_native();
const intptr_t kClosureParameterOffset = 0;
const intptr_t first_argument_parameter_offset =
is_ffi_native ? 0 : kClosureParameterOffset + 1;
const FunctionType& c_signature,
intptr_t first_argument_parameter_offset) {
ASSERT(function.is_ffi_native() || function.IsFfiCallClosure());
LocalVariable* address = MakeTemporary("address");
@ -5258,7 +5235,7 @@ Fragment FlowGraphBuilder::FfiCallFunctionBody(
body += LoadLocal(return_compound_typed_data);
}
body += FfiCall(marshaller);
body += FfiCall(marshaller, function.FfiIsLeaf());
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
if (marshaller.IsPointer(i)) {
@ -5321,31 +5298,6 @@ 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) {
@ -5381,8 +5333,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfSyncFfiCallback(
RELEASE_ASSERT(error == nullptr);
RELEASE_ASSERT(marshaller_ptr != nullptr);
const auto& marshaller = *marshaller_ptr;
const bool is_closure = function.GetFfiFunctionKind() ==
FfiFunctionKind::kIsolateLocalClosureCallback;
const bool is_closure = function.GetFfiCallbackKind() ==
FfiCallbackKind::kIsolateLocalClosureCallback;
graph_entry_ =
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);

View file

@ -137,8 +137,6 @@ 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.
@ -150,7 +148,8 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
Fragment FfiNativeLookupAddress(const Function& function);
// Expects target address on stack.
Fragment FfiCallFunctionBody(const Function& function,
const FunctionType& c_signature);
const FunctionType& c_signature,
intptr_t first_argument_parameter_offset);
Fragment FfiNativeFunctionBody(const Function& function);
Fragment NativeFunctionBody(const Function& function,
LocalVariable* first_parameter);
@ -204,7 +203,8 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
bool receiver_is_not_smi = false,
bool is_call_on_this = false);
Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller);
Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller,
bool is_leaf);
Fragment CCall(
const compiler::ffi::NativeCallingConvention& native_calling_convention);

View file

@ -154,8 +154,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
FunctionNodeHelper::kPositionalParameters);
// NOTE: FunctionNode is read further below the if.
intptr_t pos = 0;
if (function.is_ffi_native()) {
if (function.is_ffi_native() || function.IsFfiCallClosure()) {
needs_expr_temp_ = true;
// Calls with handles need try/catch variables.
if (function.FfiCSignatureContainsHandles()) {
@ -167,7 +166,9 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
FinalizeCatchVariables();
--depth_.catch_;
}
} else if (function.IsClosureFunction()) {
}
intptr_t pos = 0;
if (function.IsClosureFunction()) {
LocalVariable* closure_parameter = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ClosureParameter(), AbstractType::dynamic_type());
@ -406,17 +407,14 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
}
case UntaggedFunction::kFfiTrampoline: {
needs_expr_temp_ = true;
// 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_;
}
// Callbacks need try/catch variables.
++depth_.try_;
AddTryVariables();
--depth_.try_;
++depth_.catch_;
AddCatchVariables();
FinalizeCatchVariables();
--depth_.catch_;
FALL_THROUGH;
}
case UntaggedFunction::kInvokeFieldDispatcher: {
@ -437,7 +435,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
LocalVariable* variable = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
String::ZoneHandle(Z, function.ParameterNameAt(i)),
AbstractType::ZoneHandle(Z, function.IsFfiTrampoline()
AbstractType::ZoneHandle(Z, function.IsFfiCallbackTrampoline()
? function.ParameterTypeAt(i)
: Object::dynamic_type().ptr()));
bool added = scope_->InsertParameterAt(i, variable);

View file

@ -476,8 +476,7 @@ CodePtr CompileParsedFunctionHelper::FinalizeCompilation(
}
}
if (function.IsFfiTrampoline() &&
function.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
if (function.IsFfiCallbackTrampoline()) {
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
}

View file

@ -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(::, _asFunctionInternal, FfiAsFunctionInternal, 0x630c8491) \
V(::, _ffiCall, FfiCall, 0x6118e962) \
V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3fe722bc) \
V(::, _nativeAsyncCallbackFunction, FfiNativeAsyncCallbackFunction, \
0xbec4b7b9) \

View file

@ -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.GetFfiFunctionKind() ==
FfiFunctionKind::kIsolateLocalStaticCallback);
ASSERT(function.GetFfiCallbackKind() ==
FfiCallbackKind::kIsolateLocalStaticCallback);
return CreateSyncFfiCallbackImpl(isolate, zone, function, nullptr,
list_head);
} else {
ASSERT(function.GetFfiFunctionKind() ==
FfiFunctionKind::kIsolateLocalClosureCallback);
ASSERT(function.GetFfiCallbackKind() ==
FfiCallbackKind::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.GetFfiFunctionKind() == FfiFunctionKind::kAsyncCallback);
ASSERT(send_function.GetFfiCallbackKind() == FfiCallbackKind::kAsyncCallback);
return CreateMetadataEntry(isolate, TrampolineType::kAsync,
GetEntryPoint(zone, send_function),
static_cast<uint64_t>(send_port), list_head);

View file

@ -22,7 +22,7 @@
namespace dart {
FunctionPtr CreateTestFunction(FfiFunctionKind kind) {
FunctionPtr CreateTestFunction(FfiCallbackKind 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(FfiFunctionKind::kIsolateLocalStaticCallback));
CreateTestFunction(FfiCallbackKind::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(FfiFunctionKind::kAsyncCallback));
Function::Handle(CreateTestFunction(FfiCallbackKind::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(FfiFunctionKind::kIsolateLocalClosureCallback));
CreateTestFunction(FfiCallbackKind::kIsolateLocalClosureCallback));
const Code& code = Code::Handle(func.EnsureHasCode());
EXPECT(!code.IsNull());
// Using a FfiFunctionKind::kSync function as a dummy closure.
// Using a FfiCallbackKind::kSync function as a dummy closure.
const Function& closure_func = Function::Handle(
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
CreateTestFunction(FfiCallbackKind::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(FfiFunctionKind::kAsyncCallback));
Function::Handle(CreateTestFunction(FfiCallbackKind::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(FfiFunctionKind::kIsolateLocalStaticCallback));
CreateTestFunction(FfiCallbackKind::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(FfiFunctionKind::kAsyncCallback));
Function::Handle(CreateTestFunction(FfiCallbackKind::kAsyncCallback));
const Code& async_code = Code::Handle(async_func.EnsureHasCode());
EXPECT(!async_code.IsNull());
const Function& sync_func = Function::Handle(
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
EXPECT(!sync_code.IsNull());

View file

@ -8337,7 +8337,8 @@ FunctionPtr Function::GetOutermostFunction() const {
FunctionPtr Function::implicit_closure_function() const {
if (IsClosureFunction() || IsDispatcherOrImplicitAccessor() ||
IsFieldInitializer() || IsFfiTrampoline() || IsMethodExtractor()) {
IsFieldInitializer() || IsFfiCallbackTrampoline() ||
IsMethodExtractor()) {
return Function::null();
}
const Object& obj = Object::Handle(data());
@ -8372,7 +8373,7 @@ void Function::set_implicit_closure_function(const Function& value) const {
}
void Function::SetFfiCSignature(const FunctionType& sig) const {
ASSERT(IsFfiTrampoline());
ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
FfiTrampolineData::Cast(obj).set_c_signature(sig);
@ -8380,15 +8381,21 @@ void Function::SetFfiCSignature(const FunctionType& sig) const {
FunctionTypePtr Function::FfiCSignature() const {
auto* const zone = Thread::Current()->zone();
if (IsFfiTrampoline()) {
if (IsFfiCallbackTrampoline()) {
const Object& obj = Object::Handle(zone, data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).c_signature();
}
ASSERT(is_ffi_native());
auto const& native_instance = Instance::Handle(GetNativeAnnotation());
auto& pragma_value = Instance::Handle(zone);
if (is_ffi_native()) {
pragma_value = GetNativeAnnotation();
} else if (IsFfiCallClosure()) {
pragma_value = GetFfiCallClosurePragmaValue();
} else {
UNREACHABLE();
}
const auto& type_args =
TypeArguments::Handle(zone, native_instance.GetTypeArguments());
TypeArguments::Handle(zone, pragma_value.GetTypeArguments());
ASSERT(type_args.Length() == 1);
const auto& native_type =
FunctionType::Cast(AbstractType::ZoneHandle(zone, type_args.TypeAt(0)));
@ -8415,7 +8422,7 @@ bool FunctionType::ContainsHandles() const {
// Keep consistent with BaseMarshaller::IsCompound.
bool Function::FfiCSignatureReturnsStruct() const {
ASSERT(IsFfiTrampoline());
ASSERT(IsFfiCallbackTrampoline());
Zone* zone = Thread::Current()->zone();
const auto& c_signature = FunctionType::Handle(zone, FfiCSignature());
const auto& type = AbstractType::Handle(zone, c_signature.result_type());
@ -8441,8 +8448,7 @@ bool Function::FfiCSignatureReturnsStruct() const {
}
int32_t Function::FfiCallbackId() const {
ASSERT(IsFfiTrampoline());
ASSERT(GetFfiFunctionKind() != FfiFunctionKind::kCall);
ASSERT(IsFfiCallbackTrampoline());
const auto& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
@ -8454,8 +8460,7 @@ int32_t Function::FfiCallbackId() const {
}
void Function::AssignFfiCallbackId(int32_t callback_id) const {
ASSERT(IsFfiTrampoline());
ASSERT(GetFfiFunctionKind() != FfiFunctionKind::kCall);
ASSERT(IsFfiCallbackTrampoline());
const auto& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
@ -8466,69 +8471,64 @@ void Function::AssignFfiCallbackId(int32_t callback_id) const {
}
bool Function::FfiIsLeaf() const {
if (IsFfiTrampoline()) {
const Object& obj = Object::Handle(untag()->data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).is_leaf();
}
ASSERT(is_ffi_native());
Zone* zone = Thread::Current()->zone();
auto const& native_instance = Instance::Handle(GetNativeAnnotation());
const auto& native_class = Class::Handle(zone, native_instance.clazz());
const auto& native_class_fields = Array::Handle(zone, native_class.fields());
ASSERT(native_class_fields.Length() == 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)))
auto& pragma_value = Instance::Handle(zone);
if (is_ffi_native()) {
pragma_value = GetNativeAnnotation();
} else if (IsFfiCallClosure()) {
pragma_value = GetFfiCallClosurePragmaValue();
} else {
UNREACHABLE();
}
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)))
.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(IsFfiTrampoline());
ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).callback_target();
}
void Function::SetFfiCallbackTarget(const Function& target) const {
ASSERT(IsFfiTrampoline());
ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
FfiTrampolineData::Cast(obj).set_callback_target(target);
}
InstancePtr Function::FfiCallbackExceptionalReturn() const {
ASSERT(IsFfiTrampoline());
ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).callback_exceptional_return();
}
void Function::SetFfiCallbackExceptionalReturn(const Instance& value) const {
ASSERT(IsFfiTrampoline());
ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
FfiTrampolineData::Cast(obj).set_callback_exceptional_return(value);
}
FfiFunctionKind Function::GetFfiFunctionKind() const {
ASSERT(IsFfiTrampoline());
FfiCallbackKind Function::GetFfiCallbackKind() const {
ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).ffi_function_kind();
}
void Function::SetFfiFunctionKind(FfiFunctionKind value) const {
ASSERT(IsFfiTrampoline());
void Function::SetFfiCallbackKind(FfiCallbackKind value) const {
ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
FfiTrampolineData::Cast(obj).set_ffi_function_kind(value);
@ -9133,7 +9133,8 @@ static bool InVmTests(const Function& function) {
}
bool Function::ForceOptimize() const {
if (RecognizedKindForceOptimize() || IsFfiTrampoline() || is_ffi_native() ||
if (RecognizedKindForceOptimize() || IsFfiCallClosure() ||
IsFfiCallbackTrampoline() || is_ffi_native() ||
IsTypedDataViewFactory() || IsUnmodifiableTypedDataViewFactory()) {
return true;
}
@ -9174,6 +9175,25 @@ 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.
@ -9248,7 +9268,7 @@ bool Function::RecognizedKindForceOptimize() const {
#if !defined(DART_PRECOMPILED_RUNTIME)
bool Function::CanBeInlined() const {
if (ForceOptimize()) {
if (IsFfiTrampoline() || is_ffi_native()) {
if (IsFfiCallClosure() || IsFfiCallbackTrampoline() || 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.
@ -11000,7 +11020,7 @@ intptr_t Function::KernelLibraryOffset() const {
intptr_t Function::KernelLibraryIndex() const {
if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher() ||
IsFfiTrampoline()) {
IsFfiCallbackTrampoline()) {
return -1;
}
if (is_eval_function()) {
@ -11763,16 +11783,12 @@ 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(FfiFunctionKind kind) const {
void FfiTrampolineData::set_ffi_function_kind(FfiCallbackKind kind) const {
StoreNonPointer(&untag()->ffi_function_kind_, static_cast<uint8_t>(kind));
}

View file

@ -2949,8 +2949,7 @@ struct NameFormattingParams {
}
};
enum class FfiFunctionKind : uint8_t {
kCall,
enum class FfiCallbackKind : uint8_t {
kIsolateLocalStaticCallback,
kIsolateLocalClosureCallback,
kAsyncCallback,
@ -2986,37 +2985,31 @@ 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 trampolines.
// Can only be called on FFI natives and FFI call closures.
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.
FfiFunctionKind GetFfiFunctionKind() const;
FfiCallbackKind GetFfiCallbackKind() const;
// Can only be called on FFI trampolines.
void SetFfiFunctionKind(FfiFunctionKind value) const;
void SetFfiCallbackKind(FfiCallbackKind value) const;
// Return the signature of this function.
PRECOMPILER_WSR_FIELD_DECLARATION(FunctionType, signature);
@ -3902,15 +3895,22 @@ class Function : public Object {
}
// Returns true if this function represents an ffi trampoline.
bool IsFfiTrampoline() const {
bool IsFfiCallbackTrampoline() const {
return kind() == UntaggedFunction::kFfiTrampoline;
}
static bool IsFfiTrampoline(FunctionPtr function) {
static bool IsFfiCallbackTrampoline(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,17 +4347,14 @@ class FfiTrampolineData : public Object {
}
void set_callback_exceptional_return(const Instance& value) const;
FfiFunctionKind ffi_function_kind() const {
return static_cast<FfiFunctionKind>(untag()->ffi_function_kind_);
FfiCallbackKind ffi_function_kind() const {
return static_cast<FfiCallbackKind>(untag()->ffi_function_kind_);
}
void set_ffi_function_kind(FfiFunctionKind kind) const;
void set_ffi_function_kind(FfiCallbackKind 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);

View file

@ -1501,10 +1501,7 @@ class UntaggedFfiTrampolineData : public UntaggedObject {
// Check 'callback_target_' to determine if this is a callback or not.
int32_t callback_id_;
// 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.
// The kind of trampoline this is. See FfiCallbackKind.
uint8_t ffi_function_kind_;
};

View file

@ -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.IsFfiTrampoline());
!function.IsFfiCallbackTrampoline());
// 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

View file

@ -501,6 +501,7 @@ 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") \
@ -529,6 +530,7 @@ class ObjectPointerVisitor;
V(vm_exact_result_type, "vm:exact-result-type") \
V(vm_external_name, "vm:external-name") \
V(vm_ffi_abi_specific_mapping, "vm:ffi:abi-specific-mapping") \
V(vm_ffi_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") \

View file

@ -82,13 +82,21 @@ int sizeOf<T extends NativeType>() {
@pragma("vm:idempotent")
external Pointer<T> _fromAddress<T extends NativeType>(int ptr);
// The real implementation of this function (for interface calls) lives in
// BuildFfiAsFunctionInternal in the Kernel frontend. No calls can actually
// reach this function.
/// 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.
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_asFunctionInternal")
external DS _asFunctionInternal<DS extends Function, NS extends Function>(
Pointer<NativeFunction<NS>> ptr, bool isLeaf);
external ReturnType _ffiCall<ReturnType>(Pointer<NativeFunction> target);
@pragma("vm:recognized", "other")
@pragma("vm:idempotent")

View file

@ -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<V>.
/// Cast Pointer<T> to a Pointer<U>.
external Pointer<U> cast<U extends NativeType>();
/// Equality for Pointers only depends on their address.
@ -1556,6 +1556,10 @@ 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

View file

@ -119,21 +119,27 @@ void testAsFunction() {
Expect.throws(() {
nullptr
.cast<NativeFunction<Int32 Function(Incomplete)>>()
.asFunction<int Function(int)>();
.asFunction<int Function(int)>()
.call(42);
});
Expect.throws(() {
nullptr
.cast<NativeFunction<Incomplete Function(Int32)>>()
.asFunction<int Function(int)>();
.asFunction<int Function(int)>()
.call(42);
});
final p = calloc<Int64>(100).cast<IncompleteArrayStruct>();
Expect.throws(() {
nullptr
.cast<NativeFunction<Int32 Function(IncompleteArrayStruct)>>()
.asFunction<int Function(IncompleteArrayStruct)>();
.asFunction<int Function(IncompleteArrayStruct)>()
.call(p.ref);
});
calloc.free(p);
Expect.throws(() {
nullptr
.cast<NativeFunction<IncompleteArrayStruct Function()>>()
.asFunction<IncompleteArrayStruct Function()>();
.asFunction<IncompleteArrayStruct Function()>()
.call();
});
}

View file

@ -459,6 +459,12 @@ 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");
@ -467,7 +473,7 @@ void testNativeFunctionNullableInt() {
sumPlus42(3, null);
} catch (e) {
// TODO(http://dartbug.com/47098): Save param names to dwarf.
Expect.isTrue(e.toString().contains('ffi_param2') ||
Expect.isTrue(e.toString().contains(arg2ObfuscatedName) ||
e.toString().contains('<optimized out>'));
}
}