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>('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;
}

asFunction的实现

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;
}

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();
}

你可能感兴趣的:(Dart FFI的简单分析)