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
09789d9
refactor: extract file writing
peter-jerry-ye Dec 23, 2025
e8a3977
fix: written but never read
peter-jerry-ye Dec 23, 2025
4c74e9a
refactor: extract more from finish
peter-jerry-ye Dec 23, 2025
c708051
doc: add some documentation
peter-jerry-ye Dec 23, 2025
aca7674
fix: use pkg_resolver directly
peter-jerry-ye Dec 23, 2025
78569c0
refactor: rename fields
peter-jerry-ye Dec 23, 2025
4eecfd1
refactor: extract small functions
peter-jerry-ye Dec 23, 2025
890cdb6
refactor: use resolve for name mangling
peter-jerry-ye Dec 23, 2025
b8be186
refactor: organize code better
peter-jerry-ye Dec 23, 2025
6c6132c
fix: use callback instead of stackful
peter-jerry-ye Dec 23, 2025
850151e
refactor: simplify optionlift
peter-jerry-ye Dec 24, 2025
2761e08
refactor: remove func name passing
peter-jerry-ye Dec 24, 2025
c9f4d61
refactor: move LiftArgsLowerResults to each pkg
peter-jerry-ye Dec 24, 2025
c2ba352
feat: hide generated functions from interface
peter-jerry-ye Dec 25, 2025
f150ef8
feat: add new async pkg
peter-jerry-ye Dec 22, 2025
d80e390
feat(wip): simple-call-import
peter-jerry-ye Dec 23, 2025
354b57b
wip: add runner
peter-jerry-ye Dec 24, 2025
e4aad8c
feat: hide generated functions
peter-jerry-ye Dec 25, 2025
6a77c85
feat(wip): simple future
peter-jerry-ye Dec 25, 2025
0a7ab44
feat: future-cancel-read
peter-jerry-ye Dec 25, 2025
9d702f4
feat: future-cancel-write
peter-jerry-ye Dec 25, 2025
d23a3db
fix: use async correctly
peter-jerry-ye Jan 7, 2026
7a433ab
feat: call async import
peter-jerry-ye Jan 15, 2026
e2f6c83
feat: implement future/stream lowering and async cleanup
peter-jerry-ye Jan 20, 2026
824ca79
test: add tests for future/stream lowering
peter-jerry-ye Jan 20, 2026
08aa5b5
feat: add direction-aware type generation for exports
peter-jerry-ye Jan 21, 2026
e82f766
fix: FFI function signatures and export module prefix
peter-jerry-ye Jan 21, 2026
cc19fa9
feat: make write/close async in StreamW and improve visibility
peter-jerry-ye Jan 21, 2026
8542570
test: update tests for new OutFuture/OutStream return types
peter-jerry-ye Jan 21, 2026
7f2a46e
feat: add TaskGroup argument to exported async functions
peter-jerry-ye Jan 21, 2026
56306cf
fix: use correct TaskGroup type parameter for async exports
peter-jerry-ye Jan 21, 2026
055e135
fix: use correct taskgroup
peter-jerry-ye Jan 23, 2026
1b4d79b
test: moonbit should_fail_verify on error_context
peter-jerry-ye Jan 26, 2026
fd2fade
moonbit: implement DropHandle and remove type stubs
peter-jerry-ye Jan 26, 2026
98e9039
moonbit: document test commands
peter-jerry-ye Jan 26, 2026
40bbfee
moonbit: fix async future/stream support
peter-jerry-ye Feb 2, 2026
c28f008
moonbit: adapt async params and fixed-length list codegen
peter-jerry-ye Feb 27, 2026
4ce061d
moonbit: rework async future/stream bindings
peter-jerry-ye Feb 3, 2026
7b8ba7a
moonbit: add crate README with test commands
peter-jerry-ye Feb 3, 2026
881ef87
moonbit: add local future/stream batches
peter-jerry-ye Feb 5, 2026
f603390
moonbit: use Cancelled for local future
peter-jerry-ye Feb 5, 2026
e14b1b3
moonbit: align async intrinsic modules and async export wrappers
peter-jerry-ye Feb 11, 2026
9c38c5b
moonbit: fix runtime harness for new moon outputs and async export me…
peter-jerry-ye Feb 12, 2026
0688bf3
moonbit: add runner.mbt coverage for async runtime cases
peter-jerry-ye Feb 12, 2026
2f0c75e
moonbit: remove unused codegen constants and enum
peter-jerry-ye Feb 12, 2026
5f4c78b
moonbit: add async runtime test implementations
peter-jerry-ye Feb 13, 2026
7fe6528
moonbit: fix async future-write temp ptr and make embed encoding conf…
peter-jerry-ye Feb 14, 2026
8d822b4
moonbit: fix async cancel-import deadlock in stackless runtime
peter-jerry-ye Feb 27, 2026
a53d135
moonbit: avoid stackless yield-loop starvation while preserving cance…
peter-jerry-ye Feb 27, 2026
3b62994
moonbit: dedupe fixed-length list instruction handlers
peter-jerry-ye Feb 27, 2026
5fade41
moonbit: replace global async spawner with coroutine-local state
peter-jerry-ye Feb 27, 2026
e6ae477
moonbit: split Future and CMFuture, add nested future coverage
peter-jerry-ye Feb 27, 2026
aba0b31
moonbit: split Stream and CMStream for async ABI safety
peter-jerry-ye Feb 27, 2026
c6bb75d
moonbit: reduce async template warnings and clean generated stubs
peter-jerry-ye Feb 27, 2026
571078b
moonbit: make cleanup list demand-driven
peter-jerry-ye Mar 3, 2026
847871f
fix(moonbit): handle fixed-length lists and cleanup builtins
peter-jerry-ye Mar 3, 2026
d27e2db
refactor(moonbit): emit builtins from explicit ffi usage
peter-jerry-ye Mar 3, 2026
59bf630
fix(moonbit): make ffi builtin collection explicit and leak-free
peter-jerry-ye Mar 3, 2026
0b0968c
chore: run fmt and fix clippy in moonbit test tooling
peter-jerry-ye Mar 5, 2026
d14f651
chore(moonbit): drop unused ffi files and keep utf16 embedding
peter-jerry-ye Mar 5, 2026
90a9740
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