Dart Sdk Tips

杨成礼
2023-12-01

Dart Sdk Tips

包含了dart相关的基础command tools和dart相关的基础库

Overview

...
├── pkg     //dart工具包
├── runtime //dart运行时相
├── sdk     //dart sdk,基础工具包以及各平台的实现 
├── tools
└── utils
├── _http ...    //dart http请求库,基于dart其它基础库编写(io/isolate/match等)
├── _internal ... //不同平台(移动端和desktop中vm,浏览器的js)的对dart抽象接口的实现
│   ├── js_dev_runtime ...
│   ├── js_runtime ...
│   ├── vm    ..    // 
├── async ...       // 异步函数库,基于isolate
├── collection ...  // 
├── convert ...     //
├── core  ...       //
├── dev_compiler ...//
├── ffi ...         // 外部函接口,通过dart调用调用`C/C++`库,通过`external`关键字将dart的函数,类型定义和c/c++的实现关联起来,并通过函数指针查找和调用c库
├── internal  ...   // 对dart:core中基本类型封装和扩展
├── io ...          // 定义了input/ios相关接口,对native网络访问,文件存储,stream操作语法糖  
├── isolate ...      // dart隔离的接口定义,由dartvm创建 
├── math  ...        // 提供对系统c库的基础match函数的调用

Dart Platforms

Native: 对于针对移动和桌面设备的应用程序,Dart包括一个具有即时(JIT)编译的Dart VM和一个用于生成机器代码的提前(AOT)编译器.
Web: 对于面向web的应用程序,Dart包括开发时编译器(dartdevc)和生产时编译器(dart2js)。两个编译器都将Dart转换为JavaScript

Dart 常用命令

  analyze   Analyze Dart code in a directory.
  compile   Compile Dart to various formats.
  create    Create a new Dart project.
  fix       Apply automated fixes to Dart source code.
  format    Idiomatically format Dart source code.
  migrate   Perform null safety migration on a project.
  pub       Work with packages.
  run       Run a Dart program.
  test      Run tests for a project.

analyze

dart analyze --fatal-infos . 分析当前文件下是否有致命的错误
dart analyze --fatal-warnings . 分析当前文件下是否有警告, 一般使用这个

compile

  aot-snapshot   编译成Aot快照,`ahead of time`,占用内存低,启动快,无需要rumtime运行
  exe            编译成可执行文件
  jit-snapshot   编译成`JIT(just in time)`,可以增量运行
  js             编译成js代码
  kernel         编译成易于携带的内核快照

下面是采用不同方式编译执行test.dart

├── out.js 
├── out.js.deps
├── out.js.map
├── test.aot    //dartaotruntime test.aot
├── test.dart   //dart test.dart compile + execute
├── test.dill   //dart run test.dill
├── test.exe    //test.exe
└── test.jit    //dart run test.jit

pub

pub常用于运行远程库,该库必须有是可以执行的main函数入口

executables:
  devtools:

上面是devtools-2.5.0中制定的入口,devtools为main函数所在文件

dart pub global [activate|deactivate|list|run]

dart pub global active devtools 将package编译成exe可执行文件,并放在$HOME/.pub-cache/bin目录下
dart pub global run devtools 执行package的main函数

├── _temp
├── bin              //编译后的可以执行文件
├──── dcdg
└──── devtools       
├── git     
├── global_packages  //package快信息
└── hosted           //package源代码
/Users/jiodg45/.pub-cache/hosted/pub.flutter-io.cn/devtools-2.5.0
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── bin
├──── devtools.dart //main函数入口
├── build
├── devtools.iml
├── ios
├── lib
├── linux
├── macos
└── pubspec.yaml   //需要申明`executables`为main函数所在文件

Dart 关键字-external

dart-sdk中可以看到很多这样的注解和external关键字

下面Objectdart:core中定义

@pragma("vm:entry-point")
class Object {
  @pragma("vm:recognized", "other")
  const Object();
  external bool operator ==(Object other);
  external int get hashCode;
  external String toString();
  @pragma("vm:entry-point")
  external dynamic noSuchMethod(Invocation invocation);
  external Type get runtimeType;
}

external定义该方法需要在外实现,对应的external函数都会有一个@patch的注解与之对应, 如下Objectdart for web以及vm for dart完成了具体的实现

