vxx2 / vlib / wasm / module.v
350 lines · 305 sloc · 8.08 KB · 863a2090f8eda7dc75e061b26e0bca252f167e24
Raw
1// Copyright (c) 2023 l-m.dev. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module wasm
5
6enum Section as u8 {
7 custom_section
8 type_section
9 import_section
10 function_section
11 table_section
12 memory_section
13 global_section
14 export_section
15 start_section
16 element_section
17 code_section
18 data_section
19 data_count_section
20}
21
22enum Subsection as u8 {
23 name_module
24 name_function
25 name_local
26 // see: https://github.com/WebAssembly/extended-name-section
27 name_label
28 name_type
29 name_table
30 name_memory
31 name_global
32 name_elem
33 name_data
34}
35
36pub enum NumType as u8 {
37 i32_t = 0x7f
38 i64_t = 0x7e
39 f32_t = 0x7d
40 f64_t = 0x7c
41}
42
43pub enum ValType as u8 {
44 i32_t = 0x7f
45 i64_t = 0x7e
46 f32_t = 0x7d
47 f64_t = 0x7c
48 v128_t = 0x7b
49 funcref_t = 0x70
50 externref_t = 0x6f
51}
52
53pub enum RefType as u8 {
54 funcref_t = 0x70
55 externref_t = 0x6f
56}
57
58// Module contains the WebAssembly module.
59// Use the `compile` method to compile the module into a pure byte array.
60@[heap]
61pub struct Module {
62mut:
63 buf []u8
64 functypes []FuncType
65 functions map[string]Function
66 globals []Global
67 memory ?Memory
68 start ?string
69 fn_imports []FunctionImport
70 global_imports []GlobalImport
71 segments []DataSegment
72 tables []Table
73 elements []Element
74 debug bool
75 mod_name ?string
76}
77
78struct Global {
79 typ ValType
80 is_mut bool
81 name string
82 export bool
83mut:
84 init ConstExpression
85}
86
87struct GlobalImport {
88 mod string
89 name string
90 typ ValType
91 is_mut bool
92}
93
94struct FunctionImport {
95 mod string
96 name string
97 tidx int
98}
99
100struct Memory {
101 name string
102 export bool
103 min u32
104 max ?u32
105}
106
107struct DataSegment {
108 idx ?int
109 data []u8
110 name ?string
111}
112
113struct Table {
114 name string
115 export bool
116 reftype RefType
117 min u32
118 max ?u32
119}
120
121enum ElementMode {
122 active
123 declarative
124 passive
125}
126
127struct Element {
128 mode ElementMode
129 tableidx TableIndex
130 offset int
131 funcs []string
132}
133
134pub type LocalIndex = int
135pub type GlobalIndex = int
136pub type GlobalImportIndex = int
137pub type DataSegmentIndex = int
138pub type TableIndex = int
139pub type TypeIndex = int
140pub type ElementIndex = int
141
142pub struct FuncType {
143pub:
144 parameters []ValType
145 results []ValType
146 name ?string
147}
148
149// new_functype interns a function type and returns its type index.
150// Use it to obtain the type index required by `call_indirect`.
151pub fn (mut mod Module) new_functype(ft FuncType) TypeIndex {
152 // interns existing types
153 mut idx := mod.functypes.index(ft)
154
155 if idx == -1 {
156 idx = mod.functypes.len
157 mod.functypes << ft
158 }
159
160 return idx
161}
162
163// new_function creates a function struct.
164pub fn (mut mod Module) new_function(name string, parameters []ValType, results []ValType) Function {
165 assert name !in mod.functions.keys()
166
167 idx := mod.functions.len
168 tidx := mod.new_functype(FuncType{parameters, results, none})
169
170 return Function{
171 name: name
172 tidx: tidx
173 idx: idx
174 mod: mod
175 locals: parameters.map(FunctionLocal{}) // specifying it's ValType doesn't matter
176 }
177}
178
179// new_debug_function creates a function struct with extra debug information.
180// `argument_names` must be the same length as the parameters in the function type `typ`.
181pub fn (mut mod Module) new_debug_function(name string, typ FuncType, argument_names []?string) Function {
182 assert name !in mod.functions.keys()
183 assert typ.parameters.len == argument_names.len
184
185 idx := mod.functions.len
186 tidx := mod.new_functype(typ)
187
188 return Function{
189 name: name
190 tidx: tidx
191 idx: idx
192 mod: mod
193 locals: argument_names.map(FunctionLocal{ name: it }) // specifying it's ValType doesn't matter
194 }
195}
196
197// enable_debug sets whether to emit debug information for not.
198pub fn (mut mod Module) enable_debug(mod_name ?string) {
199 mod.debug = true
200 mod.mod_name = mod_name
201}
202
203// assign_memory assigns memory to the current module.
204pub fn (mut mod Module) assign_memory(name string, export bool, min u32, max ?u32) {
205 mod.memory = Memory{
206 name: name
207 export: export
208 min: min
209 max: max
210 }
211}
212
213// assign_start assigns the start function to the current module.
214pub fn (mut mod Module) assign_start(name string) {
215 mod.start = name
216}
217
218// assign_table declares a table in the current module and returns its index.
219// `reftype` is the element type (`funcref_t` or `externref_t`); pass `max` as
220// `none` for a growable table.
221pub fn (mut mod Module) assign_table(name string, export bool, reftype RefType, min u32, max ?u32) TableIndex {
222 len := mod.tables.len
223 mod.tables << Table{
224 name: name
225 export: export
226 reftype: reftype
227 min: min
228 max: max
229 }
230 return len
231}
232
233// new_active_element appends an active element segment that initialises table
234// `tableidx` starting at `offset` with the given (local) function references,
235// and returns its index. An active segment also declares its functions, so a
236// later `ref.func` to any of them validates.
237pub fn (mut mod Module) new_active_element(tableidx TableIndex, offset int, funcs []string) ElementIndex {
238 len := mod.elements.len
239 mod.elements << Element{
240 mode: .active
241 tableidx: tableidx
242 offset: offset
243 funcs: funcs
244 }
245 return len
246}
247
248// new_declarative_element appends a declarative element segment that declares
249// the given (local) functions, so that `ref.func` to a non-exported function
250// validates. It allocates no table space. Returns its index.
251pub fn (mut mod Module) new_declarative_element(funcs []string) ElementIndex {
252 len := mod.elements.len
253 mod.elements << Element{
254 mode: .declarative
255 funcs: funcs
256 }
257 return len
258}
259
260// new_function_import imports a new function into the current module.
261pub fn (mut mod Module) new_function_import(modn string, name string, parameters []ValType, results []ValType) {
262 assert !mod.fn_imports.any(it.mod == modn && it.name == name)
263
264 tidx := mod.new_functype(FuncType{parameters, results, none})
265
266 mod.fn_imports << FunctionImport{
267 mod: modn
268 name: name
269 tidx: tidx
270 }
271}
272
273// new_function_import_debug imports a new function into the current module with extra debug information.
274pub fn (mut mod Module) new_function_import_debug(modn string, name string, typ FuncType) {
275 assert !mod.fn_imports.any(it.mod == modn && it.name == name)
276
277 tidx := mod.new_functype(typ)
278
279 mod.fn_imports << FunctionImport{
280 mod: modn
281 name: name
282 tidx: tidx
283 }
284}
285
286// commit commits a function to the module, use `export` to export the function.
287pub fn (mut mod Module) commit(func Function, export bool) {
288 assert func.name !in mod.functions.keys()
289
290 mod.functions[func.name] = Function{
291 ...func
292 export: export
293 }
294}
295
296// new_data_segment inserts a new data segment at the memory index `pos`.
297// `name` is optional, it is used for debug info.
298pub fn (mut mod Module) new_data_segment(name ?string, pos int, data []u8) DataSegmentIndex {
299 len := mod.segments.len
300 mod.segments << DataSegment{
301 idx: pos
302 data: data
303 name: name
304 }
305 return len
306}
307
308// new_passive_data_segment inserts a new passive data segment.
309// `name` is optional, it is used for debug info.
310pub fn (mut mod Module) new_passive_data_segment(name ?string, data []u8) {
311 mod.segments << DataSegment{
312 data: data
313 name: name
314 }
315}
316
317// new_global creates a global and returns it's index.
318// See `global_get`, `global_set`.
319pub fn (mut mod Module) new_global(name string, export bool, typ ValType, is_mut bool, init ConstExpression) GlobalIndex {
320 len := mod.globals.len
321 mod.globals << Global{
322 typ: typ
323 is_mut: is_mut
324 name: name
325 export: export
326 init: init
327 }
328 return len
329}
330
331// new_global_import imports a new global into the current module and returns it's index.
332// See `global_get`, `global_set`.
333pub fn (mut mod Module) new_global_import(modn string, name string, typ ValType, is_mut bool) GlobalImportIndex {
334 assert !mod.fn_imports.any(it.mod == modn && it.name == name)
335
336 len := mod.global_imports.len
337 mod.global_imports << GlobalImport{
338 mod: modn
339 name: name
340 typ: typ
341 is_mut: is_mut
342 }
343 return len
344}
345
346// assign_global_init assigns a global with the constant expression `init`.
347// See `new_global`.
348pub fn (mut mod Module) assign_global_init(global GlobalIndex, init ConstExpression) {
349 mod.globals[global].init = init
350}
351