0%

Dart的编译和执行原理

Dart简介

官方网站

目标是高效地开发多平台应用,提供灵活的运行时环境和编译工具。

理论上所有高级语言都可跨平台,关键在于语法、平台库好不好用,编译工具成不成熟。下面会详细介绍下Dart的编译和执行

Dart SDK安装

参考Dart SDK安装。Dart SDK中包含了Dart核心库、编译器、命令行工具等。Dart SDK是由Dart源码编译出来的归档文件。

  • Flutter SDK内置了Dart SDK工具(Dart SDK归档文件,包括工具和核心库),位于{flutter_sdk}/bin/cache/dart-sdk中,不需要再单独下载。
  • Flutter Engine中依赖了Dart的源码,位于{flutter_engine}/third_party/dart中,通过ninja构建出Dart SDK可执行程序,供Flutter SDK使用。

Dart语言

支持众多特性:类型安全(静态类型检查、dynamic运行时检查)、空安全、异步调用、流、箭头函数、getter函数等。基本语法参考Dart语言

Dart库

核心库三方库

Dart项目文件

Dart使用pubspec.yaml文件保存项目信息、发布信息、依赖包等。pubspec说明

类似npm的package.json,本地会缓存依赖包,不同项目可以共用本地缓存的依赖包

可以使用dart pub <subcommand>命令管理项目,如add添加依赖,get获取依赖等。可以用dart pub -h查看帮助,也可以看dart pub说明

如果用dart开发Flutter程序,使用flutter pub <subcommand>命令替代,flutter对dart命令进行了一层包装

新建pubspec.yaml文件

1
2
3
4
5
6
7
name: myapp # 项目名称

environment: # dart版本
sdk: ">=2.12.0 <3.0.0"

dependencies: # 依赖包
js: ^0.6.0

执行dart pub get获取依赖,会生成几个文件。不需要提交,加到.gitignore

  1. pubspec.lock:保存项目信息
  2. .packages:已经弃用,替换为package_config.json文件
  3. .dart_tool/package_config.json:将依赖包映射到系统缓存该包的路径

main.dart中可以导入包使用,运行时会从package_config.json中查找依赖包路径

1
import 'package:js/js.dart' as js;

dart compiledart --snapshot可以使用--packages=<path>选项指定.packages文件或者package_config.json文件,用于编译时查找依赖包路径

Dart工具

Dart SDK中提供了一些工具,使用-h查看帮助或者参考Dart命令行工具。源码入口位于{dart_sdk}/pkg/dartdev/

  • dart:用于创建、格式化、分析、测试、编译和运行dart代码
  • dartaotruntime:用于执行aot预编译过机器码
  • dartdoc:用于生成API文档

除了上面三个工具外,还有dart2jsdart2nativedartanalyzerdartdevcdartfmtpub等工具。这些工具从2.10版本开始全部被封装到了dart中,通过dart <subcommand>的方式执行:

  • dart2native, dart2aot, dart2js 工具被 dart compile 替代
  • dartanalyzerdart analyze替代
  • dartfmtdart format替代
  • pubdart pub替代

{flutter_sdk}/bin/dartdart-sdk的工具做了一层包装,执行的时候会调用dart-sdk中的工具。

由于配置Flutter SDK环境变量{flutter_sdk}/bin的时候没有配置dart-sdk的环境变量,如果要使用dartaotruntime工具,需要进入对应目录执行,或者给dart-sdk也配置环境变量

Dart的编译和执行

Dart虚拟机

Dart虚拟机源码位于{dart_sdk}/runtime/vm中,包含以下几个部分:

DartVM作为虚拟机为Dart高级语言提供执行环境,但这并不表示Dart一定运行在虚拟机中。Dart的运行主要有几种方式:

  • 虚拟机执行:通过JIT即时编译+解释器,执行Dart源文件或者Kernel二进制文件,运行在Dart虚拟机中。对应dart run命令
  • 目标代码执行:通过AOT预编译成目标代码,运行在预编译运行时环境(Precompiled Runtime)中。不包含编译器,因此无法动态加载Dart源码。对应dartaotruntime命令
  • 开发阶段:运行在Dart虚拟机中,通过Dart虚拟机提供的即时编译器(JIT)执行,支持增量编译,热重载和调试。
  • 发布阶段:通过Dart的AOT编译器编译成目标平台的代码,在Dart预编译运行时(Precompiled Runtime)中执行,提高启动速度和执行效率。

