v4 / vlib / v / gen / wasm / tests / browser_empty_main_test.v
164 lines · 146 sloc · 4.45 KB · acd0803def213babd475cd9dfa14f3cb0d85011e
Raw
1import os
2
3struct WasmVarUint {
4 value u32
5 next_idx int
6}
7
8struct WasmModuleSummary {
9mut:
10 exports []string
11 has_start_section bool
12}
13
14fn test_wasm_browser_target_allows_empty_main() {
15 vexe := os.quoted_path(@VEXE)
16 wrkdir := os.join_path(os.vtmp_dir(), 'wasm_browser_tests')
17 os.mkdir_all(wrkdir)!
18 defer {
19 os.rmdir_all(wrkdir) or {}
20 }
21
22 source_path := os.join_path(wrkdir, 'empty_main.v')
23 os.write_file(source_path, 'pub fn main() {}\n')!
24
25 flags_sets := [
26 '-b wasm -os browser',
27 '-enable-globals -b wasm -os browser',
28 ]
29
30 for idx, flags in flags_sets {
31 output_path := os.join_path(wrkdir, 'empty_main_${idx}.wasm')
32 res :=
33 os.execute('${vexe} ${flags} -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}')
34 assert res.exit_code == 0, 'compilation failed for `${flags}`: ${res.output}'
35 assert os.exists(output_path), 'missing output for `${flags}`'
36 }
37}
38
39fn test_wasm_browser_target_allows_eprintln() {
40 vexe := os.quoted_path(@VEXE)
41 wrkdir := os.join_path(os.vtmp_dir(), 'wasm_browser_eprintln_tests')
42 os.mkdir_all(wrkdir)!
43 defer {
44 os.rmdir_all(wrkdir) or {}
45 }
46
47 source_path := os.join_path(wrkdir, 'eprintln_main.v')
48 output_path := os.join_path(wrkdir, 'eprintln_main.wasm')
49 os.write_file(source_path, "pub fn main() { eprintln('browser stderr') }\n")!
50
51 res :=
52 os.execute('${vexe} -b wasm -os browser -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}')
53 assert res.exit_code == 0, 'compilation failed: ${res.output}'
54 assert os.exists(output_path), 'missing output for browser eprintln'
55}
56
57fn test_wasm_shared_library_exports_custom_names_without_main() {
58 vexe := os.quoted_path(@VEXE)
59 wrkdir := os.join_path(os.vtmp_dir(), 'wasm_shared_library_tests')
60 os.mkdir_all(wrkdir)!
61 defer {
62 os.rmdir_all(wrkdir) or {}
63 }
64
65 source_path := os.join_path(wrkdir, 'my_wasm_lib.v')
66 output_path := os.join_path(wrkdir, 'my_wasm_lib.wasm')
67 source := [
68 'module my_wasm_lib',
69 '',
70 "@[export: 'myFunction']",
71 'fn my_function(a int, b int) int {',
72 '\treturn a + b',
73 '}',
74 ].join_lines()
75 os.write_file(source_path, source)!
76
77 res :=
78 os.execute('${vexe} -b wasm -shared -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}')
79 assert res.exit_code == 0, 'compilation failed: ${res.output}'
80 assert os.exists(output_path), 'missing output for shared wasm library'
81
82 wasm_bytes := os.read_bytes(output_path)!
83 summary := inspect_wasm_module(wasm_bytes) or { panic(err) }
84
85 assert 'myFunction' in summary.exports
86 assert '_start' !in summary.exports
87 assert summary.has_start_section
88}
89
90fn inspect_wasm_module(wasm_bytes []u8) !WasmModuleSummary {
91 if wasm_bytes.len < 8 {
92 return error('wasm module is too short')
93 }
94 if wasm_bytes[0] != 0x00 || wasm_bytes[1] != 0x61 || wasm_bytes[2] != 0x73
95 || wasm_bytes[3] != 0x6d || wasm_bytes[4] != 0x01 || wasm_bytes[5] != 0x00
96 || wasm_bytes[6] != 0x00 || wasm_bytes[7] != 0x00 {
97 return error('invalid wasm header')
98 }
99
100 mut summary := WasmModuleSummary{}
101 mut idx := 8
102 for idx < wasm_bytes.len {
103 section_id := wasm_bytes[idx]
104 idx++
105 section_len := read_wasm_u32(wasm_bytes, idx)!
106 idx = section_len.next_idx
107 section_end := idx + int(section_len.value)
108 if section_end > wasm_bytes.len {
109 return error('wasm section exceeds module bounds')
110 }
111 match section_id {
112 u8(7) {
113 mut export_idx := idx
114 exports_len := read_wasm_u32(wasm_bytes, export_idx)!
115 export_idx = exports_len.next_idx
116 for _ in 0 .. int(exports_len.value) {
117 name_len := read_wasm_u32(wasm_bytes, export_idx)!
118 export_idx = name_len.next_idx
119 name_end := export_idx + int(name_len.value)
120 if name_end > section_end {
121 return error('wasm export name exceeds section bounds')
122 }
123 summary.exports << wasm_bytes[export_idx..name_end].bytestr()
124 export_idx = name_end
125 if export_idx >= section_end {
126 return error('wasm export kind is missing')
127 }
128 export_idx++
129 export_ref := read_wasm_u32(wasm_bytes, export_idx)!
130 export_idx = export_ref.next_idx
131 }
132 }
133 u8(8) {
134 summary.has_start_section = true
135 }
136 else {}
137 }
138
139 idx = section_end
140 }
141 return summary
142}
143
144fn read_wasm_u32(wasm_bytes []u8, start int) !WasmVarUint {
145 mut value := u32(0)
146 mut shift := u32(0)
147 mut idx := start
148 for idx < wasm_bytes.len {
149 b := wasm_bytes[idx]
150 value |= u32(b & 0x7f) << shift
151 idx++
152 if b & 0x80 == 0 {
153 return WasmVarUint{
154 value: value
155 next_idx: idx
156 }
157 }
158 shift += 7
159 if shift >= 35 {
160 break
161 }
162 }
163 return error('invalid wasm varuint32 encoding')
164}
165