twasm_utils/
pack.rs

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/// Pack error.
14///
15/// Pack has number of assumptions of passed module structure.
16/// When they are violated, pack_instance returns one of these.
17#[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
44/// If a twasm module has an exported function matching "create" symbol we want to pack it into "constructor".
45/// `raw_module` is the actual contract code
46/// `ctor_module` is the constructor which should return `raw_module`
47pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module, target: &TargetRuntime) -> Result<elements::Module, Error> {
48
49	// Total number of constructor module import functions
50	let ctor_import_functions = ctor_module.import_section().map(|x| x.functions()).unwrap_or(0);
51
52	// We need to find an internal ID of function which is exported as `symbols().create`
53	// in order to find it in the Code section of the module
54	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		// Calculates a function index within module's function section
64		let function_internal_index = function_index - ctor_import_functions;
65
66		// Constructor should be of signature `func()` (void), fail otherwise
67		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		// Deploy should have no arguments and also should return nothing
75		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							// update all indirect call addresses initial values
131							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	// If new function is put in ctor module, it will have this callable index
147	let last_function_index = ctor_module.functions_space();
148
149	// We ensure here that module has the DataSection
150	if ctor_module
151		.sections()
152		.iter()
153		.find(|section| matches!(**section, Section::Data(_)))
154		.is_none() {
155		// DataSection has to be the last non-custom section according the to the spec
156		ctor_module.sections_mut().push(Section::Data(DataSection::with_entries(vec![])));
157	}
158
159	// Code data address is an address where we put the contract's code (raw_module)
160	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					// change `create` symbol export name into default `call` symbol name.
209					*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}