gear_pwasm_utils/
graph.rs

1//! Wasm binary graph format
2
3#![warn(missing_docs)]
4
5use super::ref_list::{EntryRef, RefList};
6use crate::std::{borrow::ToOwned, collections::BTreeMap, string::String, vec::Vec};
7use parity_wasm::elements;
8
9/// Imported or declared variant of the same thing.
10///
11/// In WebAssembly, function/global/memory/table instances can either be
12/// imported or declared internally, forming united index space.
13#[derive(Debug)]
14pub enum ImportedOrDeclared<T = ()> {
15	/// Variant for imported instances.
16	Imported(String, String),
17	/// Variant for instances declared internally in the module.
18	Declared(T),
19}
20
21impl<T> From<&elements::ImportEntry> for ImportedOrDeclared<T> {
22	fn from(v: &elements::ImportEntry) -> Self {
23		ImportedOrDeclared::Imported(v.module().to_owned(), v.field().to_owned())
24	}
25}
26
27/// Error for this module
28#[derive(Debug)]
29pub enum Error {
30	/// Inconsistent source representation
31	InconsistentSource,
32	/// Format error
33	Format(elements::Error),
34	/// Detached entry
35	DetachedEntry,
36}
37
38/// Function origin (imported or internal).
39pub type FuncOrigin = ImportedOrDeclared<FuncBody>;
40/// Global origin (imported or internal).
41pub type GlobalOrigin = ImportedOrDeclared<Vec<Instruction>>;
42/// Memory origin (imported or internal).
43pub type MemoryOrigin = ImportedOrDeclared;
44/// Table origin (imported or internal).
45pub type TableOrigin = ImportedOrDeclared;
46
47/// Function body.
48///
49/// Function consist of declaration (signature, i.e. type reference)
50/// and the actual code. This part is the actual code.
51#[derive(Debug)]
52pub struct FuncBody {
53	pub locals: Vec<elements::Local>,
54	pub code: Vec<Instruction>,
55}
56
57/// Function declaration.
58///
59/// As with other instances, functions can be either imported or declared
60/// within the module - `origin` field is handling this.
61#[derive(Debug)]
62pub struct Func {
63	/// Function signature/type reference.
64	pub type_ref: EntryRef<elements::Type>,
65	/// Where this function comes from (imported or declared).
66	pub origin: FuncOrigin,
67}
68
69/// Global declaration.
70///
71/// As with other instances, globals can be either imported or declared
72/// within the module - `origin` field is handling this.
73#[derive(Debug)]
74pub struct Global {
75	pub content: elements::ValueType,
76	pub is_mut: bool,
77	pub origin: GlobalOrigin,
78}
79
80/// Instruction.
81///
82/// Some instructions don't reference any entities within the WebAssembly module,
83/// while others do. This enum is for tracking references when required.
84#[derive(Debug)]
85pub enum Instruction {
86	/// WebAssembly instruction that does not reference any module entities.
87	Plain(elements::Instruction),
88	/// Call instruction which references the function.
89	Call(EntryRef<Func>),
90	/// Indirect call instruction which references function type (function signature).
91	CallIndirect(EntryRef<elements::Type>, u8),
92	/// get_global instruction which references the global.
93	GetGlobal(EntryRef<Global>),
94	/// set_global instruction which references the global.
95	SetGlobal(EntryRef<Global>),
96}
97
98/// Memory instance decriptor.
99///
100/// As with other similar instances, memory instances can be either imported
101/// or declared within the module - `origin` field is handling this.
102#[derive(Debug)]
103pub struct Memory {
104	/// Declared limits of the table instance.
105	pub limits: elements::ResizableLimits,
106	/// Origin of the memory instance (internal or imported).
107	pub origin: MemoryOrigin,
108}
109
110/// Memory instance decriptor.
111///
112/// As with other similar instances, memory instances can be either imported
113/// or declared within the module - `origin` field is handling this.
114#[derive(Debug)]
115pub struct Table {
116	/// Declared limits of the table instance.
117	pub limits: elements::ResizableLimits,
118	/// Origin of the table instance (internal or imported).
119	pub origin: TableOrigin,
120}
121
122/// Segment location.
123///
124/// Reserved for future use. Currenty only `Default` variant is supported.
125#[derive(Debug)]
126pub enum SegmentLocation {
127	/// Not used currently.
128	Passive,
129	/// Default segment location with index `0`.
130	Default(Vec<Instruction>),
131	/// Not used currently.
132	WithIndex(u32, Vec<Instruction>),
133}
134
135/// Data segment of data section.
136#[derive(Debug)]
137pub struct DataSegment {
138	/// Location of the segment in the linear memory.
139	pub location: SegmentLocation,
140	/// Raw value of the data segment.
141	pub value: Vec<u8>,
142}
143
144/// Element segment of element section.
145#[derive(Debug)]
146pub struct ElementSegment {
147	/// Location of the segment in the table space.
148	pub location: SegmentLocation,
149	/// Raw value (function indices) of the element segment.
150	pub value: Vec<EntryRef<Func>>,
151}
152
153/// Export entry reference.
154///
155/// Module can export function, global, table or memory instance
156/// under specific name (field).
157#[derive(Debug)]
158pub enum ExportLocal {
159	/// Function reference.
160	Func(EntryRef<Func>),
161	/// Global reference.
162	Global(EntryRef<Global>),
163	/// Table reference.
164	Table(EntryRef<Table>),
165	/// Memory reference.
166	Memory(EntryRef<Memory>),
167}
168
169/// Export entry description.
170#[derive(Debug)]
171pub struct Export {
172	/// Name (field) of the export entry.
173	pub name: String,
174	/// What entity is exported.
175	pub local: ExportLocal,
176}
177
178/// Module
179#[derive(Debug, Default)]
180pub struct Module {
181	/// Refence-tracking list of types.
182	pub types: RefList<elements::Type>,
183	/// Refence-tracking list of funcs.
184	pub funcs: RefList<Func>,
185	/// Refence-tracking list of memory instances.
186	pub memory: RefList<Memory>,
187	/// Refence-tracking list of table instances.
188	pub tables: RefList<Table>,
189	/// Refence-tracking list of globals.
190	pub globals: RefList<Global>,
191	/// Reference to start function.
192	pub start: Option<EntryRef<Func>>,
193	/// References to exported objects.
194	pub exports: Vec<Export>,
195	/// List of element segments.
196	pub elements: Vec<ElementSegment>,
197	/// List of data segments.
198	pub data: Vec<DataSegment>,
199	/// Other module functions that are not decoded or processed.
200	pub other: BTreeMap<usize, elements::Section>,
201}
202
203impl Module {
204	fn map_instructions(&self, instructions: &[elements::Instruction]) -> Vec<Instruction> {
205		use parity_wasm::elements::Instruction::*;
206		instructions
207			.iter()
208			.map(|instruction| match instruction {
209				Call(func_idx) => Instruction::Call(self.funcs.clone_ref(*func_idx as usize)),
210				CallIndirect(type_idx, arg2) =>
211					Instruction::CallIndirect(self.types.clone_ref(*type_idx as usize), *arg2),
212				SetGlobal(global_idx) =>
213					Instruction::SetGlobal(self.globals.clone_ref(*global_idx as usize)),
214				GetGlobal(global_idx) =>
215					Instruction::GetGlobal(self.globals.clone_ref(*global_idx as usize)),
216				other_instruction => Instruction::Plain(other_instruction.clone()),
217			})
218			.collect()
219	}
220
221	fn generate_instructions(&self, instructions: &[Instruction]) -> Vec<elements::Instruction> {
222		use parity_wasm::elements::Instruction::*;
223		instructions
224			.iter()
225			.map(|instruction| match instruction {
226				Instruction::Call(func_ref) =>
227					Call(func_ref.order().expect("detached instruction!") as u32),
228				Instruction::CallIndirect(type_ref, arg2) =>
229					CallIndirect(type_ref.order().expect("detached instruction!") as u32, *arg2),
230				Instruction::SetGlobal(global_ref) =>
231					SetGlobal(global_ref.order().expect("detached instruction!") as u32),
232				Instruction::GetGlobal(global_ref) =>
233					GetGlobal(global_ref.order().expect("detached instruction!") as u32),
234				Instruction::Plain(plain) => plain.clone(),
235			})
236			.collect()
237	}
238
239	/// Initialize module from parity-wasm `Module`.
240	pub fn from_elements(module: &elements::Module) -> Result<Self, Error> {
241		let mut res = Module::default();
242		let mut imported_functions = 0;
243
244		for (idx, section) in module.sections().iter().enumerate() {
245			match section {
246				elements::Section::Type(type_section) => {
247					res.types = RefList::from_slice(type_section.types());
248				},
249				elements::Section::Import(import_section) =>
250					for entry in import_section.entries() {
251						match *entry.external() {
252							elements::External::Function(f) => {
253								res.funcs.push(Func {
254									type_ref: res
255										.types
256										.get(f as usize)
257										.ok_or(Error::InconsistentSource)?
258										.clone(),
259									origin: entry.into(),
260								});
261								imported_functions += 1;
262							},
263							elements::External::Memory(m) => {
264								res.memory
265									.push(Memory { limits: *m.limits(), origin: entry.into() });
266							},
267							elements::External::Global(g) => {
268								res.globals.push(Global {
269									content: g.content_type(),
270									is_mut: g.is_mutable(),
271									origin: entry.into(),
272								});
273							},
274							elements::External::Table(t) => {
275								res.tables
276									.push(Table { limits: *t.limits(), origin: entry.into() });
277							},
278						};
279					},
280				elements::Section::Function(function_section) => {
281					for f in function_section.entries() {
282						res.funcs.push(Func {
283							type_ref: res
284								.types
285								.get(f.type_ref() as usize)
286								.ok_or(Error::InconsistentSource)?
287								.clone(),
288							origin: ImportedOrDeclared::Declared(FuncBody {
289								locals: Vec::new(),
290								// code will be populated later
291								code: Vec::new(),
292							}),
293						});
294					}
295				},
296				elements::Section::Table(table_section) =>
297					for t in table_section.entries() {
298						res.tables.push(Table {
299							limits: *t.limits(),
300							origin: ImportedOrDeclared::Declared(()),
301						});
302					},
303				elements::Section::Memory(table_section) =>
304					for t in table_section.entries() {
305						res.memory.push(Memory {
306							limits: *t.limits(),
307							origin: ImportedOrDeclared::Declared(()),
308						});
309					},
310				elements::Section::Global(global_section) =>
311					for g in global_section.entries() {
312						let init_code = res.map_instructions(g.init_expr().code());
313						res.globals.push(Global {
314							content: g.global_type().content_type(),
315							is_mut: g.global_type().is_mutable(),
316							origin: ImportedOrDeclared::Declared(init_code),
317						});
318					},
319				elements::Section::Export(export_section) =>
320					for e in export_section.entries() {
321						let local = match e.internal() {
322							elements::Internal::Function(func_idx) =>
323								ExportLocal::Func(res.funcs.clone_ref(*func_idx as usize)),
324							elements::Internal::Global(global_idx) =>
325								ExportLocal::Global(res.globals.clone_ref(*global_idx as usize)),
326							elements::Internal::Memory(mem_idx) =>
327								ExportLocal::Memory(res.memory.clone_ref(*mem_idx as usize)),
328							elements::Internal::Table(table_idx) =>
329								ExportLocal::Table(res.tables.clone_ref(*table_idx as usize)),
330						};
331
332						res.exports.push(Export { local, name: e.field().to_owned() })
333					},
334				elements::Section::Start(start_func) => {
335					res.start = Some(res.funcs.clone_ref(*start_func as usize));
336				},
337				elements::Section::Element(element_section) => {
338					for element_segment in element_section.entries() {
339						// let location = if element_segment.passive() {
340						// 	SegmentLocation::Passive
341						// } else if element_segment.index() == 0 {
342						// 	SegmentLocation::Default(Vec::new())
343						// } else {
344						// 	SegmentLocation::WithIndex(element_segment.index(), Vec::new())
345						// };
346
347						// TODO: update parity-wasm and uncomment the above instead
348						let init_expr = element_segment
349							.offset()
350							.as_ref()
351							.expect("parity-wasm is compiled without bulk-memory operations")
352							.code();
353						let location = SegmentLocation::Default(res.map_instructions(init_expr));
354
355						let funcs_map = element_segment
356							.members()
357							.iter()
358							.map(|idx| res.funcs.clone_ref(*idx as usize))
359							.collect::<Vec<EntryRef<Func>>>();
360
361						res.elements.push(ElementSegment { value: funcs_map, location });
362					}
363				},
364				elements::Section::Code(code_section) => {
365					for (idx, func_body) in code_section.bodies().iter().enumerate() {
366						let code = res.map_instructions(func_body.code().elements());
367						let mut func = res.funcs.get_ref(imported_functions + idx).write();
368						match &mut func.origin {
369							ImportedOrDeclared::Declared(body) => {
370								body.code = code;
371								body.locals = func_body.locals().to_vec();
372							},
373							_ => return Err(Error::InconsistentSource),
374						}
375					}
376				},
377				elements::Section::Data(data_section) => {
378					for data_segment in data_section.entries() {
379						// TODO: update parity-wasm and use the same logic as in
380						// commented element segment branch
381						let init_expr = data_segment
382							.offset()
383							.as_ref()
384							.expect("parity-wasm is compiled without bulk-memory operations")
385							.code();
386						let location = SegmentLocation::Default(res.map_instructions(init_expr));
387
388						res.data
389							.push(DataSegment { value: data_segment.value().to_vec(), location });
390					}
391				},
392				_ => {
393					res.other.insert(idx, section.clone());
394				},
395			}
396		}
397
398		Ok(res)
399	}
400
401	/// Generate raw format representation.
402	pub fn generate(&self) -> Result<elements::Module, Error> {
403		use self::ImportedOrDeclared::*;
404
405		let mut idx = 0;
406		let mut sections = Vec::new();
407
408		custom_round(&self.other, &mut idx, &mut sections);
409
410		if !self.types.is_empty() {
411			// TYPE SECTION (1)
412			let mut type_section = elements::TypeSection::default();
413			{
414				let types = type_section.types_mut();
415
416				for type_entry in self.types.iter() {
417					types.push(type_entry.read().clone())
418				}
419			}
420			sections.push(elements::Section::Type(type_section));
421			idx += 1;
422
423			custom_round(&self.other, &mut idx, &mut sections);
424		}
425
426		// IMPORT SECTION (2)
427		let mut import_section = elements::ImportSection::default();
428
429		let add = {
430			let imports = import_section.entries_mut();
431			for func in self.funcs.iter() {
432				match &func.read().origin {
433					Imported(module, field) => imports.push(elements::ImportEntry::new(
434						module.to_owned(),
435						field.to_owned(),
436						elements::External::Function(
437							func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32,
438						),
439					)),
440					_ => continue,
441				}
442			}
443
444			for global in self.globals.iter() {
445				match &global.read().origin {
446					Imported(module, field) => imports.push(elements::ImportEntry::new(
447						module.to_owned(),
448						field.to_owned(),
449						elements::External::Global(elements::GlobalType::new(
450							global.read().content,
451							global.read().is_mut,
452						)),
453					)),
454					_ => continue,
455				}
456			}
457
458			for memory in self.memory.iter() {
459				match &memory.read().origin {
460					Imported(module, field) => imports.push(elements::ImportEntry::new(
461						module.to_owned(),
462						field.to_owned(),
463						elements::External::Memory(elements::MemoryType::new(
464							memory.read().limits.initial(),
465							memory.read().limits.maximum(),
466						)),
467					)),
468					_ => continue,
469				}
470			}
471
472			for table in self.tables.iter() {
473				match &table.read().origin {
474					Imported(module, field) => imports.push(elements::ImportEntry::new(
475						module.to_owned(),
476						field.to_owned(),
477						elements::External::Table(elements::TableType::new(
478							table.read().limits.initial(),
479							table.read().limits.maximum(),
480						)),
481					)),
482					_ => continue,
483				}
484			}
485			!imports.is_empty()
486		};
487
488		if add {
489			sections.push(elements::Section::Import(import_section));
490			idx += 1;
491			custom_round(&self.other, &mut idx, &mut sections);
492		}
493
494		if !self.funcs.is_empty() {
495			// FUNC SECTION (3)
496			let mut func_section = elements::FunctionSection::default();
497			{
498				let funcs = func_section.entries_mut();
499
500				for func in self.funcs.iter() {
501					match func.read().origin {
502						Declared(_) => {
503							funcs.push(elements::Func::new(
504								func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32,
505							));
506						},
507						_ => continue,
508					}
509				}
510			}
511			sections.push(elements::Section::Function(func_section));
512			idx += 1;
513
514			custom_round(&self.other, &mut idx, &mut sections);
515		}
516
517		if !self.tables.is_empty() {
518			// TABLE SECTION (4)
519			let mut table_section = elements::TableSection::default();
520			{
521				let tables = table_section.entries_mut();
522
523				for table in self.tables.iter() {
524					match table.read().origin {
525						Declared(_) => {
526							tables.push(elements::TableType::new(
527								table.read().limits.initial(),
528								table.read().limits.maximum(),
529							));
530						},
531						_ => continue,
532					}
533				}
534			}
535			sections.push(elements::Section::Table(table_section));
536			idx += 1;
537
538			custom_round(&self.other, &mut idx, &mut sections);
539		}
540
541		if !self.memory.is_empty() {
542			// MEMORY SECTION (5)
543			let mut memory_section = elements::MemorySection::default();
544			{
545				let memories = memory_section.entries_mut();
546
547				for memory in self.memory.iter() {
548					match memory.read().origin {
549						Declared(_) => {
550							memories.push(elements::MemoryType::new(
551								memory.read().limits.initial(),
552								memory.read().limits.maximum(),
553							));
554						},
555						_ => continue,
556					}
557				}
558			}
559			sections.push(elements::Section::Memory(memory_section));
560			idx += 1;
561
562			custom_round(&self.other, &mut idx, &mut sections);
563		}
564
565		if !self.globals.is_empty() {
566			// GLOBAL SECTION (6)
567			let mut global_section = elements::GlobalSection::default();
568			{
569				let globals = global_section.entries_mut();
570
571				for global in self.globals.iter() {
572					match &global.read().origin {
573						Declared(init_code) => {
574							globals.push(elements::GlobalEntry::new(
575								elements::GlobalType::new(
576									global.read().content,
577									global.read().is_mut,
578								),
579								elements::InitExpr::new(self.generate_instructions(&init_code[..])),
580							));
581						},
582						_ => continue,
583					}
584				}
585			}
586			sections.push(elements::Section::Global(global_section));
587			idx += 1;
588
589			custom_round(&self.other, &mut idx, &mut sections);
590		}
591
592		if !self.exports.is_empty() {
593			// EXPORT SECTION (7)
594			let mut export_section = elements::ExportSection::default();
595			{
596				let exports = export_section.entries_mut();
597
598				for export in self.exports.iter() {
599					let internal = match &export.local {
600						ExportLocal::Func(func_ref) => elements::Internal::Function(
601							func_ref.order().ok_or(Error::DetachedEntry)? as u32,
602						),
603						ExportLocal::Global(global_ref) => elements::Internal::Global(
604							global_ref.order().ok_or(Error::DetachedEntry)? as u32,
605						),
606						ExportLocal::Table(table_ref) => elements::Internal::Table(
607							table_ref.order().ok_or(Error::DetachedEntry)? as u32,
608						),
609						ExportLocal::Memory(memory_ref) => elements::Internal::Memory(
610							memory_ref.order().ok_or(Error::DetachedEntry)? as u32,
611						),
612					};
613
614					exports.push(elements::ExportEntry::new(export.name.to_owned(), internal));
615				}
616			}
617			sections.push(elements::Section::Export(export_section));
618			idx += 1;
619
620			custom_round(&self.other, &mut idx, &mut sections);
621		}
622
623		if let Some(func_ref) = &self.start {
624			// START SECTION (8)
625			sections.push(elements::Section::Start(
626				func_ref.order().ok_or(Error::DetachedEntry)? as u32
627			));
628		}
629
630		if !self.elements.is_empty() {
631			// START SECTION (9)
632			let mut element_section = elements::ElementSection::default();
633			{
634				let element_segments = element_section.entries_mut();
635
636				for element in self.elements.iter() {
637					match &element.location {
638						SegmentLocation::Default(offset_expr) => {
639							let mut elements_map = Vec::new();
640							for f in element.value.iter() {
641								elements_map.push(f.order().ok_or(Error::DetachedEntry)? as u32);
642							}
643
644							element_segments.push(elements::ElementSegment::new(
645								0,
646								Some(elements::InitExpr::new(
647									self.generate_instructions(&offset_expr[..]),
648								)),
649								elements_map,
650							));
651						},
652						_ => unreachable!("Other segment location types are never added"),
653					}
654				}
655			}
656
657			sections.push(elements::Section::Element(element_section));
658			idx += 1;
659
660			custom_round(&self.other, &mut idx, &mut sections);
661		}
662
663		if !self.funcs.is_empty() {
664			// CODE SECTION (10)
665			let mut code_section = elements::CodeSection::default();
666			{
667				let funcs = code_section.bodies_mut();
668
669				for func in self.funcs.iter() {
670					match &func.read().origin {
671						Declared(body) => {
672							funcs.push(elements::FuncBody::new(
673								body.locals.clone(),
674								elements::Instructions::new(
675									self.generate_instructions(&body.code[..]),
676								),
677							));
678						},
679						_ => continue,
680					}
681				}
682			}
683			sections.push(elements::Section::Code(code_section));
684			idx += 1;
685
686			custom_round(&self.other, &mut idx, &mut sections);
687		}
688
689		if !self.data.is_empty() {
690			// DATA SECTION (11)
691			let mut data_section = elements::DataSection::default();
692			{
693				let data_segments = data_section.entries_mut();
694
695				for data_entry in self.data.iter() {
696					match &data_entry.location {
697						SegmentLocation::Default(offset_expr) => {
698							data_segments.push(elements::DataSegment::new(
699								0,
700								Some(elements::InitExpr::new(
701									self.generate_instructions(&offset_expr[..]),
702								)),
703								data_entry.value.clone(),
704							));
705						},
706						_ => unreachable!("Other segment location types are never added"),
707					}
708				}
709			}
710
711			sections.push(elements::Section::Data(data_section));
712			idx += 1;
713
714			custom_round(&self.other, &mut idx, &mut sections);
715		}
716
717		Ok(elements::Module::new(sections))
718	}
719}
720
721fn custom_round(
722	map: &BTreeMap<usize, elements::Section>,
723	idx: &mut usize,
724	sections: &mut Vec<elements::Section>,
725) {
726	while let Some(other_section) = map.get(idx) {
727		sections.push(other_section.clone());
728		*idx += 1;
729	}
730}
731
732/// New module from parity-wasm `Module`
733pub fn parse(wasm: &[u8]) -> Result<Module, Error> {
734	Module::from_elements(&::parity_wasm::deserialize_buffer(wasm).map_err(Error::Format)?)
735}
736
737/// Generate parity-wasm `Module`
738pub fn generate(f: &Module) -> Result<Vec<u8>, Error> {
739	let pm = f.generate()?;
740	::parity_wasm::serialize(pm).map_err(Error::Format)
741}
742
743#[cfg(test)]
744mod tests {
745	use indoc::indoc;
746	use parity_wasm::elements;
747
748	fn load_sample(wat: &'static str) -> super::Module {
749		super::parse(&wabt::wat2wasm(wat).expect("faled to parse wat!")[..])
750			.expect("error making representation")
751	}
752
753	fn validate_sample(module: &super::Module) {
754		let binary = super::generate(module).expect("Failed to generate binary");
755		wabt::Module::read_binary(&binary, &Default::default())
756			.expect("Wabt failed to read final binary")
757			.validate()
758			.expect("Invalid module");
759	}
760
761	#[test]
762	fn smoky() {
763		let sample = load_sample(indoc!(
764			r#"
765			(module
766				(type (func))
767				(func (type 0))
768				(memory 0 1)
769				(export "simple" (func 0)))"#
770		));
771
772		assert_eq!(sample.types.len(), 1);
773		assert_eq!(sample.funcs.len(), 1);
774		assert_eq!(sample.tables.len(), 0);
775		assert_eq!(sample.memory.len(), 1);
776		assert_eq!(sample.exports.len(), 1);
777
778		assert_eq!(sample.types.get_ref(0).link_count(), 1);
779		assert_eq!(sample.funcs.get_ref(0).link_count(), 1);
780	}
781
782	#[test]
783	fn table() {
784		let mut sample = load_sample(indoc!(
785			r#"
786			(module
787				(import "env" "foo" (func $foo))
788				(func (param i32)
789					get_local 0
790					i32.const 0
791					call $i32.add
792					drop
793				)
794				(func $i32.add (export "i32.add") (param i32 i32) (result i32)
795					get_local 0
796					get_local 1
797					i32.add
798				)
799				(table 10 anyfunc)
800
801				;; Refer all types of functions: imported, defined not exported and defined exported.
802				(elem (i32.const 0) 0 1 2)
803			)"#
804		));
805
806		{
807			let element_func = &sample.elements[0].value[1];
808			let rfunc = element_func.read();
809			let rtype = &**rfunc.type_ref.read();
810			let elements::Type::Function(ftype) = rtype;
811
812			// it's func#1 in the function  space
813			assert_eq!(rfunc.order(), Some(1));
814			// it's type#1
815			assert_eq!(ftype.params().len(), 1);
816		}
817
818		sample.funcs.begin_delete().push(0).done();
819
820		{
821			let element_func = &sample.elements[0].value[1];
822			let rfunc = element_func.read();
823			let rtype = &**rfunc.type_ref.read();
824			let elements::Type::Function(ftype) = rtype;
825
826			// import deleted so now it's func #0
827			assert_eq!(rfunc.order(), Some(0));
828			// type should be the same, #1
829			assert_eq!(ftype.params().len(), 1);
830		}
831	}
832
833	#[test]
834	fn new_import() {
835		let mut sample = load_sample(indoc!(
836			r#"
837			(module
838				(type (;0;) (func))
839				(type (;1;) (func (param i32 i32) (result i32)))
840				(import "env" "foo" (func (type 1)))
841				(func (param i32)
842					get_local 0
843					i32.const 0
844					call 0
845					drop
846				)
847				(func (type 0)
848					i32.const 0
849					call 1
850				)
851			)"#
852		));
853
854		{
855			let type_ref_0 = sample.types.clone_ref(0);
856			let declared_func_2 = sample.funcs.clone_ref(2);
857
858			let mut tx = sample.funcs.begin_insert_not_until(|f| {
859				matches!(f.origin, super::ImportedOrDeclared::Imported(_, _))
860			});
861
862			let new_import_func = tx.push(super::Func {
863				type_ref: type_ref_0,
864				origin: super::ImportedOrDeclared::Imported("env".to_owned(), "bar".to_owned()),
865			});
866
867			tx.done();
868
869			assert_eq!(new_import_func.order(), Some(1));
870			assert_eq!(declared_func_2.order(), Some(3));
871			assert_eq!(
872				match &declared_func_2.read().origin {
873					super::ImportedOrDeclared::Declared(body) => {
874						match &body.code[1] {
875							super::Instruction::Call(called_func) => called_func.order(),
876							_ => panic!("instruction #2 should be a call!"),
877						}
878					},
879					_ => panic!("func #3 should be declared!"),
880				},
881				Some(2),
882				"Call should be recalculated to 2"
883			);
884		}
885
886		validate_sample(&sample);
887	}
888
889	#[test]
890	fn simple_opt() {
891		let mut sample = load_sample(indoc!(
892			r#"
893			(module
894				(type (;0;) (func))
895				(type (;1;) (func (param i32 i32) (result i32)))
896				(type (;2;) (func (param i32 i32) (result i32)))
897				(type (;3;) (func (param i32 i32) (result i32)))
898				(import "env" "foo" (func (type 1)))
899				(import "env" "foo2" (func (type 2)))
900				(import "env" "foo3" (func (type 3)))
901				(func (type 0)
902					i32.const 1
903					i32.const 1
904					call 0
905					drop
906				)
907				(func (type 0)
908					i32.const 2
909					i32.const 2
910					call 1
911					drop
912				)
913				(func (type 0)
914					i32.const 3
915					i32.const 3
916					call 2
917					drop
918				)
919				(func (type 0)
920					call 3
921				)
922			)"#
923		));
924
925		validate_sample(&sample);
926
927		// we'll delete functions #4 and #5, nobody references it so it should be fine;
928
929		sample.funcs.begin_delete().push(4).push(5).done();
930		validate_sample(&sample);
931
932		// now we'll delete functions #1 and #2 (imported and called from the deleted above),
933		// should also be fine
934		sample.funcs.begin_delete().push(1).push(2).done();
935		validate_sample(&sample);
936
937		// now the last declared function left should call another one before it (which is index #1)
938		let declared_func_2 = sample.funcs.clone_ref(2);
939		assert_eq!(
940			match &declared_func_2.read().origin {
941				super::ImportedOrDeclared::Declared(body) => {
942					match &body.code[0] {
943						super::Instruction::Call(called_func) => called_func.order(),
944						wrong_instruction => panic!(
945							"instruction #2 should be a call but got {:?}!",
946							wrong_instruction
947						),
948					}
949				},
950				_ => panic!("func #0 should be declared!"),
951			},
952			Some(1),
953			"Call should be recalculated to 1"
954		);
955	}
956}