1use crate::std::fmt;
2use crate::std::vec::Vec;
3use crate::std::borrow::ToOwned;
4
5use tetsy_wasm::elements::{
6 self, Section, DataSection, Instruction, DataSegment, InitExpr, Internal, External,
7 ImportCountType,
8};
9use tetsy_wasm::builder;
10use super::TargetRuntime;
11use super::gas::update_call_index;
12
13#[derive(Debug)]
18pub enum Error {
19 MalformedModule,
20 NoTypeSection,
21 NoExportSection,
22 NoCodeSection,
23 InvalidCreateSignature(&'static str),
24 NoCreateSymbol(&'static str),
25 InvalidCreateMember(&'static str),
26 NoImportSection,
27}
28
29impl fmt::Display for Error {
30 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
31 match *self {
32 Error::MalformedModule => write!(f, "Module internal references are inconsistent"),
33 Error::NoTypeSection => write!(f, "No type section in the module"),
34 Error::NoExportSection => write!(f, "No export section in the module"),
35 Error::NoCodeSection => write!(f, "No code section inthe module"),
36 Error::InvalidCreateSignature(sym) => write!(f, "Exported symbol `{}` has invalid signature, should be () -> ()", sym),
37 Error::InvalidCreateMember(sym) => write!(f, "Exported symbol `{}` should be a function", sym),
38 Error::NoCreateSymbol(sym) => write!(f, "No exported `{}` symbol", sym),
39 Error::NoImportSection => write!(f, "No import section in the module"),
40 }
41 }
42}
43
44pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module, target: &TargetRuntime) -> Result<elements::Module, Error> {
48
49 let ctor_import_functions = ctor_module.import_section().map(|x| x.functions()).unwrap_or(0);
51
52 let mut create_func_id = {
55 let found_entry = ctor_module.export_section().ok_or(Error::NoExportSection)?.entries().iter()
56 .find(|entry| target.symbols().create == entry.field()).ok_or_else(|| Error::NoCreateSymbol(target.symbols().create))?;
57
58 let function_index: usize = match found_entry.internal() {
59 Internal::Function(index) => *index as usize,
60 _ => { return Err(Error::InvalidCreateMember(target.symbols().create)) },
61 };
62
63 let function_internal_index = function_index - ctor_import_functions;
65
66 let type_id = ctor_module.function_section().ok_or(Error::NoCodeSection)?
68 .entries().get(function_index - ctor_import_functions).ok_or(Error::MalformedModule)?
69 .type_ref();
70
71 let elements::Type::Function(func) = ctor_module.type_section().ok_or(Error::NoTypeSection)?
72 .types().get(type_id as usize).ok_or(Error::MalformedModule)?;
73
74 if !func.params().is_empty() {
76 return Err(Error::InvalidCreateSignature(target.symbols().create));
77 }
78 if !func.results().is_empty() {
79 return Err(Error::InvalidCreateSignature(target.symbols().create));
80 }
81
82 function_internal_index
83 };
84
85 let ret_function_id = {
86 let mut id = 0;
87 let mut found = false;
88 for entry in ctor_module.import_section().ok_or(Error::NoImportSection)?.entries().iter() {
89 if let External::Function(_) = *entry.external() {
90 if entry.field() == target.symbols().ret { found = true; break; }
91 else { id += 1; }
92 }
93 }
94 if !found {
95 let mut mbuilder = builder::from_module(ctor_module);
96 let import_sig = mbuilder.push_signature(
97 builder::signature()
98 .param().i32().param().i32()
99 .build_sig()
100 );
101
102 mbuilder.push_import(
103 builder::import()
104 .module("env")
105 .field(&target.symbols().ret)
106 .external().func(import_sig)
107 .build()
108 );
109
110 ctor_module = mbuilder.build();
111
112 let ret_func = ctor_module.import_count(ImportCountType::Function) as u32 - 1;
113
114 for section in ctor_module.sections_mut() {
115 match section {
116 elements::Section::Code(code_section) => {
117 for func_body in code_section.bodies_mut() {
118 update_call_index(func_body.code_mut(), ret_func);
119 }
120 },
121 elements::Section::Export(export_section) => {
122 for export in export_section.entries_mut() {
123 if let elements::Internal::Function(func_index) = export.internal_mut() {
124 if *func_index >= ret_func { *func_index += 1}
125 }
126 }
127 },
128 elements::Section::Element(elements_section) => {
129 for segment in elements_section.entries_mut() {
130 for func_index in segment.members_mut() {
132 if *func_index >= ret_func { *func_index += 1}
133 }
134 }
135 },
136 _ => { }
137 }
138 }
139
140 create_func_id += 1;
141 ret_func
142 }
143 else { id }
144 };
145
146 let last_function_index = ctor_module.functions_space();
148
149 if ctor_module
151 .sections()
152 .iter()
153 .find(|section| matches!(**section, Section::Data(_)))
154 .is_none() {
155 ctor_module.sections_mut().push(Section::Data(DataSection::with_entries(vec![])));
157 }
158
159 let mut code_data_address = 0i32;
161
162 for section in ctor_module.sections_mut() {
163 if let Section::Data(data_section) = section {
164 let (index, offset) = if let Some(entry) = data_section.entries().iter().last() {
165 let init_expr = entry
166 .offset()
167 .as_ref()
168 .expect("tetsy-wasm is compiled without bulk-memory operations")
169 .code();
170 if let Instruction::I32Const(offst) = init_expr[0] {
171 let len = entry.value().len() as i32;
172 let offst = offst as i32;
173 (entry.index(), offst + (len + 4) - len % 4)
174 } else {
175 (0, 0)
176 }
177 } else {
178 (0, 0)
179 };
180 let code_data = DataSegment::new(
181 index,
182 Some(InitExpr::new(vec![Instruction::I32Const(offset), Instruction::End])),
183 raw_module.clone()
184 );
185 data_section.entries_mut().push(code_data);
186 code_data_address = offset;
187 }
188 }
189
190 let mut new_module = builder::from_module(ctor_module)
191 .function()
192 .signature().build()
193 .body().with_instructions(elements::Instructions::new(
194 vec![
195 Instruction::Call((create_func_id + ctor_import_functions) as u32),
196 Instruction::I32Const(code_data_address),
197 Instruction::I32Const(raw_module.len() as i32),
198 Instruction::Call(ret_function_id as u32),
199 Instruction::End,
200 ])).build()
201 .build()
202 .build();
203
204 for section in new_module.sections_mut() {
205 if let Section::Export(export_section) = section {
206 for entry in export_section.entries_mut().iter_mut() {
207 if target.symbols().create == entry.field() {
208 *entry.field_mut() = target.symbols().call.to_owned();
210 *entry.internal_mut() = elements::Internal::Function(last_function_index as u32);
211 }
212 }
213 }
214 };
215
216 Ok(new_module)
217}
218
219#[cfg(test)]
220mod test {
221 extern crate tetsy_wasm;
222
223 use tetsy_wasm::builder;
224 use super::*;
225 use super::super::optimize;
226
227 fn test_packer(mut module: elements::Module, target_runtime: &TargetRuntime) {
228 let mut ctor_module = module.clone();
229 optimize(&mut module, vec![target_runtime.symbols().call]).expect("Optimizer to finish without errors");
230 optimize(&mut ctor_module, vec![target_runtime.symbols().create]).expect("Optimizer to finish without errors");
231
232 let raw_module = tetsy_wasm::serialize(module).unwrap();
233 let ctor_module = pack_instance(raw_module.clone(), ctor_module, target_runtime).expect("Packing failed");
234
235 let data_section = ctor_module.data_section().expect("Packed module has to have a data section");
236 let data_segment = data_section.entries().iter().last().expect("Packed module has to have a data section with at least one entry");
237 assert!(data_segment.value() == AsRef::<[u8]>::as_ref(&raw_module), "Last data segment should be equal to the raw module");
238 }
239
240 #[test]
241 fn no_data_section() {
242 let target_runtime = TargetRuntime::twasm();
243
244 test_packer(builder::module()
245 .import()
246 .module("env")
247 .field("memory")
248 .external().memory(1 as u32, Some(1 as u32))
249 .build()
250 .function()
251 .signature()
252 .params().i32().i32().build()
253 .build()
254 .body().build()
255 .build()
256 .function()
257 .signature().build()
258 .body()
259 .with_instructions(elements::Instructions::new(
260 vec![
261 elements::Instruction::End
262 ]
263 ))
264 .build()
265 .build()
266 .function()
267 .signature().build()
268 .body()
269 .with_instructions(elements::Instructions::new(
270 vec![
271 elements::Instruction::End
272 ]
273 ))
274 .build()
275 .build()
276 .export()
277 .field(target_runtime.symbols().call)
278 .internal().func(1)
279 .build()
280 .export()
281 .field(target_runtime.symbols().create)
282 .internal().func(2)
283 .build()
284 .build(),
285 &target_runtime,
286 );
287 }
288
289 #[test]
290 fn with_data_section() {
291 let target_runtime = TargetRuntime::twasm();
292
293 test_packer(builder::module()
294 .import()
295 .module("env")
296 .field("memory")
297 .external().memory(1 as u32, Some(1 as u32))
298 .build()
299 .data()
300 .offset(elements::Instruction::I32Const(16)).value(vec![0u8])
301 .build()
302 .function()
303 .signature()
304 .params().i32().i32().build()
305 .build()
306 .body().build()
307 .build()
308 .function()
309 .signature().build()
310 .body()
311 .with_instructions(elements::Instructions::new(
312 vec![
313 elements::Instruction::End
314 ]
315 ))
316 .build()
317 .build()
318 .function()
319 .signature().build()
320 .body()
321 .with_instructions(elements::Instructions::new(
322 vec![
323 elements::Instruction::End
324 ]
325 ))
326 .build()
327 .build()
328 .export()
329 .field(target_runtime.symbols().call)
330 .internal().func(1)
331 .build()
332 .export()
333 .field(target_runtime.symbols().create)
334 .internal().func(2)
335 .build()
336 .build(),
337 &target_runtime,
338 );
339 }
340}