1use crate::std::string::String;
2use crate::std::vec::Vec;
3use crate::std::borrow::ToOwned;
4
5use tetsy_wasm::{elements, builder};
6use byteorder::{LittleEndian, ByteOrder};
7
8use crate::optimizer::{import_section, export_section};
9
10type Insertion = (usize, u32, u32, String);
11
12pub fn update_call_index(instructions: &mut elements::Instructions, original_imports: usize, inserts: &[Insertion]) {
13 use tetsy_wasm::elements::Instruction::*;
14 for instruction in instructions.elements_mut().iter_mut() {
15 if let Call(call_index) = instruction {
16 if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) {
17 *call_index = (original_imports + pos) as u32;
18 } else if *call_index as usize > original_imports {
19 *call_index += inserts.len() as u32;
20 }
21 }
22 }
23}
24
25pub fn memory_section(module: &mut elements::Module) -> Option<&mut elements::MemorySection> {
26 for section in module.sections_mut() {
27 if let elements::Section::Memory(sect) = section {
28 return Some(sect);
29 }
30 }
31 None
32}
33
34pub fn externalize_mem(mut module: elements::Module, adjust_pages: Option<u32>, max_pages: u32) -> elements::Module {
35 let mut entry = memory_section(&mut module)
36 .expect("Memory section to exist")
37 .entries_mut()
38 .pop()
39 .expect("Own memory entry to exist in memory section");
40
41 if let Some(adjust_pages) = adjust_pages {
42 assert!(adjust_pages <= max_pages);
43 entry = elements::MemoryType::new(adjust_pages, Some(max_pages));
44 }
45
46 if entry.limits().maximum().is_none() {
47 entry = elements::MemoryType::new(entry.limits().initial(), Some(max_pages));
48 }
49
50 let mut builder = builder::from_module(module);
51 builder.push_import(
52 elements::ImportEntry::new(
53 "env".to_owned(),
54 "memory".to_owned(),
55 elements::External::Memory(entry),
56 )
57 );
58
59 builder.build()
60}
61
62fn foreach_public_func_name<F>(mut module: elements::Module, f: F) -> elements::Module
63where F: Fn(&mut String)
64{
65 if let Some(section) = import_section(&mut module) {
66 for entry in section.entries_mut() {
67 if let elements::External::Function(_) = *entry.external() {
68 f(entry.field_mut())
69 }
70 }
71 }
72
73 if let Some(section) = export_section(&mut module) {
74 for entry in section.entries_mut() {
75 if let elements::Internal::Function(_) = *entry.internal() {
76 f(entry.field_mut())
77 }
78 }
79 }
80
81 module
82}
83
84pub fn underscore_funcs(module: elements::Module) -> elements::Module {
85 foreach_public_func_name(module, |n| n.insert(0, '_'))
86}
87
88pub fn ununderscore_funcs(module: elements::Module) -> elements::Module {
89 foreach_public_func_name(module, |n| { n.remove(0); })
90}
91
92pub fn shrink_unknown_stack(
93 mut module: elements::Module,
94 shrink_amount: u32,
96) -> (elements::Module, u32) {
97 let mut new_stack_top = 0;
98 for section in module.sections_mut() {
99 match section {
100 elements::Section::Data(data_section) => {
101 for data_segment in data_section.entries_mut() {
102 if *data_segment
103 .offset()
104 .as_ref()
105 .expect("tetsy-wasm is compiled without bulk-memory operations")
106 .code() == [elements::Instruction::I32Const(4), elements::Instruction::End]
107 {
108 assert_eq!(data_segment.value().len(), 4);
109 let current_val = LittleEndian::read_u32(data_segment.value());
110 let new_val = current_val - shrink_amount;
111 LittleEndian::write_u32(data_segment.value_mut(), new_val);
112 new_stack_top = new_val;
113 }
114 }
115 },
116 _ => continue
117 }
118 }
119 (module, new_stack_top)
120}
121
122pub fn externalize(
123 module: elements::Module,
124 replaced_funcs: Vec<&str>,
125) -> elements::Module {
126 let import_funcs_total = module
128 .import_section().expect("Import section to exist")
129 .entries()
130 .iter()
131 .filter(|e| matches!(e.external(), &elements::External::Function(_)))
132 .count();
133
134 let mut replaces: Vec<Insertion> = replaced_funcs
137 .into_iter()
138 .filter_map(|f| {
139 let export = module
140 .export_section().expect("Export section to exist")
141 .entries().iter().enumerate()
142 .find(|&(_, entry)| entry.field() == f)
143 .expect("All functions of interest to exist");
144
145 if let elements::Internal::Function(func_idx) = *export.1.internal() {
146 let type_ref = module
147 .function_section().expect("Functions section to exist")
148 .entries()[func_idx as usize - import_funcs_total]
149 .type_ref();
150
151 Some((export.0, func_idx, type_ref, export.1.field().to_owned()))
152 } else {
153 None
154 }
155 })
156 .collect();
157
158 replaces.sort_by_key(|e| e.0);
159
160 let mut mbuilder = builder::from_module(module);
162 for (_, _, type_ref, field) in replaces.iter() {
163 mbuilder.push_import(
164 builder::import()
165 .module("env")
166 .field(field)
167 .external().func(*type_ref)
168 .build()
169 );
170 }
171
172 let mut module = mbuilder.build();
174
175 for section in module.sections_mut() {
177 match section {
178 elements::Section::Code(code_section) => {
179 for func_body in code_section.bodies_mut() {
180 update_call_index(func_body.code_mut(), import_funcs_total, &replaces);
181 }
182 },
183 elements::Section::Export(export_section) => {
184 for export in export_section.entries_mut() {
185 if let elements::Internal::Function(func_index) = export.internal_mut() {
186 if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; }
187 }
188 }
189 },
190 elements::Section::Element(elements_section) => {
191 for segment in elements_section.entries_mut() {
192 for func_index in segment.members_mut() {
194 if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; }
195 }
196 }
197 },
198 _ => { }
199 }
200 }
201
202 module
203
204}