feature: WASI preview1 support, per-context isolation & glue API
Implement full WASI snapshot_preview1 with 46 registered host functions, per-context hook/registry isolation, and a single-unit public API. WASI Preview1 (15 new handler units): - Tier 1: fd_write, fd_read, fd_close, fd_seek, fd_prestat_get, fd_prestat_dir_name, fd_fdstat_get, proc_exit, environ_*, args_* - Tier 2: clock_time_get, clock_res_get, random_get - Tier 3: 14 fd_* stubs, 10 path_* stubs (ENOSYS) - Tier 4: poll_oneoff, proc_raise, sched_yield, 4 sock_* stubs Per-context isolation: - Move WASIHooks (12 hook table) and HostFuncRegistry onto TWASMProcessContext — each context is fully independent - Dynamic registry: starts at 16 entries, doubles on demand, no cap - All 15 preview1 handlers read hooks from context, not globals Glue API (wasm.pas): - Single entry point: wasm_init, wasm_load, wasm_set_*, wasm_register_wasi_preview1, wasm_register_host_func, wasm_resolve_imports, wasm_start, wasm_prepare_start, wasm_tick, wasm_dump_state - WASURO.lpr simplified to use only the wasm unit Refactoring: - Rename leb128.pas -> wasm.types.leb128.pas (updates 40+ opcode units) - New wasm.types.wasi.pas with WASI errno/filetype constants - New wasm.types.context.pas fields for hooks + registry types - New wasm.vm.setup unit (find_start, count_func_imports) - New wasi.emu.hooks for emulator-side hook implementations - Add read_uint16, write_uint16 to wasm.types.heap Testing: - 10 new test units: hooks, registry, glue, stubs, clock, random, extensibility, call, types, vm.setup (1087 tests / 232 units) - E2E WAT test runner (wat.ps1) with 9 integration tests: hello, exit42, multiwrite, prestat, fdstat, environ, clock, clockres, random 121 files changed, 5391 insertions(+), 267 deletions(-)
This commit is contained in:
1 parent
378f999143
commit
302dcb66f0
121 files changed
+5394
-270
No files matched your search
@@ -0,0 +1,24 @@
|
||||
;; clock.wat — WASI clock_time_get test
|
||||
;; Calls clock_time_get for realtime clock, verifies ESUCCESS (exit 0)
|
||||
;; @expect-exit 0
|
||||
(module
|
||||
(import "wasi_snapshot_preview1" "clock_time_get"
|
||||
(func $clock_time_get (param i32 i64 i32) (result i32)))
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
(memory (export "memory") 1)
|
||||
|
||||
(func (export "_start")
|
||||
(local $errno i32)
|
||||
;; clock_time_get(clock_id=0 [realtime], precision=1000, time_ptr=0)
|
||||
(local.set $errno
|
||||
(call $clock_time_get
|
||||
(i32.const 0) ;; CLOCK_REALTIME
|
||||
(i64.const 1000) ;; precision
|
||||
(i32.const 0))) ;; time output pointer
|
||||
|
||||
;; Exit with errno (should be 0 = ESUCCESS)
|
||||
(call $proc_exit (local.get $errno))
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
;; clockres.wat — WASI clock_res_get test
|
||||
;; Calls clock_res_get for realtime clock, verifies ESUCCESS (exit 0)
|
||||
;; @expect-exit 0
|
||||
(module
|
||||
(import "wasi_snapshot_preview1" "clock_res_get"
|
||||
(func $clock_res_get (param i32 i32) (result i32)))
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
(memory (export "memory") 1)
|
||||
|
||||
(func (export "_start")
|
||||
(local $errno i32)
|
||||
;; clock_res_get(clock_id=0 [realtime], resolution_ptr=0)
|
||||
(local.set $errno
|
||||
(call $clock_res_get
|
||||
(i32.const 0) ;; CLOCK_REALTIME
|
||||
(i32.const 0))) ;; resolution output pointer
|
||||
|
||||
;; Exit with errno (should be 0 = ESUCCESS)
|
||||
(call $proc_exit (local.get $errno))
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
;; environ.wat — WASI environ test
|
||||
;; Calls environ_sizes_get, expects count=0 and buf_size=0.
|
||||
;;
|
||||
;; @expect-exit 0
|
||||
|
||||
(module
|
||||
(import "wasi_snapshot_preview1" "environ_sizes_get"
|
||||
(func $environ_sizes_get (param i32 i32) (result i32)))
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
(memory (export "memory") 1)
|
||||
|
||||
(func $main (export "_start")
|
||||
(local $errno i32)
|
||||
|
||||
;; environ_sizes_get(count_ptr=0, buf_size_ptr=4)
|
||||
(local.set $errno
|
||||
(call $environ_sizes_get (i32.const 0) (i32.const 4)))
|
||||
|
||||
;; Should return ESUCCESS=0
|
||||
(if (i32.ne (local.get $errno) (i32.const 0))
|
||||
(then (call $proc_exit (i32.const 1))))
|
||||
|
||||
;; count at offset 0 should be 0
|
||||
(if (i32.ne (i32.load (i32.const 0)) (i32.const 0))
|
||||
(then (call $proc_exit (i32.const 2))))
|
||||
|
||||
;; buf_size at offset 4 should be 0
|
||||
(if (i32.ne (i32.load (i32.const 4)) (i32.const 0))
|
||||
(then (call $proc_exit (i32.const 3))))
|
||||
|
||||
;; All good
|
||||
(call $proc_exit (i32.const 0))
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
;; exit42.wat — WASI proc_exit test
|
||||
;; Calls proc_exit(42) immediately. Verify ExitCode=42.
|
||||
;;
|
||||
;; @expect-exit 42
|
||||
|
||||
(module
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
(memory (export "memory") 1)
|
||||
|
||||
(func $main (export "_start")
|
||||
(call $proc_exit (i32.const 42))
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
;; fdstat.wat — WASI fd_fdstat_get test
|
||||
;; Calls fd_fdstat_get for stdout (fd=1), expects ESUCCESS.
|
||||
;; Checks that filetype byte = 2 (CHARACTER_DEVICE).
|
||||
;;
|
||||
;; @expect-exit 0
|
||||
|
||||
(module
|
||||
(import "wasi_snapshot_preview1" "fd_fdstat_get"
|
||||
(func $fd_fdstat_get (param i32 i32) (result i32)))
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
(memory (export "memory") 1)
|
||||
|
||||
(func $main (export "_start")
|
||||
(local $errno i32)
|
||||
|
||||
;; fd_fdstat_get(fd=1, buf=0) — write fdstat struct at offset 0
|
||||
(local.set $errno
|
||||
(call $fd_fdstat_get (i32.const 1) (i32.const 0)))
|
||||
|
||||
;; Should return ESUCCESS=0
|
||||
(if (i32.ne (local.get $errno) (i32.const 0))
|
||||
(then (call $proc_exit (i32.const 1))))
|
||||
|
||||
;; First byte (filetype) should be 2 (CHARACTER_DEVICE)
|
||||
(if (i32.ne (i32.load8_u (i32.const 0)) (i32.const 2))
|
||||
(then (call $proc_exit (i32.const 2))))
|
||||
|
||||
;; All good
|
||||
(call $proc_exit (i32.const 0))
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
;; hello.wat — WASI "Hello, World!" test program
|
||||
;; Writes "Hello, World!\n" to stdout via fd_write, then exits with code 0.
|
||||
;;
|
||||
;; @expect-exit 0
|
||||
;; @expect-stdout Hello, World!
|
||||
|
||||
(module
|
||||
;; Import fd_write from WASI
|
||||
(import "wasi_snapshot_preview1" "fd_write"
|
||||
(func $fd_write (param i32 i32 i32 i32) (result i32)))
|
||||
|
||||
;; Import proc_exit from WASI
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
;; 1 page of linear memory (64KB), exported as "memory"
|
||||
(memory (export "memory") 1)
|
||||
|
||||
;; "Hello, World!\n" at offset 8
|
||||
(data (i32.const 8) "Hello, World!\n")
|
||||
|
||||
;; iovec at offset 0: { buf_ptr=8, buf_len=14 }
|
||||
(data (i32.const 0) "\08\00\00\00" ;; buf ptr = 8
|
||||
"\0e\00\00\00") ;; buf len = 14
|
||||
|
||||
(func $main (export "_start")
|
||||
;; fd_write(stdout=1, iovs=0, iovs_len=1, nwritten=100)
|
||||
(call $fd_write
|
||||
(i32.const 1) ;; fd = stdout
|
||||
(i32.const 0) ;; iovs pointer
|
||||
(i32.const 1) ;; iovs count
|
||||
(i32.const 100) ;; nwritten pointer
|
||||
)
|
||||
drop ;; discard errno
|
||||
|
||||
;; proc_exit(0)
|
||||
(call $proc_exit (i32.const 0))
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
;; multiwrite.wat — WASI fd_write with multiple iovecs
|
||||
;; Writes "AB" then "CD" as two separate iovecs in one fd_write call.
|
||||
;;
|
||||
;; @expect-exit 0
|
||||
;; @expect-stdout ABCD
|
||||
|
||||
(module
|
||||
(import "wasi_snapshot_preview1" "fd_write"
|
||||
(func $fd_write (param i32 i32 i32 i32) (result i32)))
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
(memory (export "memory") 1)
|
||||
|
||||
;; String data
|
||||
(data (i32.const 100) "AB") ;; 2 bytes at offset 100
|
||||
(data (i32.const 110) "CD") ;; 2 bytes at offset 110
|
||||
|
||||
;; iovec array at offset 0 (2 entries, 8 bytes each)
|
||||
;; iovec[0] = { ptr=100, len=2 }
|
||||
(data (i32.const 0) "\64\00\00\00" ;; buf ptr = 100
|
||||
"\02\00\00\00") ;; buf len = 2
|
||||
;; iovec[1] = { ptr=110, len=2 }
|
||||
(data (i32.const 8) "\6e\00\00\00" ;; buf ptr = 110
|
||||
"\02\00\00\00") ;; buf len = 2
|
||||
|
||||
(func $main (export "_start")
|
||||
;; fd_write(stdout=1, iovs=0, iovs_len=2, nwritten=200)
|
||||
(call $fd_write
|
||||
(i32.const 1) ;; fd = stdout
|
||||
(i32.const 0) ;; iovs pointer
|
||||
(i32.const 2) ;; iovs count = 2
|
||||
(i32.const 200) ;; nwritten pointer
|
||||
)
|
||||
drop
|
||||
|
||||
(call $proc_exit (i32.const 0))
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
;; prestat.wat — WASI prestat enumeration test
|
||||
;; Calls fd_prestat_get for fds 3..7, expects EBADF (8) for all.
|
||||
;; Proves libc's pre-open enumeration loop terminates correctly.
|
||||
;;
|
||||
;; @expect-exit 0
|
||||
|
||||
(module
|
||||
(import "wasi_snapshot_preview1" "fd_prestat_get"
|
||||
(func $fd_prestat_get (param i32 i32) (result i32)))
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
(memory (export "memory") 1)
|
||||
|
||||
(func $main (export "_start")
|
||||
(local $errno i32)
|
||||
|
||||
;; fd_prestat_get(3, buf=200) should return EBADF=8
|
||||
(local.set $errno
|
||||
(call $fd_prestat_get (i32.const 3) (i32.const 200)))
|
||||
|
||||
;; If errno != 8, exit with 1 (failure)
|
||||
(if (i32.ne (local.get $errno) (i32.const 8))
|
||||
(then (call $proc_exit (i32.const 1))))
|
||||
|
||||
;; fd_prestat_get(4, buf=200) should also return EBADF
|
||||
(local.set $errno
|
||||
(call $fd_prestat_get (i32.const 4) (i32.const 200)))
|
||||
(if (i32.ne (local.get $errno) (i32.const 8))
|
||||
(then (call $proc_exit (i32.const 2))))
|
||||
|
||||
;; All good — exit 0
|
||||
(call $proc_exit (i32.const 0))
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
;; random.wat — WASI random_get test
|
||||
;; Calls random_get to fill 8 bytes, verifies ESUCCESS (exit 0)
|
||||
;; @expect-exit 0
|
||||
(module
|
||||
(import "wasi_snapshot_preview1" "random_get"
|
||||
(func $random_get (param i32 i32) (result i32)))
|
||||
(import "wasi_snapshot_preview1" "proc_exit"
|
||||
(func $proc_exit (param i32)))
|
||||
|
||||
(memory (export "memory") 1)
|
||||
|
||||
(func (export "_start")
|
||||
(local $errno i32)
|
||||
;; random_get(buf=0, buf_len=8)
|
||||
(local.set $errno
|
||||
(call $random_get
|
||||
(i32.const 0) ;; buf pointer
|
||||
(i32.const 8))) ;; 8 bytes
|
||||
|
||||
;; Exit with errno (should be 0 = ESUCCESS)
|
||||
(call $proc_exit (local.get $errno))
|
||||
)
|
||||
)
|
||||
Reference in new issue
Block a user