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