Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
beea627
feat(wasm): add experimental WIT parser and component model wrapper
metif12 Nov 9, 2026
fa6781a
feat(wasm): lift core exports into component model wrapper
metif12 Nov 9, 2026
8b5a85f
feat(wit): support attributes/includes and improve V codegen
metif12 Nov 9, 2026
611931a
style(wasm): format component.v
metif12 Nov 9, 2026
3845acc
docs(wit): add module README
metif12 Nov 9, 2026
5b9de14
test(wasm): cover component wrapping of exported function
metif12 Nov 9, 2026
f0d33d9
feat(wasm): add ComponentSection enum
metif12 Nov 9, 2026
f827bc5
test(wit): assert generated V bindings content
metif12 Nov 9, 2026
a2db40e
feat(wit): add document printer
metif12 Nov 9, 2026
cd089a6
feat(wit): handle use items in codegen
metif12 Nov 9, 2026
eda91a7
feat(wit): generate V types for variants and flags
metif12 Nov 9, 2026
86a97f1
docs(wasm): document component wrapper
metif12 Nov 9, 2026
94036ac
refactor(wasm): use ComponentSection enum in wrapper
metif12 Nov 9, 2026
9737ceb
docs(wasm): document component wrapper
metif12 Nov 9, 2026
40b37af
docs(wasm): document component wrapper
metif12 Nov 9, 2026
f823477
docs(wasm): document component wrapper
metif12 Nov 9, 2026
16329aa
docs(wasm): document component wrapper
metif12 Nov 9, 2026
fef7b8d
docs(wasm): document component wrapper
metif12 Nov 9, 2026
403ed38
docs(wasm): document component wrapper
metif12 Nov 9, 2026
c8753fb
docs(wasm): document component wrapper
metif12 Nov 9, 2026
333d152
docs(wasm): document component wrapper
metif12 Nov 9, 2026
d164bf3
docs(wasm): document component wrapper
metif12 Nov 9, 2026
4b65dae
docs(wasm): document component wrapper
metif12 Nov 9, 2026
788ec8c
docs/examples: add WIT codegen example
metif12 Nov 9, 2026
8d69932
docs(wasm): document component wrapper
metif12 Nov 9, 2026
922c6bc
docs(wasm): document component wrapper
metif12 Nov 9, 2026
39b068d
docs(wasm): document component wrapper
metif12 Nov 9, 2026
f505b9a
docs(wasm): document component wrapper
metif12 Nov 9, 2026
1c10577
docs(wasm): document component wrapper
metif12 Nov 9, 2026
b186d9a
docs(wasm): document component wrapper
metif12 Nov 9, 2026
5304ed3
docs(wasm): document component wrapper
metif12 Nov 9, 2026
4185985
docs(wasm): document component wrapper
metif12 Nov 9, 2026
5ed272a
docs(wasm): document component wrapper
metif12 Nov 9, 2026
e97af39
docs(wasm): document component wrapper
metif12 Nov 9, 2026
8f3ff37
docs(wasm): document component wrapper
metif12 Nov 9, 2026
4126c8b
docs(wasm): document component wrapper
metif12 Nov 9, 2026
01e91e1
docs(wasm): document component wrapper
metif12 Nov 9, 2026
52f0710
docs(wasm): document component wrapper
metif12 Nov 9, 2026
330dde5
docs(wasm): document component wrapper
metif12 Nov 9, 2026
ebd02b3
docs(wasm): document component wrapper
metif12 Nov 9, 2026
1a4be33
docs(wasm): document component wrapper
metif12 Nov 9, 2026
f5460ec
test(wit): cover codegen and printer output
metif12 Nov 9, 2026
0ff3c09
docs(wasm): document component wrapper
metif12 Nov 9, 2026
09e38e1
docs(wasm): document component wrapper
metif12 Nov 9, 2026
0085d20
docs(wasm): document component wrapper
metif12 Nov 9, 2026
961816a
feat(wasm): add memory/realloc opts to component canon lifting
metif12 Nov 9, 2026
deaaccf
docs(wasm): document component wrapper
metif12 Nov 9, 2026
a91f07a
docs(wasm): document component wrapper
metif12 Nov 9, 2026
02e9bcf
docs(wasm): document component wrapper
metif12 Nov 9, 2026
33827c7
docs(wasm): document component wrapper
metif12 Nov 9, 2026
ab0facd
feat(wasi): add preview1 helper module
metif12 Nov 9, 2026
2dae572
docs(wasm): document component wrapper
metif12 Nov 9, 2026
62ae29f
docs(wasm): document component wrapper
metif12 Nov 9, 2026
7d902d1
docs(wasm): document component wrapper
metif12 Nov 9, 2026
0ed233c
docs(wasm): document component wrapper
metif12 Nov 9, 2026
1a75aa5
feat(wit): print use items; add conformance matrix
metif12 Nov 9, 2026
4f44136
feat(wit): restore func codegen; map unknown types to voidptr; add WA…
metif12 Nov 9, 2026
31e17c0
chore: remove generated test artifacts
metif12 Nov 9, 2026
5b0c88d
chore: remove debug and conformance test artifacts
metif12 Nov 9, 2026
eabf22f
docs(wit): update conformance matrix for codegen
metif12 Nov 9, 2026
ce45950
feat(wasm): add cabi_realloc bump allocator for component ABI
metif12 Nov 9, 2026
a45ee43
feat(driver): add -component/-wit flags with native wrapping
metif12 Nov 9, 2026
5eeaebb
test(wasm): add builder memory/start section tests
metif12 Nov 9, 2026
029c2a9
feat(examples): add wasm component example with run.sh
metif12 Nov 9, 2026
1a9675b
feat(wit): add validation for unknown types and duplicate names
metif12 Nov 9, 2026
f105b74
cabi: step 3 - string param lifting structural test + CONFORMANCE update
metif12 Nov 9, 2026
e4f4a88
docs(wasm): document component wrapper
metif12 Nov 9, 2026
a2f5c9b
phase1: builder memory/start/import/table/element/data coverage
metif12 Nov 9, 2026
df2295e
phase2: add pref.wasm_cabi flag for canonical-ABI mode
metif12 Nov 9, 2026
1a582aa
phase3: add resource printing to WIT printer
metif12 Nov 9, 2026
2a174c2
phase4: add resource duplicate method validation
metif12 Nov 9, 2026
d118e35
phase5: add WASI preview2 example with run.sh
metif12 Nov 9, 2026
26f6c14
docs(wasm): document component wrapper
metif12 Nov 9, 2026
6f46f01
wasm: export cabi_realloc allocator
metif12 Nov 10, 2026
b125662
wasm: add WType.cabi_string variant and export-aware string detection
metif12 Nov 10, 2026
c68adf8
wasm: emit canonical-ABI string wrappers in SSAGen exports
metif12 Nov 10, 2026
126c451
wasm: WIT-aware string lifting in component wrapper
metif12 Nov 10, 2026
552a1af
wasm: native cabi parity test + WIT string-lift test
metif12 Nov 10, 2026
80f220b
wasm: node runtime proof for cabi string params + constructor printing
metif12 Nov 10, 2026
f9afee9
wasm: builder custom sections + coverage tests
metif12 Nov 10, 2026
83b71ba
wit: attributes in AST with verbatim printing + world func signatures
metif12 Nov 10, 2026
a22a555
wit: validation depth + grammar and preview2 binding tests
metif12 Nov 10, 2026
b80081b
wit: grammar world test, variant checks, container validation
metif12 Nov 10, 2026
0020b49
wasm: generalize cabi wrappers to multi-string params and string returns
metif12 Nov 10, 2026
e5a240d
wasm: generalize cabi wrappers to multi-string params and string returns
metif12 Nov 10, 2026
f8636a1
wit: include support with with-substitutions + package validation
metif12 Nov 10, 2026
9a47fdc
wit+wasm: tuples as arrays, forward-declared resources, socket bindings
metif12 Nov 10, 2026
191064a
docs: conformance reflects string returns and multi-string params
metif12 Nov 10, 2026
7bc41c9
wasm: clear error when emcc is missing for emscripten target
metif12 Nov 10, 2026
766bf0b
wasm: canonical-ABI layout helpers + enum/flags lift + documented lim…
metif12 Nov 10, 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
17 changes: 17 additions & 0 deletions cmd/tools/vwit_wrap.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import os
import wit
import wasm

