本文是閱讀dart ffi相關的代碼記錄。
源碼的位置
分成幾個部分,dart的代碼在sdk/lib/ffi下,有4個文件
- annotations.dart
- dynamic_library.dart : 定義DynamicLibrary類,用於加載so庫
- ffi.dart 定義Pointer類
- native_type.dart 定義NativeType
Pointer相關的實際實現的代碼,
在runtime/lib/ffi_path.dart, 定義的對應的native實現,關鍵的如
- Pointer.asFunction對應 Ffi_asFunction
- fromFunction 對應Ffi_fromFunction
- 等等
runtime/lib/ffi_dynamic_library_patch.dart定義DynamicLibrary的實現
- DynamicLibrary._open對應 Ffi_dl_open
- DynamicLibrary.lookup 對應 Ffi_dl_lookup
- 等等
對應的c++類
runtime/lib/ffi_dynamic_library.cc 實現open, loopkup等函數
runtime/lib/ffi.cc 實現fromFunction, asFunction等函數
lookup取得函數指針
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<ffi.NativeFunction<hello_world_func>>('hello_world')
.asFunction();
// Call the function
hello();
DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
// 取得type_args, 指 lookup的模板參數0:上面的例子ffi.NativeFunction<hello_world_func>>
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<intptr_t>(
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<hello_world_func>類型
return result;
}
asFunction的實現
DEFINE_NATIVE_ENTRY(Ffi_asFunction, 1, 1) {
//獲得 Pointer對象,如上面的例子ffi.NativeFunction<hello_world_func>
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;
}
TrampolineFunction
指定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();
}