pwasm_utils/
runtime_type.rs

1use 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}