fn main() {
if os.args.len < 5 {
println('usage: v run cmd/tools/vwit_wrap.v <core.wasm> <input.wit> <world> <out.component.wasm>')
return
}
core := os.read_file(os.args[1]) or { panic(err) }
src := os.read_file(os.args[2]) or { panic(err) }
doc := wit.parse(src) or { panic(err) }
out := wit.wrap_component(core.bytes(), doc, os.args[3])
os.write_file_array(os.args[4], out) or { panic(err) }
println('wrote ${os.args[4]} (${out.len} bytes)')
_ = wasm.is_component
}
15 changes: 15 additions & 0 deletions doc/wasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,18 @@ optimized SSA:
./v test vlib/v/gen/wasm/ssa_gen_test.v
V3_TEST_WASM=1 ./v vlib/v/compiler_tests/wasm_codegen_test.v
```

## Emscripten output (needs the Emscripten SDK)

The native backend above emits a standalone `.wasm` module and never invokes Emscripten.
If you instead need Emscripten's JavaScript shell or HTML page, target `wasm32_emscripten`
explicitly and compile the generated C with `emcc` from an activated
[Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) on your `PATH`:

```sh
./v -os wasm32_emscripten -cc emcc -o hello.html examples/hello_world.v
```

Without `emcc` on `PATH`, that combination fails with a message pointing back here.
Emitting only C (`-o hello.c`) never invokes a C compiler, so it works without Emscripten
installed.
17 changes: 17 additions & 0 deletions examples/wasm/component/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# V WebAssembly Component Example

## Build

```sh
v -b wasm -component -wit hello.wit hello.v -o hello.wasm
```

## Run

Requires `wasmtime`:

```sh
wasmtime run hello.component.wasm
```

If wasmtime is not installed, the build still succeeds but running is skipped.
5 changes: 5 additions & 0 deletions examples/wasm/component/hello.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module main

fn main() {
println('hello from V component')
}
5 changes: 5 additions & 0 deletions examples/wasm/component/hello.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package example:hello@0.1.0;

world hello {
export run: func();
}
9 changes: 9 additions & 0 deletions examples/wasm/component/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
set -e
V=${V:-v}
$V -b wasm -component -wit hello.wit hello.v -o hello.wasm
if command -v wasmtime >/dev/null 2>&1; then
wasmtime run hello.component.wasm
else
echo "wasmtime not found; skipping execution"
fi
4 changes: 3 additions & 1 deletion examples/wasm/playground/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ the V compiler and V's direct WebAssembly backend. No compilation server is need

Build V in the repository root (`make` if `./v` is missing, then `./v self`). Install and
activate the [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html), so
`emcc` is on your `PATH`. From the repository root, run:
`emcc` is on your `PATH`. (That SDK is only needed to compile the compiler itself into the
browser playground; ordinary `-b wasm` output never requires it.) From the repository root,
run:

```sh
sh examples/wasm/playground/build.sh
Expand Down
19 changes: 19 additions & 0 deletions examples/wasm/wasi_preview2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# WASI Preview 2 Example

