pwasm_utils/
export_globals.rs

1use parity_wasm::elements;
2
3use crate::optimizer::{export_section, global_section};
4
5/// Export all declared mutable globals.
6///
7/// This will export all internal mutable globals under the name of
8/// concat(`prefix`, i) where i is the index inside the range of
9/// [0..<total number of internal mutable globals>].
10pub fn export_mutable_globals(module: &mut elements::Module, prefix: impl Into<String>) {
11	let exports = global_section(module)
12		.map(|section| {
13			section
14				.entries()
15				.iter()
16				.enumerate()
17				.filter_map(
18					|(index, global)| {
19						if global.global_type().is_mutable() {
20							Some(index)
21						} else {
22							None
23						}
24					},
25				)
26				.collect::<Vec<_>>()
27		})
28		.unwrap_or_default();
29
30	if module.export_section().is_none() {
31		module
32			.sections_mut()
33			.push(elements::Section::Export(elements::ExportSection::default()));
34	}
35
36	let prefix: String = prefix.into();
37	for (symbol_index, export) in exports.into_iter().enumerate() {
38		let new_entry = elements::ExportEntry::new(
39			format!("{}_{}", prefix, symbol_index),
40			elements::Internal::Global(
41				(module.import_count(elements::ImportCountType::Global) + export) as _,
42			),
43		);
44		export_section(module)
45			.expect("added above if does not exists")
46			.entries_mut()
47			.push(new_entry);
48	}
49}
50
51#[cfg(test)]
52mod tests {
53
54	use super::export_mutable_globals;
55	use parity_wasm::elements;
56
57	fn parse_wat(source: &str) -> elements::Module {
58		let module_bytes = wabt::Wat2Wasm::new()
59			.validate(true)
60			.convert(source)
61			.expect("failed to parse module");
62		elements::deserialize_buffer(module_bytes.as_ref()).expect("failed to parse module")
63	}
64
65	macro_rules! test_export_global {
66		(name = $name:ident; input = $input:expr; expected = $expected:expr) => {
67			#[test]
68			fn $name() {
69				let mut input_module = parse_wat($input);
70				let expected_module = parse_wat($expected);
71
72				export_mutable_globals(&mut input_module, "exported_internal_global");
73
74				let actual_bytes = elements::serialize(input_module)
75					.expect("injected module must have a function body");
76
77				let expected_bytes = elements::serialize(expected_module)
78					.expect("injected module must have a function body");
79
80				assert_eq!(actual_bytes, expected_bytes);
81			}
82		};
83	}
84
85	test_export_global! {
86		name = simple;
87		input = r#"
88		(module
89			(global (;0;) (mut i32) (i32.const 1))
90			(global (;1;) (mut i32) (i32.const 0)))
91		"#;
92		expected = r#"
93		(module
94			(global (;0;) (mut i32) (i32.const 1))
95			(global (;1;) (mut i32) (i32.const 0))
96			(export "exported_internal_global_0" (global 0))
97			(export "exported_internal_global_1" (global 1)))
98		"#
99	}
100
101	test_export_global! {
102		name = with_import;
103		input = r#"
104		(module
105			(import "env" "global" (global $global i64))
106			(global (;0;) (mut i32) (i32.const 1))
107			(global (;1;) (mut i32) (i32.const 0)))
108		"#;
109		expected = r#"
110		(module
111			(import "env" "global" (global $global i64))
112			(global (;0;) (mut i32) (i32.const 1))
113			(global (;1;) (mut i32) (i32.const 0))
114			(export "exported_internal_global_0" (global 1))
115			(export "exported_internal_global_1" (global 2)))
116		"#
117	}
118
119	test_export_global! {
120		name = with_import_and_some_are_immutable;
121		input = r#"
122		(module
123			(import "env" "global" (global $global i64))
124			(global (;0;) i32 (i32.const 1))
125			(global (;1;) (mut i32) (i32.const 0)))
126		"#;
127		expected = r#"
128		(module
129			(import "env" "global" (global $global i64))
130			(global (;0;) i32 (i32.const 1))
131			(global (;1;) (mut i32) (i32.const 0))
132			(export "exported_internal_global_0" (global 2)))
133		"#
134	}
135}