Dart 2之后,Dart VM不支持直接执行源代码,只接收Kernel AST序列化成的Kernel二进制文件(即.dill文件)。通过Dart的编译前端(CFE,common front-end)编译,并被其他工具所依赖使用,例如Dart VM、dart2js、Dart Dev Compiler。

Dart运行时会被打包到Self-Contained的目标可执行程序中,同时也是Dart虚拟机的一部分,包含以下功能

  • 内存管理:提供对象分配和分代垃圾回收功能。
  • 运行时类型检查和强制转换
  • 管理isolates:包括主isolate和应用自行创建的isolate

虚拟机执行

使用dart run命令启动虚拟机执行程序,如下

  1. 新建main.dart文件

    1
    2
    3
    4
    //main.dart
    void main() {
    print('Hello, World!');
    }
  2. 执行dart main.dart,输出”Hello, World!”

run子命令启动一个Dart虚拟机,执行未编译过的源码或者部分快照类型(JIT、Kernel快照),不支持执行aot快照。

可以省略,例如dart main.dartdart main.dill

Dart编译

compile命令

dart compile命令封装了不同场景下的编译,不需要手动执行编译前端和编译后端。源码位于{dart_sdk}/pkg/dartdev/lib/src/commands/compile.dart,分为以下几种方式:

  1. exe:生成Self-Contained可执行文件,包含生成的目标代码和一个小型的Dart运行时,可以直接运行
    1. 编译:dart compile exe main.dart,生成main.exe文件
    2. 运行:./main.exe,输出”Hello, World!”
  2. aot-snapshot:生成AOT快照文件,包含生成的目标代码,但不包含Dart运行时,需要使用dartaotruntime执行
    1. 编译:dart compile aot-snapshot main.dart,生成main.aot文件
    2. 执行dartaotruntime main.aot,输出”Hello, World!”
  3. jit-snapshot:生成JIT快照文件,包含生成的目标代码,不同的是在训练运行期间已经加载和解析过代码,使用dart run运行。由于在训练运行期间已经解析和编译过,Dart虚拟机不需要再进行解析和编译,因此可以更快的执行代码。(经过训练和优化,有可能比aot执行更快)
    1. 编译:dart compile jit-snapshot main.dart,生成main.jit文件,并且会执行一遍程序训练,输出”Hello, World!”
    2. 运行:dart run main.jit,输出”Hello, World!”
  4. kernel:生成.dill二进制的kernel快照文件,是一种中间代码,和平台无关,具有可移植性。包含二进制格式的Dart抽象语法树(Kernel AST)
    1. 编译:dart compile kernel main.dart,生成main.dill文件
    2. 执行:dart run main.dill,输出”Hello, World!”
  5. js:生成js文件
    1. 编译:dart compile js main.dart,生成out.js文件
    2. 可以使用webdev serve命令启动开发服务器运行js

exeaot-snapshot存在一些限制:

  1. 不支持交叉编译、只能本地编译本地运行:需要在macOS、Windows、Linux主机上分别编译出三个目标程序
  2. 生成的可执行程序不支持签名
  3. 不支持dart:mirrors(用于动态反射)和dart:developer(用于调试检查)库,参考Dart核心库说明

对比下编译产物文件,如下:exe > jit-snapshot > aot-snapshot > kernel > dart source code,一般情况下执行效率刚好相反。

查看dart compile源码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//{dart_sdk}/pkg/dartdev/lib/src/commands/compile.dart
//dart compile命令
class CompileCommand extends DartdevCommand {
static const String cmdName = 'compile';
CompileCommand({bool verbose = false})
: super(cmdName, 'Compile Dart to various formats.', verbose) {
addSubcommand(CompileJSCommand(verbose: verbose));
addSubcommand(CompileSnapshotCommand( //dart compile jit-snapshot子命令
commandName: CompileSnapshotCommand.jitSnapshotCmdName,
help: 'to a JIT snapshot.\n'
'To run the snapshot use: dart run <JIT file>',
fileExt: 'jit',
formatName: 'app-jit',
verbose: verbose,
));
addSubcommand(CompileSnapshotCommand( //dart compile kernel子命令
commandName: CompileSnapshotCommand.kernelCmdName,
help: 'to a kernel snapshot.\n'
'To run the snapshot use: dart run <kernel file>',
fileExt: 'dill',
formatName: 'kernel',
verbose: verbose,
));
addSubcommand(CompileNativeCommand(
commandName: CompileNativeCommand.exeCmdName,
help: 'to a self-contained executable.',
format: 'exe',
verbose: verbose,
));
addSubcommand(CompileNativeCommand(
commandName: CompileNativeCommand.aotSnapshotCmdName,
help: 'to an AOT snapshot.\n'
'To run the snapshot use: dartaotruntime <AOT snapshot file>',
format: 'aot',
verbose: verbose,
));
}
}

dart –snapshot

在Flutter SDK中经常看到.snapshot后缀的文件,如flutter_tools.snapshot,查看Flutter命令脚本的源码中使用了dart --snapshot命令,官网没有说明。使用dart --snapshot查看帮助如下:

--snapshot用于生成快照文件,--snapshot-kind指定生成JIT快照还是kernel快照。默认生成kernel快照。

dart --snapshot源码入口位于{dart-sdk}/runtime/bin/main.cc

CompileSnapshotCommand

查看CompileSnapshotCommand代码,对应dart compile kerneldart compile jit-snapshot命令。实际就是调用dart --snapshot-kind=$formatName执行。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//{dart_sdk}/pkg/dartdev/lib/src/commands/compile.dart
//jit和kernel编译
class CompileSnapshotCommand extends CompileSubcommandCommand {
static const String jitSnapshotCmdName = 'jit-snapshot';
static const String kernelCmdName = 'kernel';

final String commandName;
final String help;
final String fileExt;
final String formatName;

@override
FutureOr<int> run() async {
//...
//调用dart --snapshot-kind=$formatName
List<String> args = [];
args.add('--snapshot-kind=$formatName');
args.add('--snapshot=${path.canonicalize(outputFile)}');
final process = await startDartProcess(sdk, args);
return process.exitCode;
}
}

dart compile kernel/jit-snapshot等价于dart --snapshot-kind=kernel/app-jits。只不过是新版本Dart工具统一封装到compile中而已。

例如dart --snapshot=main.snapshot main.dart生成main.snapshotdart compile kernel main.dart生成main.dillmain.dillmain.snapshot实际上是一样的,都是Kernel快照文件,文件大小也相同。

CompileNativeCommand

查看CompileNativeCommand代码,对应dart compile aotdart compile exe命令,调用了dart2nativegenerateNative方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//{dart_sdk}/pkg/dartdev/lib/src/commands/compile.dart
//exe和aot编译
class CompileNativeCommand extends CompileSubcommandCommand {
static const String exeCmdName = 'exe';
static const String aotSnapshotCmdName = 'aot-snapshot';

final String commandName;
final String format;
final String help;

@override
FutureOr<int> run() async {
//...
try {
await generateNative(
//...
);
return 0;
} catch (e) {
log.stderr('Error: AOT compilation failed');
log.stderr(e.toString());
return compileErrorExitCode;
}
}
}

generateNative流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//{dart-sdk}/pkg/dart2native/lib/generate.dart
final Directory binDir = File(Platform.resolvedExecutable).parent;
final String executableSuffix = Platform.isWindows ? '.exe' : '';
final String dartaotruntime = path.join(binDir.path, 'dartaotruntime$executableSuffix');
final String genKernel = path.join(binDir.path, 'snapshots', 'gen_kernel.dart.snapshot');
final String genSnapshot = path.join(binDir.path, 'utils', 'gen_snapshot$executableSuffix');
final String productPlatformDill = path.join(binDir.parent.path, 'lib', '_internal', 'vm_platform_strong_product.dill');