Generated V bindings from WASI preview2 WIT files.

## Build

```sh
v -b wasm -component -wit hello.wit hello.v -o hello.wasm
```

## Run

Requires `wasmtime`:

```sh
wasmtime run hello.component.wasm
```

If wasmtime is not installed, the build still succeeds but running is skipped.
5 changes: 5 additions & 0 deletions examples/wasm/wasi_preview2/hello.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module main

fn main() {
println('hello from WASI preview2 component')
}
5 changes: 5 additions & 0 deletions examples/wasm/wasi_preview2/hello.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package example:hello@0.1.0;

world hello {
export run: func();
}
9 changes: 9 additions & 0 deletions examples/wasm/wasi_preview2/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
set -e
V=${V:-v}
$V -b wasm -component -wit hello.wit hello.v -o hello.wasm
if command -v wasmtime >/dev/null 2>&1; then
wasmtime run hello.component.wasm
else
echo "wasmtime not found; skipping execution"
fi
5 changes: 5 additions & 0 deletions examples/wasm_component/hello.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module main

fn main() {
println('hello from V wasm component')
}
9 changes: 9 additions & 0 deletions examples/wasm_component/hello.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package example:hello@0.1.0;

interface api {
run: func() -> string;
}

world hello {
export api;
}
12 changes: 12 additions & 0 deletions examples/wit/example.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package example:demo@0.1.0;