lib/_internal/js_runtime/lib/core_patch.dart:
  52  @patch
  53: class Object {
  54    @patch

lib/_internal/vm/lib/object_patch.dart:
  14  @pragma("vm:entry-point")
  15: class Object {
  16    // The VM has its own implementation of equals.

lib/core/object.dart:
  17  @pragma("vm:entry-point")
  18: class Object {
  19    /// Creates a new [Object] instance.

Dart关键字-native

  1. dart:ffi 中的应用,外部函数接口,通过绑定动态绑定native库,绕开methodchannel的调用,提升数据的传输效率

DynamicLibrary.open, dart:ffi为接口抽象层,_internal/vm/lib/ffi则是对应平台的具体实现

lib/_internal/vm/lib/ffi_dynamic_library_patch.dart:
  10  
  11: DynamicLibrary _open(String path) native "Ffi_dl_open";
  12  DynamicLibrary _processLibrary() native "Ffi_dl_processLibrary";

lib/_internal/vm/lib/ffi_dynamic_library_patch.dart:
  18    @patch
  19:   factory DynamicLibrary.open(String path) {
  20      return _open(path);

lib/ffi/dynamic_library.dart:
  32    /// which are equal (`==`), but not [identical].
  33:   external factory DynamicLibrary.open(String path);
  34  

ffi_dynamic_library_patch中使用了native关键字Ffi_dl_open,该函数最终会和native的函数指针绑定. 在 https://github.com/dart-lang/sdk/blob/main/runtime/lib/ffi_dynamic_library.cc实现Native定义的方法

DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(String, lib_path, arguments->NativeArgAt(0));

  void* handle = LoadDynamicLibrary(lib_path.ToCString());

  return DynamicLibrary::New(handle);
}

DEFINE_NATIVE_ENTRY(Ffi_dl_processLibrary, 0, 0) {
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) ||              \
    defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
  return DynamicLibrary::New(RTLD_DEFAULT);
#else
  const Array& args = Array::Handle(Array::New(1));
  args.SetAt(0,
             String::Handle(String::New(
                 "DynamicLibrary.process is not available on this platform.")));
  Exceptions::ThrowByType(Exceptions::kUnsupported, args);
#endif
}


third_party/tonic/dart_library_natives.cc:
  14  
  15: void DartLibraryNatives::Register(std::initializer_list<Entry> entries) {
  16    for (const Entry& entry : entries) 

third_party/tonic/dart_library_natives.h:
class DartLibraryNatives {
 public:
  DartLibraryNatives();
  ~DartLibraryNatives();

  struct Entry {
    const char* symbol;
    Dart_NativeFunction native_function;
    int argument_count;
    bool auto_setup_scope;
  };
  
  //注册native函数
  void Register(std::initializer_list<Entry> entries); 
  
  //Dart_NativeFunction--> exectue --> Dart_Handle --> dart
  Dart_NativeFunction GetNativeFunction(Dart_Handle name,
                                        int argument_count,
                                        bool* auto_setup_scope);
  const uint8_t* GetSymbol(Dart_NativeFunction native_function);

 private:
  std::unordered_map<std::string, Entry> entries_;
  std::unordered_map<Dart_NativeFunction, const char*> symbols_;

  TONIC_DISALLOW_COPY_AND_ASSIGN(DartLibraryNatives);
};

} 

DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
  GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));

  GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(String, argSymbolName,
                               arguments->NativeArgAt(1));

  void* handle = dlib.GetHandle();

  const uword pointer =
      reinterpret_cast<uword>(ResolveSymbol(handle, argSymbolName.ToCString()));
  return Pointer::New(type_arg, pointer);
}

FFi其实主要是用了2个系统函数
dl_open (dynamic library open),打开动态库并将其装入内存中
dlsym 根据动态链接库操作句柄与符号,返回符号对应的地址,不但可以获取函数地址,也可以获取变量地址,返回符号对应的地址。当调用dart断的lookup函数最终会调用此方法,如下代码就是通过lookup函数去查找动态库的hello_world方法, 具体示例见https://dart.dev/guides/libraries/c-interop

  final HelloWorld hello = dylib
   .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
   .asFunction();

2.dart:ui中也定义了许多的native函数绑定义,它是flutter frameworkengine的桥梁,在创建DartVM的时候会完成绑定

lib/ui/dart_ui.cc
void DartUI::InitForGlobal() {
  if (!g_natives) {
    g_natives = new tonic::DartLibraryNatives();
    Canvas::RegisterNatives(g_natives); //画布
    CanvasGradient::RegisterNatives(g_natives); 
    CanvasImage::RegisterNatives(g_natives);
    CanvasPath::RegisterNatives(g_natives);
    CanvasPathMeasure::RegisterNatives(g_natives);
    Codec::RegisterNatives(g_natives);
    ColorFilter::RegisterNatives(g_natives);  //颜色滤镜
    DartRuntimeHooks::RegisterNatives(g_natives);
    EngineLayer::RegisterNatives(g_natives);  //具柄,用于保留引擎层的图层
    FontCollection::RegisterNatives(g_natives);
    FragmentShader::RegisterNatives(g_natives); //片段着色器
    ImageDescriptor::RegisterNatives(g_natives);
    ImageFilter::RegisterNatives(g_natives); //图片滤镜
    ImageShader::RegisterNatives(g_natives); //图片色器
    ImmutableBuffer::RegisterNatives(g_natives);
    IsolateNameServerNatives::RegisterNatives(g_natives);
    NativeStringAttribute::RegisterNatives(g_natives);
    Paragraph::RegisterNatives(g_natives); //文本布局引擎接口
    ParagraphBuilder::RegisterNatives(g_natives);
    Picture::RegisterNatives(g_natives); //用于后续加入到senece中 或 绘制到canvase中
    PictureRecorder::RegisterNatives(g_natives); //记录一些列图形操作指令,并保存到Picture对象中,通过engine记录绘制指令并返回到flutter端,
    Scene::RegisterNatives(g_natives); //提供界面场景,表示合成场景的不透明对象,它是由sceneBuilder在engine中根据picture组合生成layers合成的,可以通过`FlutterView.render`显示出来
    SceneBuilder::RegisterNatives(g_natives); //根据给定的可视化内容构建场景
    SemanticsUpdate::RegisterNatives(g_natives);
    SemanticsUpdateBuilder::RegisterNatives(g_natives);
    Vertices::RegisterNatives(g_natives);
    PlatformConfiguration::RegisterNatives(g_natives); //用户处理engine和flutter framework之间的通信,拥有主窗口,PlatformConfigurationClient接口运行时控制器定义。
  }
}

