diff --git a/component-model/src/SUMMARY.md b/component-model/src/SUMMARY.md index 6c2b2b94..813959ea 100644 --- a/component-model/src/SUMMARY.md +++ b/component-model/src/SUMMARY.md @@ -22,6 +22,7 @@ - [Building a simple component](./building-a-simple-component.md) - [C/C++](./language-support/building-a-simple-component/c.md) - [C#](./language-support/building-a-simple-component/csharp.md) + - [Dart](./language-support/building-a-simple-component/dart.md) - [Go](./language-support/building-a-simple-component/go.md) - [JavaScript](./language-support/building-a-simple-component/javascript.md) - [Python](./language-support/building-a-simple-component/python.md) @@ -31,16 +32,19 @@ - [WebAssembly Text Format (WAT)](./language-support/building-a-simple-component/wat.md) - [Other Languages](./language-support/building-a-simple-component/other-languages.md) - [Importing and reusing components](./importing-and-reusing-components.md) + - [Dart](./language-support/importing-and-reusing-components/dart.md) - [Rust](./language-support/importing-and-reusing-components/rust.md) - [Javascript](./language-support/importing-and-reusing-components/javascript.md) - [Other Languages](./language-support/importing-and-reusing-components/other-languages.md) - [Creating runnable components](./creating-runnable-components.md) + - [Dart](./language-support/creating-runnable-components/dart.md) - [Rust](./language-support/creating-runnable-components/rust.md) - [Javascript](./language-support/creating-runnable-components/javascript.md) - [Other languages](./language-support/creating-runnable-components/other-languages.md) - [Using WIT resources](./using-wit-resources.md) - [Rust](./language-support/using-wit-resources/rust.md) - [Using HTTP in components](./using-http-in-components.md) + - [Dart](./language-support/using-http-in-components/dart.md) - [Rust](./language-support/using-http-in-components/rust.md) - [Running Components](./running-components.md) - [Wasmtime](./running-components/wasmtime.md) diff --git a/component-model/src/building-a-simple-component.md b/component-model/src/building-a-simple-component.md index b3f9f3bf..23c1117f 100644 --- a/component-model/src/building-a-simple-component.md +++ b/component-model/src/building-a-simple-component.md @@ -15,6 +15,7 @@ This guide is implemented for various languages: |----------------------------------------------------------------------------------------| | [C/C++](./language-support/building-a-simple-component/c.md) | | [C#](./language-support/building-a-simple-component/csharp.md) | +| [Dart](./language-support/building-a-simple-component/dart.md) | | [Go](./language-support/building-a-simple-component/go.md) | | [JavaScript](./language-support/building-a-simple-component/javascript.md) | | [Python](./language-support/building-a-simple-component/python.md) | diff --git a/component-model/src/creating-runnable-components.md b/component-model/src/creating-runnable-components.md index 7faf27ba..0835b135 100644 --- a/component-model/src/creating-runnable-components.md +++ b/component-model/src/creating-runnable-components.md @@ -53,6 +53,7 @@ This guide is implemented for various languages: | Language | |---------------------------------------------------------------------------------------| +| [Dart](./language-support/creating-runnable-components/dart.md) | | [Rust](./language-support/creating-runnable-components/rust.md) | | [Javascript](./language-support/creating-runnable-components/javascript.md) | | [Other Languages](./language-support/creating-runnable-components/other-languages.md) | diff --git a/component-model/src/importing-and-reusing-components.md b/component-model/src/importing-and-reusing-components.md index 1a32e332..6ddd384c 100644 --- a/component-model/src/importing-and-reusing-components.md +++ b/component-model/src/importing-and-reusing-components.md @@ -21,6 +21,7 @@ This guide is implemented for various languages: | Language | |-------------------------------------------------------------------------------------------| +| [Dart](./language-support/importing-and-reusing-components/dart.md) | | [Rust](./language-support/importing-and-reusing-components/rust.md) | | [Javascript](./language-support/importing-and-reusing-components/javascript.md) | | [Other languages](./language-support/importing-and-reusing-components/other-languages.md) | diff --git a/component-model/src/introduction.md b/component-model/src/introduction.md index 59bd9909..3ca888d3 100644 --- a/component-model/src/introduction.md +++ b/component-model/src/introduction.md @@ -15,11 +15,12 @@ This documentation is aimed at _users_ of the component model: developers of lib |--------------------------|----------------------|-------------------| | [Why Components?] | [C/C++] | [Composing] | | [Components] | [C#] | [Running] | -| [Interfaces] | [Go] | [Distributing] | -| [Worlds] | [JavaScript] | | +| [Interfaces] | [Dart] | [Distributing] | +| [Worlds] | [Go] | [Distributing] | +| | [JavaScript] | | | | [Python] | | | | [Rust] | | -| | [MoonBit] | | +| | [MoonBit] | | [Why Components?]: ./design/why-component-model.md [Components]: ./design/components.md @@ -28,6 +29,7 @@ This documentation is aimed at _users_ of the component model: developers of lib [C/C++]: ./language-support/building-a-simple-component/c.md [C#]: ./language-support/building-a-simple-component/csharp.md +[Dart]: ./language-support/building-a-simple-component/dart.md [Go]: ./language-support/building-a-simple-component/go.md [JavaScript]: ./language-support/building-a-simple-component/javascript.md [Python]: ./language-support/building-a-simple-component/python.md diff --git a/component-model/src/language-support.md b/component-model/src/language-support.md index 8b2573f5..769d569c 100644 --- a/component-model/src/language-support.md +++ b/component-model/src/language-support.md @@ -27,6 +27,7 @@ without using a higher-level language front-end. - [Building a Component with `wit-bindgen` and `wasm-tools`](./language-support/building-a-simple-component/c.md#building-a-component-with-wit-bindgen-and-wasm-tools) - [Running a Component from C/C++ Applications](./language-support/building-a-simple-component/c.md#running-a-component-from-cc-applications) - [C# Tooling](./language-support/building-a-simple-component/csharp.md) + - [Dart Tooling](./language-support/building-a-simple-component/dart.md) - [Go Tooling](./language-support/building-a-simple-component/go.md) - [JavaScript Tooling](./language-support/building-a-simple-component/javascript.md) - [Building a Component with `jco`](./language-support/building-a-simple-component/javascript.md#building-a-component-with-jco) diff --git a/component-model/src/language-support/building-a-simple-component/dart.md b/component-model/src/language-support/building-a-simple-component/dart.md new file mode 100644 index 00000000..f455fba8 --- /dev/null +++ b/component-model/src/language-support/building-a-simple-component/dart.md @@ -0,0 +1,139 @@ +# Dart Tooling + +> [!WARNING] +> Compiling Dart to non-web WebAssembly targets is experimental. +> This guide requires Dart version `3.14.0-251.0.dev` or later. + +WebAssembly components in Dart can be built with the [wasm_tools package](https://github.com/simolus3/wasm.dart/) on pub.dev. + +This guide walks through building a Dart component that implements +the `adder` world defined in the [`adder/world.wit` package][docs-adder]. +The component will implement the `adder` world, which contains an `add` interface with an `add` function. + +Keep in mind that this is a basic intro. For more examples, please see the [dart-samples][examples] +from the `wasm_tools` package or TODO: running a component. + +If you still have questions, feel free to open an issue on [the repository][wasm_tools] +or reach out on [Zulip][chat]. + +## 1. Create your Dart project + +Begin by creating a fresh Dart project: + +```sh +dart create -t cli dart_wasm_adder +cd dart_wasm_adder +``` + +## 2. Install the tools + +All tools requires to create WebAssembly components from Dart can be installed via pub: + +```sh +dart pub add wasm_components dev:wasm_tools +``` + +The `wasm_components` package provides runtime support for component models +(like an allocator or async task management), while `wasm_tools` contains `witgen` +and tools to compile Dart to components. + +## 3. Generate bindings for the Wasm component + +Since we will be implementing the [`adder` world][docs-adder], we can copy the WIT to our project. +Create a file named `adder.wit` and paste the following code into it: + +```wit +{{#include ../../../examples/tutorial/wit/adder/world.wit}} +``` + +Generate Dart code and required metadata for the compiler with + +```sh +dart run wasm_tools witgen -i adder.wit +``` + +This generates: + +- `lib/src/components/docs_adder.dart` containing interfaces (only `add` in this example). +- `lib/src/components/docs_adder_adder.dart` containing bindings for the `adder` world. +- `lib/src/components/docs_adder_adder.json` containing metadata used to turn WebAssembly + modules emitted by `dart2wasm` into components. + +The JSON file describe which component imports and exports a Dart program needs. +The toolchain reads it via [link hooks]. All packages defining component imports +or exports need one, including the `dart_wasm_adder` package. Create a +`hook/link.dart` file with these contents: + +```dart +import 'dart:convert'; +import 'dart:io'; + +import 'package:hooks/hooks.dart'; +import 'package:wasm_tools/hooks.dart'; + +void main(List args) => link(args, (input, output) async { + if (input.config.buildWasmComponent) { + final abi = input.packageRoot.resolve('lib/src/components/docs_adder_adder.json'); + + output.dependencies.add(abi); + output.assets.webAssemblyComponents.add( + WasmComponentAsset( + encoded: json.decode( + File(abi.toFilePath()).readAsStringSync(), + ) as Map, + ), + ); + } +}); +``` + +## 4. Implement the `add` function + +The generated `adderComponent` function is used to define this component: +It receives imported interfaces as parameters (in this case, there aren't any) +and returns the exported interface. + +For this example, replace `bin/dart_wasm_adder.dart` with: + +```dart +import 'package:dart_wasm_adder/src/components/docs_adder.dart'; +import 'package:dart_wasm_adder/src/components/docs_adder_adder.dart'; + +import 'package:wasm_components/wasm_components.dart'; + +void main() { + adderComponent((_) => const _Add()); +} + +final class const _Add() implements Add { + @override + int add({required int x, required int y}) { + return x + y; + } +} +``` + +## 5. Testing the `add` component + +With all generated bindings set up, the component can be compiled: + +```sh +dart run wasm_tools compile bin/dart_wasm_adder.dart +``` + +This creates `bin/dart_wasm_adder.wasm`, a component we can invoke from the +CLI with [wasmtime]: + +```console +$ wasmtime run --invoke 'add(1, 2)' bin/dart_wasm_adder.wasm +3 +``` + +With this, we have successfully built and run a basic WebAssembly component with Dart 🎉 + +[wasm_tools]: https://github.com/simolus3/wasm.dart/ +[examples]: https://github.com/simolus3/wasm.dart/tree/main/pkg/wasm_tools/example +[docs-adder]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit +[chat]: https://bytecodealliance.zulipchat.com/#narrow/channel/394175-SIG-Guest-Languages/topic/Dart.20subgroup/with/614175797 +[link hooks]: https://dart.dev/tools/hooks +[wasmtime]: https://wasmtime.dev/ diff --git a/component-model/src/language-support/creating-runnable-components/dart.md b/component-model/src/language-support/creating-runnable-components/dart.md new file mode 100644 index 00000000..6e4ea30c --- /dev/null +++ b/component-model/src/language-support/creating-runnable-components/dart.md @@ -0,0 +1,81 @@ +# Creating Runnable Components (Dart) + +> [!WARNING] +> Compiling Dart to non-web WebAssembly targets is experimental. +> This guide requires Dart version `3.14.0-251.0.dev` or later. + +## Creating a command component + +A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime` +(or other `wasi:cli` hosts). Ignoring the specifics of defining components in Dart, this is the +equivalent of running a plain Dart program with a `main()` function. + +### 1. Create a new Dart project + +To create a command with Dart, start with a fresh Dart package: + +```sh +dart create -t cli dart_wasm_cli +``` + +To configure this package for WebAssembly components, add tooling dependencies. +Additionally, the `wasi` package provides generated bindings to WASI definitions, +meaning that running `dart run wasm_tools witgen` won't be necessary. + +```sh +dart pub add wasi wasm_components dev:wasm_tools +``` + +### 2. Write the relevant Dart + +The following code can be inserted into `bin/dart_wasm_cli.dart`: + +```dart +import 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:wasi/cli/command.dart'; +import 'package:wasi/cli.dart'; +import 'package:wasm_components/wasm_components.dart'; + +void main() { + commandComponent((imports) => _Run(imports.cliStdout)); +} + +final class _Run(final Stdout stdout) implements Run { + @override + Future> run() async { + final out = StreamController(); + final stdoutDone = stdout.writeViaStream(data: out.stream); + + out.add(utf8.encode('Hello world!\n')); + out.close(); + await stdoutDone; + + return const .ok(null); + } +} +``` + +### 3. Build the component + +To build the component, use `wasm_tools compile`: + +```sh +dart run wasm_tools compile bin/dart_wasm_cli.dart +``` + +The [link hook] of the `wasi` package detects that this Dart program defines a +command component and configures the relevant imports and exports. +This allows the component to target `wasi:cli/command@0.3.0`. + +### 4. Run the component with `wasmtime` + +To run your command component: + +```sh +wasmtime run bin/dart_wasm_cli.wasm +``` + +[link hook]: https://github.com/simolus3/wasm.dart/blob/ec9f801ebe258fc4c8dc0dceec35af8b9447627f/pkg/wasi/hook/link.dart#L17-L21 diff --git a/component-model/src/language-support/importing-and-reusing-components/dart.md b/component-model/src/language-support/importing-and-reusing-components/dart.md new file mode 100644 index 00000000..e928f3f2 --- /dev/null +++ b/component-model/src/language-support/importing-and-reusing-components/dart.md @@ -0,0 +1,95 @@ +# Importing and Reusing components (Dart) + +> [!WARNING] +> Compiling Dart to non-web WebAssembly targets is experimental. +> This guide requires Dart version `3.14.0-251.0.dev` or later. + +## Importing an interface + +The world file (`wit/world.wit`) we generated doesn't specify any imports. +If your component consumes other components, you can edit the `world.wit` file to import their interfaces. + +For example, suppose you have created and built the adder component as explained in the earlier tutorials and want to use +that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: + +```wit +{{#include ../../../examples/tutorial/wit/calculator/world.wit}} +``` + +### Referencing the package to import + +To generate code for multiple wit packages, we need multiple `.wit` files. Treating the +calculator as the entrypoint, use a file structure like this: + +``` +pubspec.yaml +wit/ +├── deps/ +│ └── docs-adder-0.1.0/ +│ └── adder.wit +└── calculator.wit +``` + +To generate Dart code, select the directory and the root world: + +```sh +dart run wasm_tools witgen -i ./wit -w "docs:calculator/calculator" +``` + +### Calling the import from Dart + +Now the declaration of `add` in the adder's WIT file is visible as an import when +defining the `Calculate` component: + +```dart +import 'package:dart_wasm_adder/src/components/docs_adder.dart'; +import 'package:dart_wasm_adder/src/components/docs_calculator.dart'; +import 'package:dart_wasm_adder/src/components/docs_calculator_calculator.dart'; + +import 'package:wasm_components/wasm_components.dart'; + +void main() { + calculatorComponent((imports) => _Calculator(add: imports.adderAdd)); +} + +final class const _Calculator({required final Add add}) implements Calculate { + @override + int evalExpression({ + required CalculateOp op, + required int x, + required int y, + }) { + return switch (op) { + .add => add.add(x: x, y: y), + }; + } +} +``` + +### Fulfilling the import + +When you build this using `dart run wasm_tools compile`, the `add` interface remains unsatisfied +(i.e. imported). + +The calculator has taken a dependency on the `add` _interface_, but has not linked the `adder` implementation of +that interface - this is not like referencing the `Add` Dart class (Indeed, `calculator` could import the `add` interface even if there was no Dart implementation of the WIT file) . + +You can see this by running [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the calculator's world: + +``` +$ dart run wasm_tools compile bin/calculate.dart --no-implicit-wasi-imports + +$ wasm-tools component wit ./bin/calculate.wasm +package root:component; + +world root { + import docs:adder/add@0.1.0; + + export docs:calculator/calculate@0.1.0; +} +``` + +As the import is unfulfilled, the `calculate.wasm` component could not run by itself in its current form. The next step is to fulfill the `add` import, so that only `calculate` is exported, and the component can be run. + +The process of fulfilling imports via other component's exports is called "composition". Learn more about how to compose the calculator.wasm +with an adder.wasm into a single, self-contained component in the [component composition guide](../../composing-and-distributing/composing.md). diff --git a/component-model/src/language-support/using-http-in-components/dart.md b/component-model/src/language-support/using-http-in-components/dart.md new file mode 100644 index 00000000..9698798f --- /dev/null +++ b/component-model/src/language-support/using-http-in-components/dart.md @@ -0,0 +1,97 @@ +# Using HTTP in Dart Components + +> [!WARNING] +> Compiling Dart to non-web WebAssembly targets is experimental. +> This guide requires Dart version `3.14.0-251.0.dev` or later. + +### 1. Create a new Dart project + +Again, we'll start with a fresh Dart package: + +```sh +dart create -t cli dart_wasm_service +``` + +To configure this package for WebAssembly components, add tooling dependencies. +Additionally, the `wasi` package provides generated bindings to WASI definitions, +meaning that running `dart run wasm_tools witgen` won't be necessary. + +```sh +dart pub add wasi wasm_components dev:wasm_tools +``` + +### 2. Writing the HTTP handler + +The following code can be inserted into `bin/dart_wasm_service.dart`: + +```dart +import 'dart:async'; +import 'dart:convert'; + +import 'package:wasi/src/components/wasi_http_service.dart'; +import 'package:wasi/src/components/wasi_http.dart'; +import 'package:wasm_components/wasm_components.dart'; + +void main() { + serviceComponent((imports) => _RequestHandler(imports)); +} + +final class _RequestHandler(final ServiceImports _imports) implements Handler { + var _requestId = 0; + + @override + Future, TypesErrorCode>> handle({ + required Owned request, + }) async { + final headers = _imports.httpTypes.constructorFields(); + + final responseText = + ''' + + + + dart2wasm http server + + +

This website is running Dart!

+ +

+This is request number ${_requestId++} served by this server. +

+ + +'''; + + final (response, _) = _imports.httpTypes.staticResponseNew( + headers: headers, + contents: .some(.value(utf8.encode(responseText))), + trailers: Future.syncValue(.ok(.none)), + ); + + request.drop(); + return .ok(response); + } +} +``` + +### 3. Build the component + +To build the component, use `wasm_tools compile`: + +```sh +dart run wasm_tools compile bin/dart_wasm_service.dart +``` + +### 4. Serve the component with `wasmtime` + +To run your HTTP service component: + +```sh +wasmtime serve bin/dart_wasm_service.wasm -O pooling-max-tables-per-module=4 +``` + +> [!NOTE] +> +> The `pooling-max-tables-per-module` option is required since `wasmtime serve` restricts +> this by default, preventing Dart modules from running. +> `serve` is the only `wasmtime` subcommand with this restriction. diff --git a/component-model/src/using-http-in-components.md b/component-model/src/using-http-in-components.md index 9cf9523e..e0d80bfc 100644 --- a/component-model/src/using-http-in-components.md +++ b/component-model/src/using-http-in-components.md @@ -8,4 +8,5 @@ This guide is implemented for various languages: | Language | |--------------------------------------------------------| +| [Dart](./language-support/using-http-in-components/dart.md) | | [Rust](./language-support/using-http-in-components/rust.md) |