interface calc {
record point { x: f64, y: f64 }
add: func(a: s32, b: s32) -> s32;
scale: func(p: point, factor: f64) -> point;
}

world app {
import calc;
export run: func() -> string;
}
8 changes: 8 additions & 0 deletions examples/wit/main.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os
import wit

fn main() {
src := os.read_file('example.wit') or { panic(err) }
doc := wit.parse(src) or { panic(err) }
println(doc.to_v())
}
30 changes: 30 additions & 0 deletions vlib/v/compiler_tests/driver_cli_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -2236,6 +2236,36 @@ fn main() {
assert_driver_wasm_output(explicit_output)
}

fn test_emscripten_missing_emcc_suggests_native_wasm() {
root := os.join_path(os.vtmp_dir(), 'v3_driver_emscripten_toolchain_${os.getpid()}')
os.rmdir_all(root) or {}
os.mkdir_all(root) or { panic(err) }
defer {
os.rmdir_all(root) or {}
}
v3_bin := build_driver_cli_v3(root)
source := os.join_path(root, 'hello.v')
os.write_file(source, 'module main\n\nfn main() {\n\tprintln("hi")\n}\n') or {
panic(err)
}
if _ := os.find_abs_path_of_executable('emcc') {
println('skipping ${@FN}: emcc is on PATH, so the missing-toolchain guidance cannot trigger')
} else {
html_output := os.join_path(root, 'hello.html')
assert_driver_cli_failure(v3_bin, ['-os', 'wasm32_emscripten', '-cc', 'emcc', '-o',
html_output, source], 'was not found')
missing := cmdexec.run(v3_bin, ['-os', 'wasm32_emscripten', '-cc', 'emcc', '-o', html_output,
source])
assert missing.output.contains('Emscripten SDK'), missing.output
assert missing.output.contains('v -b wasm'), missing.output
}
// Emitting only C never invokes a C compiler, so it must work without emcc.
c_output := os.join_path(root, 'hello_emscripten.c')
c_only := cmdexec.run(v3_bin, ['-os', 'wasm32_emscripten', '-o', c_output, source])
assert c_only.exit_code == 0, c_only.output
assert os.is_file(c_output)
}

