tree: ae3af6bbf83062024babcf873952ec1b9114a43c
  1. common/
  2. host/
  3. modules/
  4. dynamic_interface.yaml
  5. README.md
  6. run.sh
pkg/dynamic_modules/example/README.md

Calculator example

This simple command-line calculator example illustrates how to use Dart Dynamic Modules. Please refer to the package readme for details about what Dynamic Modules are, their limitations, typical steps to use them, and how this example maps to those steps.

The code in this folder is a simple calculator command line interface that lets users extend it with new operations beyond those built-in.

We recommend exploring the example in this order:

  • Check out dynamic_interface.yaml and common/common.dart. These define the API surface area. That is, the interface exposed to dynamic modules to define new operations, as well as the API to register them.
  • Then, see host/main.dart. This defines the main REPL and provides one built-in operation (the addition operation).
  • Then, check the files under the modules/ subfolder. Each file defines a dynamic module that provides new operations for the calculator following the API surface area defined earlier.
  • Finally look at the run.sh script to see how the pieces come together.

The run.sh script contains a lot of steps and details:

  • First, it shows how to build Dart SDK artifacts with support for Dart Dynamic Modules once you have a proper Dart SDK checkout. A change with how we normally build Dart is the use of the --dart-dynamic-modules build flag.

  • Second, it shows how to use the dynamic interface to build various artifacts:

    • The unoptimized kernel of the app: an input needed later when compiling bytecode.
    • The optimized AOT kernel of the app: an input to the next step.
    • The optimized AOT snapshot of the app: the app that we will be running.
    • The bytecode for each dynamic module.

Traditionally compilation of an AOT app is done in a single command (e.g. dart compile exe), but we had to split the underlying logic into multiple steps (building kernel and snapshots separately). In part, this is because we haven't added the plumbing to provide the dynamic interface through the regular commands. But another reason is that we need additional artifacts to anchor the compilation of dynamic modules later (like the unoptimized kernel), which are not usually materialized when using the regular commands.

Bytecode files are copied over to a data directory, this is an over simplistic way to represent the delivery of a dynamic module to an app (rather than using the network or some other mechanism).

We haven't implemented provenance validation in the package yet, so the validation step described in the documentation is not represented in the example yet. We will update it in the future once the corresponding API is added.