Future<void> generateNative({
//...
}) async {
//...
try {
final String kernelFile = path.join(tempDir.path, 'kernel.dill');
//1. 编译前端,生成kernel文件:执行gen_kernel.dart.snapshot程序,最终会调用front_end方法
final kernelResult = await generateAotKernel(Platform.executable, genKernel,
productPlatformDill, sourcePath, kernelFile, packages, defines,
enableExperiment: enableExperiment,
extraGenKernelOptions: [
'--invocation-modes=compile',
'--verbosity=$verbosity',
if (soundNullSafety != null)
'--${soundNullSafety ? '' : 'no-'}sound-null-safety',
]);
//...

final String snapshotFile = (outputKind == Kind.aot
? outputPath
: path.join(tempDir.path, 'snapshot.aot'));
//2. 编译后端,生成aot目标文件:执行gen_snapshot程序
final snapshotResult = await generateAotSnapshot(genSnapshot, kernelFile,
snapshotFile, debugPath, enableAsserts, extraOptions);
//...
if (outputKind == Kind.exe) {
if (verbose) {
print('Generating executable.');
}
//3. exe编译需要加上Dart运行时
await writeAppendedExecutable(dartaotruntime, snapshotFile, outputPath);

if (Platform.isLinux || Platform.isMacOS) {
//4. 标记为可执行
await markExecutable(outputPath);
}
}

print('Generated: $outputPath');
} finally {
tempDir.deleteSync(recursive: true);
}
}

编译流程

  • Dart编译前端(frontend,{dart_sdk}/pkg/front_end/lib/src/api_prototype/front_end.dart):将Dart源码编译为Kernel二进制文件,是一种平台无关的中间代码。

    Dart编译前端生成的.dill文件类似于Java编译前端的.class文件,通过虚拟机执行,和平台无关。

  • Dart编译后端(gen_snapshot,{dart_sdk}/runtime/bin/gen_snapshot.cc):将Kernel二进制文件编译出目标代码

    • 将Kernel二进制代码生成一个控制流图(CFG,control flow graph),CFG由中间语言(IL,Intermediate Language)指令组成。
    • 对IL指令进行优化
    • CFG编译成机器码,每个IL指令对应多个机器指令

    IL指令类似于虚拟机指令,从堆栈中获取操作数,执行操作,将结果推送到堆栈中

  • 创建exe可执行文件:writeAppendedExecutable方法合并dartaotruntime和aot目标代码

命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 编译前端1
dart {dart-sdk}/bin/snapshots/frontend_server.dart.snapshot
--sdk-root {dart-sdk}/lib/_internal/
--platform vm_platform_strong.dill
--aot --tfa
-Ddart.vm.profile=false
-Ddart.vm.product=true
--output-dill {build_dir}/app.dill
main.dart

# 编译前端2:和方式一等价,参数稍微有些差异
dart {dart-sdk}/bin/snapshots/gen_kernel.dart.snapshot
--platform {dart-sdk}/lib/_internal/vm_platform_strong.dill
--aot --tfa
-Ddart.vm.profile=false
-Ddart.vm.product=true
-o {build_dir}/app.dill
main.dart

# 编译后端
{dart-sdk}/bin/utils/gen_snapshot
--deterministic
--snapshot_kind=app-aot-elf
--elf={output_path}/app.aot
app.dill

# 运行aot文件
{dart-sdk}/bin/dartaotruntime app.aot

gen_kernel.dart.snapshotfrontend_server.dart.snapshot都是调用front_end的方法(可以分析源码,或者执行命令异常查看方法调用栈),参数稍微有些差异

例如frontend_server.dart.snapshot支持--import-dill参数,可以加载和链接其他dill文件。

gen_kernel.dart.snapshot--platform参数用于指定sdk完整路径,用于将平台库加载到产物中。与frontend_server.dart.snapshot--sdk-root--platform参数作用相同,--sdk-root指定文件夹路径,--platform指定文件名,加起来是完整路径。

  • Dart平台库位于{dart-sdk}/lib/_internal/vm_platform_strong.dill
  • Flutter平台库中包含Dart库和Flutter框架本身,位于{flutter_sdk}/bin/cache/artifacts/engine/common/flutter_patched_sdk/platform_strong.dill

