Skip to content

mirco_dart is a dart interpreter that can dynamically execute dart code

License

Notifications You must be signed in to change notification settings

lancexin/micro_dart

Repository files navigation

This project is still in the development stage and there are still many bugs. It is not recommended to use it in the project. Currently, it only serves as a learning reference

Introduce

The initial goal of this project was to develop a lightweight dart interpreter on Flutter that could dynamically execute dart code and enable hot updates on Flutter。

I don't want to introduce other languages like Lua, JavaScript, Just use Dart language。

I don't want to introduce some third-party language interpreters, such as LuaVM, JSCore。

I hope the bridging code can be automatically generated。

I hope the solution is lightweight, without so many private APIs, and the old code can be dynamically modified with minimal modifications. The new code can be integrated just like the Flutter plugin。

Project Directory

micro_dart_compiler

MicroDart compiler compiles dart code into dynamically runnable bytecode

you can test the compiler : micro_dart_compiler/test/test_all.dart

micro_dart_runtime

MicroDart interpreter, which can run bytecode

micro_dart_generator

A code generator that generates MicroDart interpreter bridging code based on the current dart_sdk, giving the interpreter the ability to call non dynamic code, but it is not yet fully developed

micro_dart_proxy_core

The dark SDK bridging code generated by the code generator, and the proxy directory contains manually modified code that is currently not supported for generation

micro_dart_flutter

Flutter SDK bridging code, the proxy directory contains manually modified code that is currently not supported for generation

Examples

Some Flutter/Dart examples of dynamic execute code use of MicroDart

test report

At present, it seems that there is a certain performance loss when using micro_dart. If the bridge code of the all Flutter SDK is connected, it will increase by about 30M in the software package and 2-50M in terms of running memory. If you want to test it specifically, you can try the examples.

Questions

How to compile the micro_dart_compiler?

  1. Before downloading the code, you need to download the dark sdk. The specific download method can be found in the document https://github.com/dart-lang/sdk/wiki/Building I recommend you to download it using dept_tools.
  2. I am using Dark SDK version 3.0.5. you need checkout the right version and then run gclient sync to download the related package
  3. Modify the path in micro_dart_compiler/pubspec.yaml to point to the dark SDK you downloaded

How to generate executable dynamic code?

You can execute the following commands through command-line tools:

dart run micro_dart.dart.snapshot --verbose examples/flutter_plugin_2/lib/plugin_2.dart --op-out plugin_2_ops.txt --json-ast-out plugin_2_json.txt --ast-out plugin_2_ast.txt --external-methods-out external-methods-out.txt

If it cannot run, check if the dart version is 3.0.5

There are also relevant examples in the test directory of micro_dart_compiler that you can take a look at:

micro_dart_compiler/test/dart_example_1_generate.dart

micro_dart_compiler/test/flutter_plugin_1_generate.dart

micro_dart_compiler/test/flutter_plugin_2_generate.dart

micro_dart_compiler/test/flutter_plugin_animations_generate.dart

micro_dart_compiler/test/flutter_plugin_gallery_generate.dart

In the example, the code is all called in the main function. Can it only be globally dynamic?

It can be partially dynamic, it can be a widget or a logical method. CallStatisticFunction is the entry method that calls dynamic code. It doesn't necessarily need to be called in the main function. For example, if you only want a certain widget to perform a hot update, you can call callStaticFunction to return the desired widget, which can be placed under a certain widget node. such as :

class BankListInfoPage extends StatelessWidget {
    const BankListInfoPage({super.key});
    @OverRide
    Widget build(BuildContext context) {
        return Scaffold(
        appBar: AppBar(
        title: const Text("我的银行卡"),
        ),
        body: engine.callStaticFunction(packageUri, "getMyBankWidget", [], {});
    }
}

How to generate bridging code?

Micro_dart_generator is a code generator, which is a build_runner library integrated into the original program:

dev_dependencies:
  micro_dart_generator: 
    path: ../../micro_dart_generator

Then in the command line, dart run build_runner will automatically generate bridging code

If you don't want to use build_runner, or just want to generate bridging code for dark core or flutter SDK, there are some codes below that can be used for reference in micro_dart_generator/bin.

If you don't want to modify it every time, you can modify overwrite_strategy.json. This file can overwrite some erroneous generated code. Or if you want to ignore some global packages, you can configure them in this file

How to generate minimum bridging code?

At present, micro_dart_generator can be generated through configuration, and the configuration format can be referred to:

examples/flutter_example_gallery_2/micro_dart_external_methods.json

examples/flutter_animations_2/micro_dart_external_methods.json

examples/flutter_example_2/micro_dart_external_methods.json

According to this file, micro_dart_compiler has the ability to generate its own minimum amount when compiling dynamic code, which represents all external bridges called by the current plugin. The specific generation method can refer to:

micro_dart_compiler/test/flutter_plugin_2_generate.dart:

File("${flutterExamplePath}micro_dart_external_methods.json")
    .writeAsStringSync(program.getExternalCallMethods());

the relationship between dynamic code and the original program

The original program is separate from the dynamic code. Dynamic code is in the form of plugins, which depend on the original program。

For example, the example of flutter_gallery:

flutter_plugin_gallery is the code repository that we need to dynamically update

flutter_example_gallery is the original program and a non dynamic code repository

The following needs to be configured in the pubspec.yaml of flutter_plugin_gallery:

dependencies:
  flutter:
    sdk: flutter
  flutter_example_gallery:
    path: ../flutter_example_gallery

The following needs to be configured in the pubspec.yaml of flutter_example_gallery:

dependencies:
  flutter:
    sdk: flutter
  micro_dart_flutter: 
    path: ../../micro_dart_flutter
  micro_dart_runtime: 
    path: ../../micro_dart_runtime

The micro_dart_flutter here is the bridging code, and if you are also using Flutter 3.10.5, you can use it directly. But if it is another version of Flutter, you need to generate your own bridging code through micro_dart_generator, and there are subtle differences in the SDK for different Flutter versions. I also do not recommend introducing global flutter bridging because it still consumes a lot of memory and the compilation will be slow.

Why should dynamic code be separated from the original program?

Dynamic code is distinguished by package, and the compiler can easily determine whether dynamic code needs to be generated based on the package name. Moreover, non packageName calls can be considered as external calls, which is convenient and simple to do. Another advantage of doing this is that if there is no need for dynamic in the future, this plugin can be directly introduced into the original program without any code modifications.

Can multiple dynamic plugins be introduced?

Sure, a dynamic plugin corresponds to a MicroDartEngine object, which is an interpreter environment. Multiple interpreters MicroDartEngine objects are allowed in the source program。

Does a dynamic plugin correspond to a page?

Not necessarily, you can put multiple pages in one dynamic plugin, it depends on yourself. However, the initialization of MicroDartEngine consumes resources, depending on the size of the dynamic code, which will be imported into memory at once. I understand that in scenarios where multiple MicroDartEngines are sensitive to memory, MicroDartEngines can be initialized when needed and destroyed when used up.

About

mirco_dart is a dart interpreter that can dynamically execute dart code

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages