Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
efb9c0c
refactor: extract file writing
peter-jerry-ye Dec 23, 2025
65c9875
fix: written but never read
peter-jerry-ye Dec 23, 2025
a52cb69
refactor: extract more from finish
peter-jerry-ye Dec 23, 2025
d3c073d
doc: add some documentation
peter-jerry-ye Dec 23, 2025
ad36cfa
fix: use pkg_resolver directly
peter-jerry-ye Dec 23, 2025
1bc9abc
refactor: rename fields
peter-jerry-ye Dec 23, 2025
510ebff
refactor: extract small functions
peter-jerry-ye Dec 23, 2025
17b6107
refactor: use resolve for name mangling
peter-jerry-ye Dec 23, 2025
b51032e
refactor: organize code better
peter-jerry-ye Dec 23, 2025
0d864f6
fix: use callback instead of stackful
peter-jerry-ye Dec 23, 2025
2edd0d2
refactor: simplify optionlift
peter-jerry-ye Dec 24, 2025
2cbeaba
refactor: remove func name passing
peter-jerry-ye Dec 24, 2025
3b09601
refactor: move LiftArgsLowerResults to each pkg
peter-jerry-ye Dec 24, 2025
96c5c1f
feat: hide generated functions from interface
peter-jerry-ye Dec 25, 2025
7120dcc
feat: add new async pkg
peter-jerry-ye Dec 22, 2025
5df7b08
feat(wip): simple-call-import
peter-jerry-ye Dec 23, 2025
99d036b
wip: add runner
peter-jerry-ye Dec 24, 2025
3594c6b
feat: hide generated functions
peter-jerry-ye Dec 25, 2025
f2474b0
feat(wip): simple future
peter-jerry-ye Dec 25, 2025
c55e659
feat: future-cancel-read
peter-jerry-ye Dec 25, 2025
eeb5810
feat: future-cancel-write
peter-jerry-ye Dec 25, 2025
0d30f2d
fix: use async correctly
peter-jerry-ye Jan 7, 2026
92455b5
feat: call async import
peter-jerry-ye Jan 15, 2026
765e28e
feat: implement future/stream lowering and async cleanup
peter-jerry-ye Jan 20, 2026
8901c74
test: add tests for future/stream lowering
peter-jerry-ye Jan 20, 2026
3b7944d
feat: add direction-aware type generation for exports
peter-jerry-ye Jan 21, 2026
1f3cbb1
fix: FFI function signatures and export module prefix
peter-jerry-ye Jan 21, 2026
f040b60
feat: make write/close async in StreamW and improve visibility
peter-jerry-ye Jan 21, 2026
481acec
test: update tests for new OutFuture/OutStream return types
peter-jerry-ye Jan 21, 2026
db041f9
feat: add TaskGroup argument to exported async functions
peter-jerry-ye Jan 21, 2026
5817bed
fix: use correct TaskGroup type parameter for async exports
peter-jerry-ye Jan 21, 2026
17260ca
fix: use correct taskgroup
peter-jerry-ye Jan 23, 2026
9b55bb5
test: moonbit should_fail_verify on error_context
peter-jerry-ye Jan 26, 2026
94f5926
moonbit: implement DropHandle and remove type stubs
peter-jerry-ye Jan 26, 2026
dc849d4
moonbit: document test commands
peter-jerry-ye Jan 26, 2026
62babf3
moonbit: fix async future/stream support
peter-jerry-ye Feb 2, 2026
84dceff
moonbit: adapt async params and fixed-length list codegen
peter-jerry-ye Feb 27, 2026
132c658
moonbit: rework async future/stream bindings
peter-jerry-ye Feb 3, 2026
4b81773
moonbit: add crate README with test commands
peter-jerry-ye Feb 3, 2026
c525ae8
moonbit: add local future/stream batches
peter-jerry-ye Feb 5, 2026
e3d4d5c
moonbit: use Cancelled for local future
peter-jerry-ye Feb 5, 2026
0fec8a5
moonbit: align async intrinsic modules and async export wrappers
peter-jerry-ye Feb 11, 2026
7e0117e
moonbit: fix runtime harness for new moon outputs and async export me…
peter-jerry-ye Feb 12, 2026
60e9bdf
moonbit: add runner.mbt coverage for async runtime cases
peter-jerry-ye Feb 12, 2026
9ed4044
moonbit: remove unused codegen constants and enum
peter-jerry-ye Feb 12, 2026
d8febda
moonbit: add async runtime test implementations
peter-jerry-ye Feb 13, 2026
d85a8d8
moonbit: fix async future-write temp ptr and make embed encoding conf…
peter-jerry-ye Feb 14, 2026
d3effd9
moonbit: fix async cancel-import deadlock in stackless runtime
peter-jerry-ye Feb 27, 2026
82bbfb6
moonbit: avoid stackless yield-loop starvation while preserving cance…
peter-jerry-ye Feb 27, 2026
6f443f8
moonbit: dedupe fixed-length list instruction handlers
peter-jerry-ye Feb 27, 2026
ad800f8
moonbit: replace global async spawner with coroutine-local state
peter-jerry-ye Feb 27, 2026
f42744b
moonbit: split Future and CMFuture, add nested future coverage
peter-jerry-ye Feb 27, 2026
55c26ab
moonbit: split Stream and CMStream for async ABI safety
peter-jerry-ye Feb 27, 2026
18a17da
moonbit: reduce async template warnings and clean generated stubs
peter-jerry-ye Feb 27, 2026
5a0511c
moonbit: make cleanup list demand-driven
peter-jerry-ye Mar 3, 2026
3f338a8
fix(moonbit): handle fixed-length lists and cleanup builtins
peter-jerry-ye Mar 3, 2026
518ea22
refactor(moonbit): emit builtins from explicit ffi usage
peter-jerry-ye Mar 3, 2026
e859dd7
fix(moonbit): make ffi builtin collection explicit and leak-free
peter-jerry-ye Mar 3, 2026
c3ad018
chore: run fmt and fix clippy in moonbit test tooling
peter-jerry-ye Mar 5, 2026
434265d
chore(moonbit): drop unused ffi files and keep utf16 embedding
peter-jerry-ye Mar 5, 2026
09bc25e
test(runtime-async): add rust test components for moonbit future/stre…
peter-jerry-ye Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/moonbit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ description = """
MoonBit bindings generator for WIT and the component model, typically used
through the `wit-bindgen-cli` crate.
"""
readme = "README.md"

