// Copyright (c) 2023 l-m.dev. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. module wasm enum Section as u8 { custom_section type_section import_section function_section table_section memory_section global_section export_section start_section element_section code_section data_section data_count_section } enum Subsection as u8 { name_module name_function name_local // see: https://github.com/WebAssembly/extended-name-section name_label name_type name_table name_memory name_global name_elem name_data } pub enum NumType as u8 { i32_t = 0x7f i64_t = 0x7e f32_t = 0x7d f64_t = 0x7c } pub enum ValType as u8 { i32_t = 0x7f i64_t = 0x7e f32_t = 0x7d f64_t = 0x7c v128_t = 0x7b funcref_t = 0x70 externref_t = 0x6f } pub enum RefType as u8 { funcref_t = 0x70 externref_t = 0x6f } // Module contains the WebAssembly module. // Use the `compile` method to compile the module into a pure byte array. [heap] pub struct Module { mut: buf []u8 functypes []FuncType functions map[string]Function globals []Global memory ?Memory start ?string fn_imports []FunctionImport global_imports []GlobalImport segments []DataSegment debug bool mod_name ?string } struct Global { typ ValType is_mut bool name string export bool mut: init ConstExpression } struct GlobalImport { mod string name string typ ValType is_mut bool } struct FunctionImport { mod string name string tidx int } struct Memory { name string export bool min u32 max ?u32 } struct DataSegment { idx ?int data []u8 name ?string } pub type LocalIndex = int pub type GlobalIndex = int pub type GlobalImportIndex = int pub type DataSegmentIndex = int pub struct FuncType { pub: parameters []ValType results []ValType name ?string } fn (mut mod Module) new_functype(ft FuncType) int { // interns existing types mut idx := mod.functypes.index(ft) if idx == -1 { idx = mod.functypes.len mod.functypes << ft } return idx } // new_function creates a function struct. pub fn (mut mod Module) new_function(name string, parameters []ValType, results []ValType) Function { assert name !in mod.functions.keys() idx := mod.functions.len tidx := mod.new_functype(FuncType{parameters, results, none}) return Function{ name: name tidx: tidx idx: idx mod: mod locals: parameters.map(FunctionLocal{}) // specifying it's ValType doesn't matter } } // new_debug_function creates a function struct with extra debug information. // `argument_names` must be the same length as the parameters in the function type `typ`. pub fn (mut mod Module) new_debug_function(name string, typ FuncType, argument_names []?string) Function { assert name !in mod.functions.keys() assert typ.parameters.len == argument_names.len idx := mod.functions.len tidx := mod.new_functype(typ) return Function{ name: name tidx: tidx idx: idx mod: mod locals: argument_names.map(FunctionLocal{ name: it }) // specifying it's ValType doesn't matter } } // enable_debug sets whether to emit debug information for not. pub fn (mut mod Module) enable_debug(mod_name ?string) { mod.debug = true mod.mod_name = mod_name } // assign_memory assigns memory to the current module. pub fn (mut mod Module) assign_memory(name string, export bool, min u32, max ?u32) { mod.memory = Memory{ name: name export: export min: min max: max } } // assign_start assigns the start function to the current module. pub fn (mut mod Module) assign_start(name string) { mod.start = name } // new_function_import imports a new function into the current module. pub fn (mut mod Module) new_function_import(modn string, name string, parameters []ValType, results []ValType) { assert !mod.fn_imports.any(it.mod == modn && it.name == name) tidx := mod.new_functype(FuncType{parameters, results, none}) mod.fn_imports << FunctionImport{ mod: modn name: name tidx: tidx } } // new_function_import_debug imports a new function into the current module with extra debug information. pub fn (mut mod Module) new_function_import_debug(modn string, name string, typ FuncType) { assert !mod.fn_imports.any(it.mod == modn && it.name == name) tidx := mod.new_functype(typ) mod.fn_imports << FunctionImport{ mod: modn name: name tidx: tidx } } // commit commits a function to the module, use `export` to export the function. pub fn (mut mod Module) commit(func Function, export bool) { assert func.name !in mod.functions.keys() mod.functions[func.name] = Function{ ...func export: export } } // new_data_segment inserts a new data segment at the memory index `pos`. // `name` is optional, it is used for debug info. pub fn (mut mod Module) new_data_segment(name ?string, pos int, data []u8) DataSegmentIndex { len := mod.segments.len mod.segments << DataSegment{ idx: pos data: data name: name } return len } // new_passive_data_segment inserts a new passive data segment. // `name` is optional, it is used for debug info. pub fn (mut mod Module) new_passive_data_segment(name ?string, data []u8) { mod.segments << DataSegment{ data: data name: name } } // new_global creates a global and returns it's index. // See `global_get`, `global_set`. pub fn (mut mod Module) new_global(name string, export bool, typ ValType, is_mut bool, init ConstExpression) GlobalIndex { len := mod.globals.len mod.globals << Global{ typ: typ is_mut: is_mut name: name export: export init: init } return len } // new_global_import imports a new global into the current module and returns it's index. // See `global_get`, `global_set`. pub fn (mut mod Module) new_global_import(modn string, name string, typ ValType, is_mut bool) GlobalImportIndex { assert !mod.fn_imports.any(it.mod == modn && it.name == name) len := mod.global_imports.len mod.global_imports << GlobalImport{ mod: modn name: name typ: typ is_mut: is_mut } return len } // assign_global_init assigns a global with the constant expression `init`. // See `new_global`. pub fn (mut mod Module) assign_global_init(global GlobalIndex, init ConstExpression) { mod.globals[global].init = init }