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