xwasm_utils/
graph.rs

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