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