fn test_driver_rejects_invalid_cli_and_parses_vmod_subdirs() {
root := os.join_path(os.vtmp_dir(), 'v3_driver_cli_${os.getpid()}')
os.rmdir_all(root) or {}
Expand Down
63 changes: 63 additions & 0 deletions vlib/v/driver/driver.v
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ $if !skip_ssa ? {

$if !skip_wasm ? {
import v.gen.wasm as wasmgen
import wit
}
// vfmt on

Expand Down Expand Up @@ -10059,6 +10060,8 @@ pub fn run(args []string) {
mut is_livemain := false
mut is_liveshared := false
mut hot_fn := ''
mut component_flag := false
mut wit_file_path := ''
mut subsystem := pref.Subsystem.auto
mut icon_path := ''
mut is_strict := false
Expand Down Expand Up @@ -10236,6 +10239,12 @@ pub fn run(args []string) {
backend = normalize_backend_name(args[i].all_after('='))
backend_explicit = !backend_from_project
i++
} else if args[i] == '-component' {
component_flag = true
i++
} else if args[i] == '-wit' && i + 1 < args.len {
wit_file_path = args[i + 1]
i += 2
} else if args[i] == '-cross' {
// A modifier, not a target: it combines with `-os`/`-cc`, as in
// `v -cross -os windows -cc msvc`. See `v help build-c`.
Expand Down Expand Up @@ -11278,6 +11287,21 @@ pub fn run(args []string) {
// The executable suffix does not belong to the requested generated C path.
bin_file = target_bin_file
}
if backend in ['c', 'fastc'] && target.os == 'wasm32_emscripten' && !c_only {
// The emscripten target links through emcc, so fail here with guidance
// instead of a generic missing-executable error later. Emitting only C
// (`-o file.c`) never reaches this point, so it stays toolchain-free.
cc_base := os.file_name(c_compiler).to_lower()
if c_compiler_explicit && cc_base in ['emcc', 'emcc.bat', 'em++', 'em++.bat'] {
if _ := os.find_abs_path_of_executable(c_compiler) {
} else {
eprintln('error: cannot build for target `wasm32_emscripten`: C compiler `${c_compiler}` was not found.')
eprintln('Install the Emscripten SDK (https://emscripten.org/docs/getting_started/downloads.html) and activate it so `emcc` is on PATH,')
eprintln('or build a native WebAssembly module instead, which needs no Emscripten: `v -b wasm -o out.wasm input.v`.')
exit(1)
}
}
}
validate_windows_icon_option(icon_path, target.os, backend, is_shared, is_o, c_only,
generate_c_project) or {
eprintln(err.msg())
Expand Down Expand Up @@ -11422,6 +11446,19 @@ pub fn run(args []string) {
eprintln('fastc-phase driver.prefs ${driver_sw.elapsed().microseconds()}us')
}
prefs.target = target
prefs.component = component_flag
prefs.wit_file = wit_file_path
prefs.wasm_cabi = component_flag || 'wasm_cabi' in user_defines
if prefs.component {
if prefs.wit_file == '' {
eprintln('error: -component requires -wit <file.wit>')
exit(1)
}
if backend != 'wasm' {
eprintln('error: -component requires -b wasm')
exit(1)
}
}
prefs.output_cross_c = output_cross_c
prefs.thread_stack_size = if thread_stack_size_set {
thread_stack_size
Expand Down Expand Up @@ -14801,6 +14838,7 @@ pub fn run(args []string) {
}
m.release_codegen_analysis_metadata()
mut g := wasmgen.SSAGen.new(m)
g.set_wasm_cabi(prefs.wasm_cabi || prefs.component)
mut main_fn := config.main_fn
mut exports := config.exports.clone()
if main_fn.len == 0 {
Expand All @@ -14823,6 +14861,31 @@ pub fn run(args []string) {
eprintln('error writing ${output_file}')
exit(1)
}
if prefs.component {
// Invoke WIT parser + component wrapper (native flow).
wit_src := os.read_file(prefs.wit_file) or {
eprintln('error reading wit file ${prefs.wit_file}')
exit(1)
}
doc := wit.parse(wit_src) or {
eprintln('error parsing wit file ${prefs.wit_file}: ${err}')
exit(1)
}
core := os.read_file(output_file) or {
eprintln('error reading ${output_file}')
exit(1)
}
// Component output path
comp_out := output_file.all_before_last('.wasm') + '.component.wasm'
// Wrap with WIT-aware canonical lifting (string params lift via
// memory + realloc); falls back to plain wrapping.
wrapped := wit.wrap_component(core.bytes(), doc, '')
os.write_file_array(comp_out, wrapped) or {
eprintln('error writing ${comp_out}')
exit(1)
}
eprintln('component: wrote ${comp_out}')
}
for warning in g.warnings_list() {
eprintln('wasm: ${warning}')
}
Expand Down
Loading
Loading