[lints]
workspace = true
Expand Down
68 changes: 68 additions & 0 deletions crates/moonbit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# `wit-bindgen-moonbit`

MoonBit language bindings generator for WIT and the Component Model.

## Usage

Generate bindings via the `moonbit` subcommand:

```bash
wit-bindgen moonbit [OPTIONS] <WIT>
```

See `wit-bindgen help moonbit` for available options.

## Local async usage

For pure MoonBit code (no FFI), you can create local future/stream pairs.

Future + Promise:

```mbt
let (f, p) = @async.Future::new[Int]()
@async.spawn(async fn() { p.write(42) })
let value = f.get()
```

Stream + Sink (batched reads/writes):

```mbt
let (s, sink) = @async.Stream::new[Byte]()
@async.spawn(async fn() {
let chunk : Array[Byte] = [1, 2, 3, 4]
let _ = sink.write(chunk[:])
sink.close()
})
let chunk = s.read(4096)
match chunk {
None => ()
Some(bytes) => {
let _ = bytes.length()
}
}
```

`Stream::read(count)` returns up to `count` elements; `Sink::write` accepts
`ArrayView[T]` so byte streams can batch data efficiently. `Stream::new`
accepts an optional `capacity` (<= 0 means unbounded).

## Testing

From the repo root, run the MoonBit codegen tests:

```bash
cargo run test \
--languages rust,moonbit \
--artifacts target/artifacts \
--rust-wit-bindgen-path ./crates/guest-rust \
tests/codegen
```

And the async runtime tests (requires an async component-model runner):

```bash
cargo run test --languages rust,moonbit tests/runtime-async \
--artifacts target/artifacts \
--rust-wit-bindgen-path ./crates/guest-rust \
--runner "wasmtime -W component-model-async"
```
266 changes: 266 additions & 0 deletions crates/moonbit/src/async/async_abi.mbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
// #region subtask

///|
priv struct SubTask {
handle : Int
state : SubTaskState
}

///|
priv enum SubTaskState {
Starting = 0
Started = 1
Returned = 2
Cancelled_before_started = 3
Cancelled_before_returned = 4
}

///|
fn SubTaskState::from(int : Int) -> SubTaskState {
match int {
0 => Starting
1 => Started
2 => Returned
3 => Cancelled_before_started
4 => Cancelled_before_returned
_ => panic()
}
}

///|
fn SubTask::from(code : Int) -> SubTask {
{ handle: code >> 4, state: SubTaskState::from(code & 0xf) }
}

///|
/// None : the subtask is blocked
fn SubTask::cancel(self : SubTask) -> SubTaskState? {
let result = subtask_cancel(self.handle)
if result == -1 {
None
} else {
Some(SubTaskState::from(subtask_cancel(self.handle)))
}
}

// #endregion

// #region events

///|
priv enum EventCode {
None = 0
SubTask = 1
StreamRead = 2
StreamWrite = 3
FutureRead = 4
FutureWrite = 5
TaskCancelled = 6
}