--target参数:使用Dart平台库时,target值需要为vm,使用Flutter平台库时,target值需要为flutter,否则会编译失败。说明如下:

1
2
3
4
$ ./dart-sdk/bin/dart ./dart-sdk/bin/snapshots/frontend_server.dart.snapshot -h
#....
--target Target model that determines what core libraries are available
[vm (default), flutter, flutter_runner, dart_runner, dartdevc]

target相关源码位于{dart-sdk}/pkg/vm/lib/target/

注:target为flutter时,无法使用dartaotruntime执行aot文件,由于找不到对应的平台库,会报错Dart_LookupLibrary: library 'dart:_builtin' not found.

Kernel文件踩坑

  • dart compile kernel生成的Kernel文件不能用于gen_snapshot后端编译,会报错:Unable to use class Library:'dart:core' Class: _List@0150898 which is not loaded yet.。但是可以使用dart run main.dill运行。
  • 编译前端--no-aot参数生成的Kernel文件不能用于gen_snapshot后端编译,会报错:error: Missing table selector metadata! Probably gen_kernel was run in non-AOT mode or without TFA.。但是可以使用dart run main.dill运行
  • 编译前端--aot参数生成的Kernel文件可以用于gen_snapshot后端编译,但是不能直接用dart运行,会报错:error: vm.procedure-attributes.metadata metadata is allowed in precompiled mode only

上述三者都生成了Kernel文件,但是不太一样。具体区别暂时不清楚。

Dart SDK版本一致

Dart编译前端、编译后端、以及Dart运行时的版本必须一致,否则会报错版本不匹配。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Dart运行不同版本的编译前端报错
$ dart ~/Flutter/engine/src/out/host_debug_unopt/dart-sdk/bin/snapshots/frontend_server.dart.snapshot --sdk-root ./ --target=flutter -Ddart.vm.profile=false -Ddart.vm.product=false --aot --tfa --output-dill ~/Desktop/bin/main.dill ~/Desktop/bin/main.dart
Wrong full snapshot version, expected '9cf77f4405212c45daf608e1cd646852' found 'd56742caf7b3b3f4bd2df93a9bbb5503'

# gen_snapshot编译不同版本的dill文件报错
$ ~/Flutter/flutter/bin/cache/dart-sdk/bin/utils/gen_snapshot --snapshot_kind=app-aot-elf --elf=main.aot main.dill
Can't load Kernel binary: Invalid kernel binary format version.

# Dart执行不同版本的dill文件报错
$ dart main.dill
Can't load Kernel binary: Invalid kernel binary format version.

# Dart预编译运行时执行不同版本的aot文件报错
$ ~/Flutter/engine/src/out/host_debug_unopt/dart-sdk/bin/dartaotruntime main.aot
VM initialization failed: Wrong full snapshot version, expected 'd56742caf7b3b3f4bd2df93a9bbb5503' found '9cf77f4405212c45daf608e1cd646852'

生成的dill文件和aot文件中带了版本信息,执行的时候会进行校验。

主机上有多个版本Dart SDK,例如Flutter引擎编译出来的Dart SDK和Flutter SDK中内置的Dart SDK版本不一致。此时需要分别进入对应路径下执行命令,或者配置多个环境变量。为了避免麻烦,可以切换Flutter引擎到对应的commit id,保持版本一致。

Web平台

dart支持在Web平台上执行,既不是JIT也不是AOT:生成JavaScript代码,运行在浏览器中,而不是目标平台代码

  1. 开发阶段使用dartdevc增量式编译器
  2. 生产环境使用dart2js编译器,高版本替换为dart compile js命令

官方建议使用webdev工具,而不是直接使用dartdevcdart2js工具。

  • webdev serve:编译并部署到开发服务器,使用localhost:8080访问。默认使用dartdevc编译。添加--release选项,替换为dart2js编译
  • webdev build:默认使用dart2js编译,添加--no-release选项,替换为dartdevc编译

Dart源码下载和编译

Dart SDK是Dart源码的编译产物。

