123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- use leb::{write_fixed_leb16_at_idx, write_fixed_leb32_at_idx, write_leb_u32};
- use util::{SafeToU16, SafeToU8};
- use wasmgen::wasm_opcodes as op;
- #[allow(dead_code)]
- pub const FN0_TYPE_INDEX: u8 = 0;
- #[allow(dead_code)]
- pub const FN1_TYPE_INDEX: u8 = 1;
- #[allow(dead_code)]
- pub const FN2_TYPE_INDEX: u8 = 2;
- #[allow(dead_code)]
- pub const FN3_TYPE_INDEX: u8 = 3;
- #[allow(dead_code)]
- pub const FN0_RET_TYPE_INDEX: u8 = 4;
- #[allow(dead_code)]
- pub const FN1_RET_TYPE_INDEX: u8 = 5;
- #[allow(dead_code)]
- pub const FN2_RET_TYPE_INDEX: u8 = 6;
- pub const NR_FN_TYPE_INDEXES: u8 = 7;
- pub struct WasmBuilder {
- pub output: Vec<u8>,
- pub code_section: Vec<u8>,
- pub instruction_body: Vec<u8>,
- idx_import_table_size: usize, // for rewriting once finished
- idx_import_count: usize, // for rewriting once finished
- idx_import_entries: usize, // for searching the imports
- import_table_size: usize, // the current import table size (to avoid reading 2 byte leb)
- import_count: u16, // same as above
- initial_static_size: usize, // size of module after initialization, rest is drained on reset
- }
- impl WasmBuilder {
- pub fn new() -> Self {
- WasmBuilder {
- output: Vec::with_capacity(256),
- code_section: Vec::with_capacity(256),
- instruction_body: Vec::with_capacity(256),
- idx_import_table_size: 0,
- idx_import_count: 0,
- idx_import_entries: 0,
- import_table_size: 2,
- import_count: 0,
- initial_static_size: 0,
- }
- }
- pub fn init(&mut self) {
- self.output.extend("\0asm".as_bytes());
- // wasm version in leb128, 4 bytes
- self.output.push(op::WASM_VERSION);
- self.output.push(0);
- self.output.push(0);
- self.output.push(0);
- self.write_type_section();
- self.write_import_section_preamble();
- // store state of current pointers etc. so we can reset them later
- self.initial_static_size = self.output.len();
- }
- pub fn reset(&mut self) {
- self.output.drain(self.initial_static_size..);
- self.set_import_table_size(2);
- self.set_import_count(0);
- self.code_section.clear();
- self.instruction_body.clear();
- }
- pub fn finish(&mut self, no_of_locals_i32: u8) -> usize {
- self.write_memory_import();
- self.write_function_section(1);
- self.write_export_section();
- // write code section preamble
- self.output.push(op::SC_CODE);
- let idx_code_section_size = self.output.len(); // we will write to this location later
- self.output.push(0);
- self.output.push(0); // write temp val for now using 4 bytes
- self.output.push(0);
- self.output.push(0);
- self.output.push(1); // number of function bodies: just 1
- // same as above but for body size of the function
- let idx_fn_body_size = self.output.len();
- self.output.push(0);
- self.output.push(0);
- self.output.push(0);
- self.output.push(0);
- self.output.push(1); // count of local blocks
- dbg_assert!(no_of_locals_i32 < 128);
- self.output.push(no_of_locals_i32);
- self.output.push(op::TYPE_I32);
- self.output.append(&mut self.code_section);
- self.output.push(op::OP_END);
- // write the actual sizes to the pointer locations stored above. We subtract 4 from the actual
- // value because the ptr itself points to four bytes
- let fn_body_size = (self.output.len() - idx_fn_body_size - 4) as u32;
- write_fixed_leb32_at_idx(&mut self.output, idx_fn_body_size, fn_body_size);
- let code_section_size = (self.output.len() - idx_code_section_size - 4) as u32;
- write_fixed_leb32_at_idx(&mut self.output, idx_code_section_size, code_section_size);
- self.output.len()
- }
- pub fn write_type_section(&mut self) {
- self.output.push(op::SC_TYPE);
- let idx_section_size = self.output.len();
- self.output.push(0);
- self.output.push(NR_FN_TYPE_INDEXES); // number of type descriptors
- // FN0
- self.output.push(op::TYPE_FUNC);
- self.output.push(0); // no args
- self.output.push(0); // no return val
- // FN1
- self.output.push(op::TYPE_FUNC);
- self.output.push(1);
- self.output.push(op::TYPE_I32);
- self.output.push(0);
- // FN2
- self.output.push(op::TYPE_FUNC);
- self.output.push(2);
- self.output.push(op::TYPE_I32);
- self.output.push(op::TYPE_I32);
- self.output.push(0);
- // FN3
- self.output.push(op::TYPE_FUNC);
- self.output.push(3);
- self.output.push(op::TYPE_I32);
- self.output.push(op::TYPE_I32);
- self.output.push(op::TYPE_I32);
- self.output.push(0);
- // FN0_RET
- self.output.push(op::TYPE_FUNC);
- self.output.push(0);
- self.output.push(1);
- self.output.push(op::TYPE_I32);
- // FN1_RET
- self.output.push(op::TYPE_FUNC);
- self.output.push(1);
- self.output.push(op::TYPE_I32);
- self.output.push(1);
- self.output.push(op::TYPE_I32);
- // FN2_RET
- self.output.push(op::TYPE_FUNC);
- self.output.push(2);
- self.output.push(op::TYPE_I32);
- self.output.push(op::TYPE_I32);
- self.output.push(1);
- self.output.push(op::TYPE_I32);
- let new_len = self.output.len();
- let size = (new_len - 1) - idx_section_size;
- self.output[idx_section_size] = size.safe_to_u8();
- }
- /// Goes over the import block to find index of an import entry by function name
- pub fn get_import_index(&self, fn_name: &str) -> Option<u16> {
- let mut offset = self.idx_import_entries;
- for i in 0..self.import_count {
- offset += 1; // skip length of module name
- offset += 1; // skip module name itself
- let len = self.output[offset] as usize;
- offset += 1;
- let name = self
- .output
- .get(offset..(offset + len))
- .expect("get function name");
- if name == fn_name.as_bytes() {
- return Some(i);
- }
- offset += len; // skip the string
- offset += 1; // skip import kind
- offset += 1; // skip type index
- }
- None
- }
- pub fn set_import_count(&mut self, count: u16) {
- dbg_assert!(count < 0x4000);
- self.import_count = count;
- let idx_import_count = self.idx_import_count;
- write_fixed_leb16_at_idx(&mut self.output, idx_import_count, count);
- }
- pub fn set_import_table_size(&mut self, size: usize) {
- dbg_assert!(size < 0x4000);
- self.import_table_size = size;
- let idx_import_table_size = self.idx_import_table_size;
- write_fixed_leb16_at_idx(&mut self.output, idx_import_table_size, size.safe_to_u16());
- }
- pub fn write_import_section_preamble(&mut self) {
- self.output.push(op::SC_IMPORT);
- self.idx_import_table_size = self.output.len();
- self.output.push(1 | 0b10000000);
- self.output.push(2); // 2 in 2 byte leb
- self.idx_import_count = self.output.len();
- self.output.push(1 | 0b10000000);
- self.output.push(0); // 0 in 2 byte leb
- // here after starts the actual list of imports
- self.idx_import_entries = self.output.len();
- }
- pub fn write_memory_import(&mut self) {
- self.output.push(1);
- self.output.push('e' as u8);
- self.output.push(1);
- self.output.push('m' as u8);
- self.output.push(op::EXT_MEMORY);
- self.output.push(0); // memory flag, 0 for no maximum memory limit present
- write_leb_u32(&mut self.output, 256); // initial memory length of 256 pages, takes 2 bytes in leb128
- let new_import_count = self.import_count + 1;
- self.set_import_count(new_import_count);
- let new_table_size = self.import_table_size + 8;
- self.set_import_table_size(new_table_size);
- }
- pub fn write_import_entry(&mut self, fn_name: &str, type_index: u8) -> u16 {
- self.output.push(1); // length of module name
- self.output.push('e' as u8); // module name
- self.output.push(fn_name.len().safe_to_u8());
- self.output.extend(fn_name.as_bytes());
- self.output.push(op::EXT_FUNCTION);
- self.output.push(type_index);
- let new_import_count = self.import_count + 1;
- self.set_import_count(new_import_count);
- let new_table_size = self.import_table_size + 1 + 1 + 1 + fn_name.len() + 1 + 1;
- self.set_import_table_size(new_table_size);
- self.import_count - 1
- }
- pub fn write_function_section(&mut self, count: u8) {
- self.output.push(op::SC_FUNCTION);
- self.output.push(1 + count); // length of this section
- self.output.push(count); // count of signature indices
- for _ in 0..count {
- self.output.push(FN1_TYPE_INDEX);
- }
- }
- pub fn write_export_section(&mut self) {
- self.output.push(op::SC_EXPORT);
- self.output.push(1 + 1 + 1 + 1 + 2); // size of this section
- self.output.push(1); // count of table: just one function exported
- self.output.push(1); // length of exported function name
- self.output.push('f' as u8); // function name
- self.output.push(op::EXT_FUNCTION);
- // index of the exported function
- // function space starts with imports. index of last import is import count - 1
- // the last import however is a memory, so we subtract one from that
- let next_op_idx = self.output.len();
- self.output.push(0);
- self.output.push(0); // add 2 bytes for writing 16 byte val
- write_fixed_leb16_at_idx(&mut self.output, next_op_idx, self.import_count - 1);
- }
- pub fn get_fn_idx(&mut self, fn_name: &str, type_index: u8) -> u16 {
- match self.get_import_index(fn_name) {
- Some(idx) => idx,
- None => {
- let idx = self.write_import_entry(fn_name, type_index);
- idx
- },
- }
- }
- pub fn get_op_ptr(&self) -> *const u8 { self.output.as_ptr() }
- pub fn get_op_len(&self) -> u32 { self.output.len() as u32 }
- pub fn commit_instruction_body_to_cs(&mut self) {
- self.code_section.append(&mut self.instruction_body);
- }
- }
- #[cfg(test)]
- mod tests {
- use std::fs::File;
- use std::io::prelude::*;
- use wasmgen::module_init::*;
- use wasmgen::wasm_util::*;
- #[test]
- fn import_table_management() {
- let mut w = WasmBuilder::new();
- w.init();
- assert_eq!(0, w.get_fn_idx("foo", FN0_TYPE_INDEX));
- assert_eq!(1, w.get_fn_idx("bar", FN1_TYPE_INDEX));
- assert_eq!(0, w.get_fn_idx("foo", FN0_TYPE_INDEX));
- assert_eq!(2, w.get_fn_idx("baz", FN2_TYPE_INDEX));
- }
- #[test]
- fn builder_test() {
- let mut m = WasmBuilder::new();
- m.init();
- let mut foo_index = m.get_fn_idx("foo", FN0_TYPE_INDEX);
- call_fn(&mut m.code_section, foo_index);
- let bar_index = m.get_fn_idx("bar", FN0_TYPE_INDEX);
- call_fn(&mut m.code_section, bar_index);
- m.finish(2);
- m.reset();
- push_i32(&mut m.code_section, 2);
- let baz_index = m.get_fn_idx("baz", FN1_RET_TYPE_INDEX);
- call_fn(&mut m.instruction_body, baz_index);
- foo_index = m.get_fn_idx("foo", FN1_TYPE_INDEX);
- call_fn(&mut m.instruction_body, foo_index);
- m.commit_instruction_body_to_cs();
- m.finish(0);
- let op_ptr = m.get_op_ptr();
- let op_len = m.get_op_len();
- dbg_log!("op_ptr: {:?}, op_len: {:?}", op_ptr, op_len);
- let mut f = File::create("build/dummy_output.wasm").expect("creating dummy_output.wasm");
- f.write_all(&m.output).expect("write dummy_output.wasm");
- }
- }
|