本文是阅读dart ffi相关的代码记录。
分成几个部分,dart的代码在sdk/lib/ffi下,有4个文件
Pointer相关的实际实现的代码,
在runtime/lib/ffi_path.dart, 定义的对应的native实现,关键的如
runtime/lib/ffi_dynamic_library_patch.dart定义DynamicLibrary的实现
对应的c++类
runtime/lib/ffi_dynamic_library.cc 实现open, loopkup等函数
runtime/lib/ffi.cc 实现fromFunction, asFunction等函数
dart的用法
import 'dart:ffi' as ffi;
import 'dart:io' show Platform;
// FFI signature of the hello_world C function
typedef hello_world_func = ffi.Void Function();
// Dart type definition for calling the C foreign function
typedef HelloWorld = void Function();
main() {
// Open the dynamic library
var path = "./hello_library/libhello.so";
if (Platform.isMacOS) path = './hello_library/libhello.dylib';
if (Platform.isWindows) path = r'hello_library\Debug\hello.dll';
final dylib = ffi.DynamicLibrary.open(path);
// Look up the C function 'hello_world'
final HelloWorld hello = dylib
.lookup>('hello_world')
.asFunction();
// Call the function
hello();
DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
// 取得type_args, 指 lookup的模板参数0:上面的例子ffi.NativeFunction>
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
// 对象指针,即this指针:DynamicLibrary实现
GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
//函数名
GET_NON_NULL_NATIVE_ARGUMENT(String, argSymbolName,
arguments->NativeArgAt(1));
void* handle = dlib.GetHandle();
// 调用dlsysm获得函数值
const intptr_t pointer = reinterpret_cast(
ResolveSymbol(handle, argSymbolName.ToCString()));
// TODO(dacoharkes): should this return Object::null() if address is 0?
// https://github.com/dart-lang/sdk/issues/35756
// 创建一个native指针,使用int类型保存
// type_arg确定参数类型,pointer保存地址
RawPointer* result =
Pointer::New(type_arg, Integer::Handle(zone, Integer::New(pointer)));
// result对应dart中的ffi.NativeFunction类型
return result;
}
DEFINE_NATIVE_ENTRY(Ffi_asFunction, 1, 1) {
//获得 Pointer对象,如上面的例子ffi.NativeFunction
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
AbstractType& pointer_type_arg = // 获得类型信息
AbstractType::Handle(pointer.type_argument());
ASSERT(IsNativeFunction(pointer_type_arg));
// 获得asFunction的模板参数
// asFunction的声明: external R asFunction<@DartRepresentationOf("T") R extends Function>(); 获取其中的R类型,上面的例子 HelloWorld类型
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
ASSERT(DartAndCTypeCorrespond(pointer_type_arg, type_arg));
// dart_signature 获得R的签名,上面例子 HelloWorld类型
Function& dart_signature = Function::Handle(Type::Cast(type_arg).signature());
// Pointer类的类型参数,即NativeType的类型
TypeArguments& nativefunction_type_args =
TypeArguments::Handle(pointer_type_arg.arguments());
AbstractType& nativefunction_type_arg =
AbstractType::Handle(nativefunction_type_args.TypeAt(0));
Function& c_signature =
Function::Handle(Type::Cast(nativefunction_type_arg).signature());
// 生成内部函数接口,TrampolineFunction创建一个跳板函数对象,参数从dart_signature转换为c_signature
Function& function =
Function::Handle(TrampolineFunction(dart_signature, c_signature));
// Set the c function pointer in the context of the closure rather than in
// the function so that we can reuse the function for each c function with
// the same signature.
// C函数指针包装在context中
Context& context = Context::Handle(Context::New(1));
context.SetAt(0, Integer::Handle(zone, pointer.GetCMemoryAddress()));
// 包装成Function类,在dart中可以直接调用
RawClosure* raw_closure =
Closure::New(Object::null_type_arguments(), Object::null_type_arguments(),
function, context, Heap::kOld);
return raw_closure;
}
指定RawFunction::kFfiTrampoline类型,专门用于ffi调用
// TODO(dacoharkes): Cache the trampolines.
// We can possibly address simultaniously with 'precaching' in AOT.
static RawFunction* TrampolineFunction(const Function& dart_signature,
const Function& c_signature) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
String& name =
String::ZoneHandle(Symbols::New(Thread::Current(), "FfiTrampoline"));
const Library& lib = Library::Handle(Library::FfiLibrary());
const Class& owner_class = Class::Handle(lib.toplevel_class());
Function& function =
Function::Handle(zone, Function::New(name, RawFunction::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);
function.set_num_fixed_parameters(dart_signature.num_fixed_parameters());
function.set_result_type(AbstractType::Handle(dart_signature.result_type()));
function.set_parameter_types(Array::Handle(dart_signature.parameter_types()));
// The signature function won't have any names for the parameters. We need to
// assign unique names for scope building and error messages.
const intptr_t num_params = dart_signature.num_fixed_parameters();
const Array& parameter_names = Array::Handle(Array::New(num_params));
for (intptr_t i = 0; i < num_params; ++i) {
if (i == 0) {
name = Symbols::ClosureParameter().raw();
} else {
name = Symbols::NewFormatted(thread, ":ffiParam%" Pd, i);
}
parameter_names.SetAt(i, name);
}
function.set_parameter_names(parameter_names);
function.SetFfiCSignature(c_signature);
return function.raw();
}