Skip to content

Commit 0e098a9

Browse files
wasm: support WASI program arguments and IO functions (#26294)
1 parent 7b22bae commit 0e098a9

File tree

5 files changed

+184
-1
lines changed

5 files changed

+184
-1
lines changed

vlib/builtin/wasm/string.v

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,23 @@ pub fn (s string) at(idx int) u8 {
7575
}
7676
return unsafe { s.str[idx] }
7777
}
78+
79+
// Convert C string to V string
80+
pub fn cstring_to_vstring(ptr &u8) string {
81+
if ptr == 0 {
82+
return ''
83+
}
84+
85+
mut len := 0
86+
unsafe {
87+
for ptr[len] != 0 {
88+
len++
89+
}
90+
}
91+
unsafe {
92+
return string{
93+
str: ptr
94+
len: len
95+
}
96+
}
97+
}
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
11
module builtin
22

3-
struct CIOVec {
3+
pub struct CIOVec {
4+
pub:
45
buf &u8
56
len usize
67
}
78

89
type Errno = u16
910
type FileDesc = int
1011

12+
@[wasm_import_namespace: 'wasi_snapshot_preview1']
13+
fn WASM.args_sizes_get(argc &u32, argc_buf_size &u32) Errno
14+
15+
@[wasm_import_namespace: 'wasi_snapshot_preview1']
16+
fn WASM.args_get(argv &&u8, argv_buf &u8) Errno
17+
1118
@[wasm_import_namespace: 'wasi_snapshot_preview1']
1219
fn WASM.fd_write(fd FileDesc, iovs &CIOVec, iovs_len usize, retptr &usize) Errno
1320

21+
@[wasm_import_namespace: 'wasi_snapshot_preview1']
22+
fn WASM.fd_read(fd FileDesc, iovs &CIOVec, iovs_len usize, nread &usize) Errno
23+
24+
@[wasm_import_namespace: 'wasi_snapshot_preview1']
25+
fn WASM.fd_sync(fd FileDesc) Errno
26+
27+
@[wasm_import_namespace: 'wasi_snapshot_preview1']
28+
fn WASM.random_get(buf &u8, buf_len usize) Errno
29+
1430
@[wasm_import_namespace: 'wasi_snapshot_preview1']
1531
@[noreturn]
1632
fn WASM.proc_exit(rval int)

vlib/v/gen/wasm/mem.v

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ pub fn (mut g Gen) get_var_from_expr(node ast.Expr) ?Var {
7272
ast.ParExpr {
7373
return g.get_var_from_expr(node.expr)
7474
}
75+
ast.CastExpr {
76+
// For cast expressions like &u32(), we need to look through
77+
// the cast to get the underlying variable because WASM don't have
78+
// really pointers like in C
79+
return g.get_var_from_expr(node.expr)
80+
}
7581
ast.SelectorExpr {
7682
mut addr := g.get_var_from_expr(node.expr) or {
7783
// if place {

vlib/v/gen/wasm/tests/wasi_api.vv

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
struct Args {
2+
argc u32
3+
argc_buf_size u32
4+
}
5+
6+
fn wasi_args_sizes_get() {
7+
args := Args{}
8+
if WASM.args_sizes_get(&args.argc, &args.argc_buf_size) == 0 {
9+
println('args_sizes_get: ok')
10+
}
11+
if args.argc >= 1 {
12+
println('args_sizes_get: argc >= 1')
13+
}
14+
if args.argc_buf_size > 0 {
15+
println('args_sizes_get: argc_buf_size > 0')
16+
}
17+
}
18+
19+
fn wasi_args_get() {
20+
unsafe {
21+
args := Args{}
22+
WASM.args_sizes_get(&args.argc, &args.argc_buf_size)
23+
24+
mut argv_buf := malloc(args.argc_buf_size + 1)
25+
mut argv_ptrs := malloc(args.argc)
26+
27+
if WASM.args_get(&argv_ptrs[0], &argv_buf[0]) == 0 {
28+
println('args_get: ok')
29+
}
30+
31+
// @TODO: Check if the end ends with .wasm with a proper builtin
32+
if cstring_to_vstring(argv_buf).len == (args.argc_buf_size) - 1 {
33+
println('wasi_args_get: ok')
34+
}
35+
}
36+
}
37+
38+
fn wasi_random_get() {
39+
mut random_table := [128]u8{}
40+
41+
errno := WASM.random_get(&random_table[0], 128)
42+
if errno == 0 {
43+
println('random_get: ok')
44+
}
45+
46+
mut min := u8(255)
47+
mut max := u8(0)
48+
mut all_same := true
49+
first_val := random_table[0]
50+
51+
for i in 0 .. 128 {
52+
val := random_table[i]
53+
if val < min {
54+
min = val
55+
}
56+
if val > max {
57+
max = val
58+
}
59+
if val != first_val {
60+
all_same = false
61+
}
62+
}
63+
64+
println('random_get: has low values = ${min < 200}')
65+
println('random_get: has high values = ${max > 55}')
66+
println('random_get: values vary = ${!all_same}')
67+
}
68+
69+
fn wasi_fd_write() {
70+
message := 'fd_write: Hello from WASI WOORLD!\n'
71+
message_struct := CIOVec{
72+
buf: message.str
73+
len: usize(message.len)
74+
}
75+
76+
if WASM.fd_write(1, &message_struct, 1, 0) == 0 {
77+
println('fd_write: ok')
78+
}
79+
}
80+
81+
fn wasi_fd_write_multiple_iovecs() {
82+
msg1 := 'Multiple '
83+
msg2 := 'IO '
84+
msg3 := 'vectors yay!\n'
85+
86+
iovs := [
87+
CIOVec{
88+
buf: msg1.str
89+
len: usize(msg1.len)
90+
},
91+
CIOVec{
92+
buf: msg2.str
93+
len: usize(msg2.len)
94+
},
95+
CIOVec{
96+
buf: msg3.str
97+
len: usize(msg3.len)
98+
},
99+
]!
100+
101+
if WASM.fd_write(1, &iovs[0], 3, 0) == 0 {
102+
println('fd_write_multi: ok')
103+
}
104+
}
105+
106+
fn wasi_fd_sync() {
107+
// Flush stdout (fd 1)
108+
if WASM.fd_sync(1) == 0 {
109+
println('fd_sync: ok on stdout')
110+
}
111+
112+
// Flush stderr (fd 2)
113+
if WASM.fd_sync(2) == 0 {
114+
println('fd_sync: ok on stderr')
115+
}
116+
}
117+
118+
fn main() {
119+
wasi_args_sizes_get()
120+
wasi_args_get()
121+
wasi_random_get()
122+
wasi_fd_write()
123+
wasi_fd_write_multiple_iovecs()
124+
wasi_fd_sync()
125+
// @TODO: Find a way to test fd_read ?
126+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
args_sizes_get: ok
2+
args_sizes_get: argc >= 1
3+
args_sizes_get: argc_buf_size > 0
4+
args_get: ok
5+
wasi_args_get: ok
6+
random_get: ok
7+
random_get: has low values = true
8+
random_get: has high values = true
9+
random_get: values vary = true
10+
fd_write: Hello from WASI WOORLD!
11+
fd_write: ok
12+
Multiple IO vectors yay!
13+
fd_write_multi: ok
14+
fd_sync: ok on stdout
15+
fd_sync: ok on stderr

0 commit comments

Comments
 (0)