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