void DartRuntimeHooks::Install(bool is_ui_isolate,
                               const std::string& script_uri) {
  Dart_Handle builtin = Dart_LookupLibrary(ToDart("dart:ui"));
  InitDartInternal(builtin, is_ui_isolate);
  InitDartCore(builtin, script_uri);
  InitDartAsync(builtin, is_ui_isolate);
  InitDartIO(builtin, script_uri);
}

dart注解

  1. pragma
    @pragma("vm:entry-point", ...)用于标注对应的class或方法在AOT模式下,是否可以被dart vmnative访问.其作用就是避免TFA(type flow analysis)tree shaking,以及obfuscation,TFA为深度挖掘更多的类型信息,从而决定哪些无用的代码是可以优化的.obfuscation会混淆代码,导致名称变化. 通过vm:entry-point注解保留的class或方法名不变,从而能通过native或vm进行访问.

应用在class上

@pragma("vm:entry-point")
@pragma("vm:entry-point", true/false)
@pragma("vm:entry-point", !const bool.formEnvironment("dart.vm.product"))
class C { ... }

在上面示例中vm:entry-point", truevm:entry-point"会允许vm和native code分配内存
vm:entry-point", false则会保留类不被混淆,没有alloc的存根,不允许vm和native code创建

应用在method上

@pragma("vm:entry-point")
@pragma("vm:entry-point", true/false)
@pragma("vm:entry-point", !const bool.formEnvironment("dart.vm.product"))
@pragma("vm:entry-point", "get")
@pragma("vm:entry-point", "call")
void foo() { ... }

同上面提到的一样,当第二个参数为null或者false不能被dart vmnative执行,可以不被混淆
实用getcall定义的注解可以被Dart_GetFieldDart_Invoke访问.

@pragma("vm:entry-point") 
@pragma("vm:entry-point", null)  //支持 get 和 set访问
@pragma("vm:entry-point", true/false) false不支持native/vm get 和 set访问
@pragma("vm:entry-point", !const bool.formEnvironment("dart.vm.product"))
@pragma("vm:entry-point", "get"/"set") //指定支持get或set,二选一
int foo;

用于追中vm调用信息,通过制定勾子函数,来追踪vm的调用

void hook(String functionName, int entryPointId) {
  // ...
}

class C<T> {
  @pragma('vm:testing.unsafe.trace-entrypoints-fn', hook)
  void foo(T x) {
    // ...
  }
}

指定返回类型,多态的类型适配

class A {}
class B extends A {}

// Reference to type via type literal
@pragma("vm:exact-result-type", B)
A foo() native "foo_impl";

// Reference to type via path
@pragma("vm:exact-result-type", "dart:core#_Smi");
int foo() native "foo_impl";

// 类型的泛型适配
@pragma("vm:exact-result-type",
          [D, "result-type-uses-passed-type-arguments"])
  factory D();  // returns an instance of D<T>
}

Dart Optimization Levels

详见https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/optimization_levels.md

优化等级,
–optimization_level=
0: unoptimized (O0)
1: size optimized (Os)
2: default optimized (O2)
3: speed optimized (O3)

好处
允许对不符合当前O2理念的尺寸或速度进行新的优化
能够从O2中移除现有优化,这些优化仅对尺寸产生了不成比例的负面影响
允许引入更昂贵的分析,可以(但不保证)找到更多优化机会
为与其他用户不同的用户提供更多的控制权,使他们更喜欢大小或速度
可能会提供更多关于优化的见解,这些优化最初被认为是有风险的,但“在现场”有所帮助;也许可以找到更好的启发式方法来将这些迁移到O2
坏处
通过编译器的另外两个代码路径,增加了所有测试矩阵的大小(Dart、颤振、性能、正确性)
错误使用标志以避免花费时间寻找更好的启发式方法的风险

设置代码的优化等级在一定程度上可以提高代码的执行效率,减少程序的体积,但过度的优化可能会导致代码的执的正确性,具体还需要结合项目实际情况

Support

dart-lang/sdk
engine

 类似资料:

相关阅读

相关文章

相关问答