twasm_utils/
runtime_type.rs1use tetsy_wasm::{elements, builder};
2use self::elements::{ Module, GlobalEntry, External, ExportEntry, GlobalType, ValueType, InitExpr, Instruction, Internal };
3use byteorder::{ LittleEndian, ByteOrder };
4
5pub fn inject_runtime_type(module: Module, runtime_type: [u8; 4], runtime_version: u32) -> Module {
6 let runtime_type: u32 = LittleEndian::read_u32(&runtime_type);
7 let globals_count: u32 = match module.global_section() {
8 Some(section) => section.entries().len() as u32,
9 None => 0
10 };
11 let imported_globals_count: u32 = match module.import_section() {
12 Some(section) => section
13 .entries()
14 .iter()
15 .filter(|e| matches!(*e.external(), External::Global(_)))
16 .count() as u32,
17 None => 0
18 };
19 let total_globals_count: u32 = globals_count + imported_globals_count;
20
21 builder::from_module(module)
22 .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Instruction::I32Const(runtime_type as i32), Instruction::End])))
23 .with_export(ExportEntry::new("RUNTIME_TYPE".into(), Internal::Global(total_globals_count)))
24 .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Instruction::I32Const(runtime_version as i32), Instruction::End])))
25 .with_export(ExportEntry::new("RUNTIME_VERSION".into(), Internal::Global(total_globals_count + 1)))
26 .build()
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32 #[test]
33 fn it_injects() {
34 let mut module = builder::module()
35 .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Instruction::I32Const(42 as i32)])))
36 .build();
37 let mut runtime_type: [u8; 4] = Default::default();
38 runtime_type.copy_from_slice(b"emcc");
39 module = inject_runtime_type(module, runtime_type, 1);
40 let global_section = module.global_section().expect("Global section expected");
41 assert_eq!(3, global_section.entries().len());
42 let export_section = module.export_section().expect("Export section expected");
43 assert!(export_section.entries().iter().any(|e| e.field() == "RUNTIME_TYPE"));
44 assert!(export_section.entries().iter().any(|e| e.field() == "RUNTIME_VERSION"));
45 }
46}