Dart FFI的簡單分析

本文是閱讀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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章