源码下载和编译

  1. 安装depot_tools

    1. 下载:git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
    2. 设置环境变量:.bash_profile文件中添加export PATH=/your_path/depot_tools/:$PATH
  2. 下载Dart源码和DEPS依赖

    1
    2
    3
    4
    5
    # 创建目录
    mkdir dart-sdk
    cd dart-sdk
    # 通过gclient下载dart源码和DEPS依赖
    fetch dart
  3. 编译Dart SDK源码,生成Dart SDK归档文件

    1
    2
    3
    4
    cd sdk
    ./tools/build.py --no-goma --mode release --arch x64 create_sdk
    # 也可以指定多个build_targets编译
    ./tools/build.py --mode=release --arch=x64 create_sdk runtime gen_snapshot frontend_server.dart.snapshot

build_targets参数没有找到具体说明,不过可以参考tools/bots/test_matrix.json文件中的builder_configurations

踩坑:FlutterEngine中下载的Dart SDK({flutter_engine}/src/third_party/dart)不包含编译需要的依赖项目,无法直接用于编译,因此需要使用gclient重新下载Dart源码。

应该也可以手动创建.gclient文件,执行gclient sync下载依赖

交叉编译

编译生成Dart虚拟机

交叉编译Android平台的Dart VM,可以在Android平台执行Dart应用程序

  1. 下载Android需要的依赖,如NDK,SDK等
    1. 修改dart-sdk目录下的.gclient文件:最后一行添加target_os = ['android']
    2. 下载依赖项目:执行gclient sync
  2. 交叉编译Arm64的Android平台的Dart VM:./tools/build.py --no-goma --arch=arm64 --mode=release --os=android runtime
  3. 使用adb将编译后的Dart VM工具push到Android平台中:adb push ./xcodebuild/ReleaseAndroidARM64/dart /data/local/tmp/dart
  4. 将Dart程序push到Android平台中:adb push main.dart /data/local/tmp/
  5. 运行Dart程序:adb shell /data/local/tmp/dart /data/local/tmp/main.dart

踩坑:这里无法使用adb shell进入Android终端执行,会报错,缺少工具和库。

编译生成Dart SDK

可以将编译出来的整个SDK归档文件push到Android平台中并设置环境变量,此时可以像在主机上一样使用Dart SDK,编译和执行Dart应用。如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 交叉编译Android平台的Dart SDK
$ ./tools/build.py --no-goma -a=arm64 --mode=release --os=android create_sdk
$ adb push ./xcodebuild/ReleaseAndroidARM64/dart-sdk/ /data/local/tmp/dart-sdk/
$ adb shell
HWAUM-Q:/ $ cd /data/local/tmp
# 设置环境变量
HWAUM-Q:/data/local/tmp $ export PATH="$PATH:$PWD/dart-sdk/bin"
# Dart虚拟机执行,输出Hello World
HWAUM-Q:/data/local/tmp $ dart run main.dart

# 编译aot快照
HWAUM-Q:/data/local/tmp $ dart compile aot-snapshot main.dart
# 执行aot文件,输出Hello World
HWAUM-Q:/data/local/tmp $ dartaotruntime main.aot

# 编译exe可执行程序
HWAUM-Q:/data/local/tmp $ dart compile exe main.dart
# 执行exe程序,输出Hello World
HWAUM-Q:/data/local/tmp $ ./main.exe

# dart编译前端,生成main.dill
HWAUM-Q:/data/local/tmp $ dart dart-sdk/bin/snapshots/frontend_server.dart.snapshot --sdk-root dart-sdk/lib/_internal/ --platform vm_platform_strong.dill --aot --tfa -Ddart.vm.product=true --output-dill main.dill main.dart
# dart编译后端,生成main.aot
HWAUM-Q:/data/local/tmp $ ./dart-sdk/bin/utils/gen_snapshot --snapshot_kind=app-aot-elf --elf=main.aot main.dill
# 执行main.aot文件,输出Hello World
HWAUM-Q:/data/local/tmp $ dartaotruntime main.aot

结语

了解Dart编译方式和不同产物,以及执行原理,主要有以下场景

  1. 为动态化,分包等提供一些思路
  2. 使用交叉编译在嵌入式平台中执行Dart或Flutter程序
  3. 定制虚拟机、编译器等。

参考资料:

欢迎关注我的其它发布渠道