///|
fn EventCode::from(int : Int) -> EventCode {
match int {
0 => EventCode::None
1 => EventCode::SubTask
2 => EventCode::StreamRead
3 => EventCode::StreamWrite
4 => EventCode::FutureRead
5 => EventCode::FutureWrite
6 => EventCode::TaskCancelled
_ => panic()
}
}

///|
priv enum Events {
None
Subtask(Int, SubTaskState)
StreamRead(Int, StreamResult)
StreamWrite(Int, StreamResult)
FutureRead(Int, FutureReadResult)
FutureWrite(Int, FutureWriteResult)
TaskCancelled
}

///|
fn Events::new(code : EventCode, i : Int, j : Int) -> Events {
match code {
None => None
SubTask => Subtask(i, SubTaskState::from(j))
StreamRead => StreamRead(i, StreamResult::from(j))
StreamWrite => StreamWrite(i, StreamResult::from(j))
FutureRead => FutureRead(i, FutureReadResult::from(j))
FutureWrite => FutureWrite(i, FutureWriteResult::from(j))
TaskCancelled => TaskCancelled
}
}

// #endregion

// #region waitable set

///|
struct WaitableSet(Int) derive(Eq, Show, Hash)

///|
fn WaitableSet::new() -> WaitableSet {
WaitableSet(waitable_set_new())
}

///|
fn WaitableSet::drop(self : Self) -> Unit {
waitable_set_drop(self.0)
}

// #endregion

// #region Future

///|
priv enum FutureReadResult {
Completed = 0
Cancelled = 2
}

///|
fn FutureReadResult::from(int : Int) -> FutureReadResult {
match int {
0 => Completed
2 => Cancelled
_ => panic()
}
}

///|
priv enum FutureWriteResult {
Completed = 0
Dropped = 1
Cancelled = 2
}

///|
fn FutureWriteResult::from(int : Int) -> FutureWriteResult {
match int {
0 => Completed
1 => Dropped
2 => Cancelled
_ => panic()
}
}

// #endregion

// #region Stream

///|
priv struct StreamResult {
progress : Int
copy_result : CopyResult
}

///|
fn StreamResult::from(int : Int) -> StreamResult {
let progress = int >> 4
let copy_result = CopyResult::from(int & 0xf)
{ progress, copy_result }
}

///|
priv enum CopyResult {
Completed = 0
Dropped = 1
Cancelled = 2
}

///|
fn CopyResult::from(int : Int) -> CopyResult {
match int {
0 => Completed
1 => Dropped
2 => Cancelled
_ => panic()
}
}

// #endregion

// #region callback code

///|
/// Code to let the runtime know what to do in the callback
priv enum CallbackCode {
Completed
Yield
Wait(WaitableSet)
Poll(WaitableSet)
}

///|
fn CallbackCode::encode(self : Self) -> Int {
match self {
Completed => 0
Yield => 1
Wait(id) => 2 | (id.0 << 4)
Poll(id) => 3 | (id.0 << 4)
}
}

///|
fn CallbackCode::_decode(int : Int) -> CallbackCode {
let id = int >> 4
match int & 0xf {
0 => Completed
1 => Yield
2 => Wait(id)
3 => Poll(id)
_ => panic()
}
}

// #endregion

// #region Component async primitives

///|
/// Return whether is cancelled.
/// Use for non-callback implementation.
fn _yield() -> Bool = "$root" "[cancellable][yield]"

///|
pub fn backpressure_inc() = "$root" "[backpressure-inc]"

///|
pub fn backpressure_dec() = "$root" "[backpressure-dec]"

///|
fn subtask_cancel(id : Int) -> Int = "$root" "[subtask-cancel]"

///|
fn subtask_drop(id : Int) = "$root" "[subtask-drop]"

///|
pub fn context_set(task : Int) = "$root" "[context-set-0]"

///|
pub fn context_get() -> Int = "$root" "[context-get-0]"

///|
fn tls_set(tls : Int) = "$root" "[context-set-0]"

///|
fn tls_get() -> Int = "$root" "[context-get-0]"

///|
pub fn task_cancel() = "[export]$root" "[task-cancel]"

///|
fn waitable_set_new() -> Int = "$root" "[waitable-set-new]"

///|
fn waitable_set_drop(set : Int) = "$root" "[waitable-set-drop]"

///|
fn waitable_join(waitable : Int, set : Int) = "$root" "[waitable-join]"

// #endregion
21 changes: 21 additions & 0 deletions crates/moonbit/src/async/async_primitive.mbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
async fn[T] async_suspend(
cb : ((T) -> Unit, (Cancelled) -> Unit) -> Unit,
) -> T raise Cancelled = "%async.suspend"

///|
fn run_async(f : async () -> Unit noraise) = "%async.run"
Loading
Loading