twasm_utils/
graph.rs

1//! Wasm binary graph format
2
3#![warn(missing_docs)]
4
5use tetsy_wasm::elements;
6use super::ref_list::{RefList, EntryRef};
7use crate::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 tetsy_wasm::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 tetsy_wasm::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 tetsy-wasm `Module`.
239	pub fn from_elements(module: &elements::Module) -> Result<Self, Error> {
240
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.types.get(f as usize).ok_or(Error::InconsistentSource)?.clone(),
255									origin: entry.into(),
256								});
257								imported_functions += 1;
258							},
259							elements::External::Memory(m) => {
260								res.memory.push(Memory {
261									limits: *m.limits(),
262									origin: entry.into(),
263								});
264							},
265							elements::External::Global(g) => {
266								res.globals.push(Global {
267									content: g.content_type(),
268									is_mut: g.is_mutable(),
269									origin: entry.into(),
270								});
271							},
272							elements::External::Table(t) => {
273								res.tables.push(Table {
274									limits: *t.limits(),
275									origin: entry.into(),
276								});
277							},
278						};
279					}
280				},
281				elements::Section::Function(function_section) => {
282					for f in function_section.entries() {
283						res.funcs.push(Func {
284							type_ref: res.types.get(f.type_ref() as usize)
285								.ok_or(Error::InconsistentSource)?.clone(),
286							origin: ImportedOrDeclared::Declared(FuncBody {
287								locals: Vec::new(),
288								// code will be populated later
289								code: Vec::new(),
290							}),
291						});
292					};
293				},
294				elements::Section::Table(table_section) => {
295					for t in table_section.entries() {
296						res.tables.push(Table {
297							limits: *t.limits(),
298							origin: ImportedOrDeclared::Declared(()),
299						});
300					}
301				},
302				elements::Section::Memory(table_section) => {
303					for t in table_section.entries() {
304						res.memory.push(Memory {
305							limits: *t.limits(),
306							origin: ImportedOrDeclared::Declared(()),
307						});
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				},
320				elements::Section::Export(export_section) => {
321					for e in export_section.entries() {
322						let local = match e.internal() {
323							elements::Internal::Function(func_idx) => {
324								ExportLocal::Func(res.funcs.clone_ref(*func_idx as usize))
325							},
326							elements::Internal::Global(global_idx) => {
327								ExportLocal::Global(res.globals.clone_ref(*global_idx as usize))
328							},
329							elements::Internal::Memory(mem_idx) => {
330								ExportLocal::Memory(res.memory.clone_ref(*mem_idx as usize))
331							},
332							elements::Internal::Table(table_idx) => {
333								ExportLocal::Table(res.tables.clone_ref(*table_idx as usize))
334							},
335						};
336
337						res.exports.push(Export { local, name: e.field().to_owned() })
338					}
339				},
340				elements::Section::Start(start_func) => {
341					res.start = Some(res.funcs.clone_ref(*start_func as usize));
342				},
343				elements::Section::Element(element_section) => {
344					for element_segment in element_section.entries() {
345
346						// let location = if element_segment.passive() {
347						// 	SegmentLocation::Passive
348						// } else if element_segment.index() == 0 {
349						// 	SegmentLocation::Default(Vec::new())
350						// } else {
351						// 	SegmentLocation::WithIndex(element_segment.index(), Vec::new())
352						// };
353
354						// TODO: update tetsy-wasm and uncomment the above instead
355						let init_expr = element_segment
356							.offset()
357							.as_ref()
358							.expect("tetsy-wasm is compiled without bulk-memory operations")
359							.code();
360						let location = SegmentLocation::Default(res.map_instructions(init_expr));
361
362						let funcs_map = element_segment
363							.members().iter()
364							.map(|idx| res.funcs.clone_ref(*idx as usize))
365							.collect::<Vec<EntryRef<Func>>>();
366
367						res.elements.push(ElementSegment {
368							value: funcs_map,
369							location,
370						});
371					}
372				},
373				elements::Section::Code(code_section) => {
374					for (idx, func_body) in code_section.bodies().iter().enumerate() {
375						let code = res.map_instructions(func_body.code().elements());
376						let mut func = res.funcs.get_ref(imported_functions + idx).write();
377						match &mut func.origin {
378							ImportedOrDeclared::Declared(body) => {
379								body.code = code;
380								body.locals = func_body.locals().to_vec();
381							},
382							_ => { return Err(Error::InconsistentSource); }
383						}
384					}
385				},
386				elements::Section::Data(data_section) => {
387					for data_segment in data_section.entries() {
388						// TODO: update tetsy-wasm and use the same logic as in
389						// commented element segment branch
390						let init_expr = data_segment
391							.offset()
392							.as_ref()
393							.expect("tetsy-wasm is compiled without bulk-memory operations")
394							.code();
395						let location = SegmentLocation::Default(res.map_instructions(init_expr));
396
397						res.data.push(DataSegment {
398							value: data_segment.value().to_vec(),
399							location,
400						});
401					}
402				},
403				_ => {
404					res.other.insert(idx, section.clone());
405				}
406			}
407		}
408
409		Ok(res)
410	}
411
412	/// Generate raw format representation.
413	pub fn generate(&self) -> Result<elements::Module, Error> {
414		use self::ImportedOrDeclared::*;
415
416		let mut idx = 0;
417		let mut sections = Vec::new();
418
419		custom_round(&self.other, &mut idx, &mut sections);
420
421		if !self.types.is_empty() {
422			// TYPE SECTION (1)
423			let mut type_section = elements::TypeSection::default();
424			{
425				let types = type_section.types_mut();
426
427				for type_entry in self.types.iter() {
428					types.push(type_entry.read().clone())
429				}
430			}
431			sections.push(elements::Section::Type(type_section));
432			idx += 1;
433
434			custom_round(&self.other, &mut idx, &mut sections);
435		}
436
437		// IMPORT SECTION (2)
438		let mut import_section = elements::ImportSection::default();
439
440		let add = {
441			let imports = import_section.entries_mut();
442			for func in self.funcs.iter() {
443				match &func.read().origin {
444					Imported(module, field) => {
445						imports.push(
446							elements::ImportEntry::new(
447								module.to_owned(),
448								field.to_owned(),
449								elements::External::Function(
450									func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32
451								),
452							)
453						)
454					},
455					_ => continue,
456				}
457			}
458
459			for global in self.globals.iter() {
460				match &global.read().origin {
461					Imported(module, field) => {
462						imports.push(
463							elements::ImportEntry::new(
464								module.to_owned(),
465								field.to_owned(),
466								elements::External::Global(
467									elements::GlobalType::new(
468										global.read().content,
469										global.read().is_mut,
470									)
471								),
472							)
473						)
474					},
475					_ => continue,
476				}
477			}
478
479			for memory in self.memory.iter() {
480				match &memory.read().origin {
481					Imported(module, field) => {
482						imports.push(
483							elements::ImportEntry::new(
484								module.to_owned(),
485								field.to_owned(),
486								elements::External::Memory(
487									elements::MemoryType::new(
488										memory.read().limits.initial(),
489										memory.read().limits.maximum(),
490									)
491								),
492							)
493						)
494					},
495					_ => continue,
496				}
497			}
498
499			for table in self.tables.iter() {
500				match &table.read().origin {
501					Imported(module, field) => {
502						imports.push(
503							elements::ImportEntry::new(
504								module.to_owned(),
505								field.to_owned(),
506								elements::External::Table(
507									elements::TableType::new(
508										table.read().limits.initial(),
509										table.read().limits.maximum(),
510									)
511								),
512							)
513						)
514					},
515					_ => continue,
516				}
517			}
518			!imports.is_empty()
519		};
520
521		if add {
522			sections.push(elements::Section::Import(import_section));
523			idx += 1;
524			custom_round(&self.other, &mut idx, &mut sections);
525		}
526
527		if !self.funcs.is_empty() {
528			// FUNC SECTION (3)
529			let mut func_section = elements::FunctionSection::default();
530			{
531				let funcs = func_section.entries_mut();
532
533				for func in self.funcs.iter() {
534					match func.read().origin {
535						Declared(_) => {
536							funcs.push(elements::Func::new(
537								func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32
538							));
539						},
540						_ => continue,
541					}
542				}
543			}
544			sections.push(elements::Section::Function(func_section));
545			idx += 1;
546
547			custom_round(&self.other, &mut idx, &mut sections);
548		}
549
550		if !self.tables.is_empty() {
551			// TABLE SECTION (4)
552			let mut table_section = elements::TableSection::default();
553			{
554				let tables = table_section.entries_mut();
555
556				for table in self.tables.iter() {
557					match table.read().origin {
558						Declared(_) => {
559							tables.push(elements::TableType::new(
560								table.read().limits.initial(),
561								table.read().limits.maximum(),
562							));
563						},
564						_ => continue,
565					}
566				}
567			}
568			sections.push(elements::Section::Table(table_section));
569			idx += 1;
570
571			custom_round(&self.other, &mut idx, &mut sections);
572		}
573
574		if !self.memory.is_empty() {
575			// MEMORY SECTION (5)
576			let mut memory_section = elements::MemorySection::default();
577			{
578				let memories = memory_section.entries_mut();
579
580				for memory in self.memory.iter() {
581					match memory.read().origin {
582						Declared(_) => {
583							memories.push(elements::MemoryType::new(
584								memory.read().limits.initial(),
585								memory.read().limits.maximum(),
586							));
587						},
588						_ => continue,
589					}
590				}
591			}
592			sections.push(elements::Section::Memory(memory_section));
593			idx += 1;
594
595			custom_round(&self.other, &mut idx, &mut sections);
596		}
597
598		if !self.globals.is_empty() {
599			// GLOBAL SECTION (6)
600			let mut global_section = elements::GlobalSection::default();
601			{
602				let globals = global_section.entries_mut();
603
604				for global in self.globals.iter() {
605					match &global.read().origin {
606						Declared(init_code) => {
607							globals.push(elements::GlobalEntry::new(
608								elements::GlobalType::new(global.read().content, global.read().is_mut),
609								elements::InitExpr::new(self.generate_instructions(&init_code[..])),
610							));
611						},
612						_ => continue,
613					}
614				}
615			}
616			sections.push(elements::Section::Global(global_section));
617			idx += 1;
618
619			custom_round(&self.other, &mut idx, &mut sections);
620		}
621
622		if !self.exports.is_empty() {
623			// EXPORT SECTION (7)
624			let mut export_section = elements::ExportSection::default();
625			{
626				let exports = export_section.entries_mut();
627
628				for export in self.exports.iter() {
629					let internal = match &export.local {
630						ExportLocal::Func(func_ref) => {
631							elements::Internal::Function(func_ref.order().ok_or(Error::DetachedEntry)? as u32)
632						},
633						ExportLocal::Global(global_ref) => {
634							elements::Internal::Global(global_ref.order().ok_or(Error::DetachedEntry)? as u32)
635						},
636						ExportLocal::Table(table_ref) => {
637							elements::Internal::Table(table_ref.order().ok_or(Error::DetachedEntry)? as u32)
638						},
639						ExportLocal::Memory(memory_ref) => {
640							elements::Internal::Memory(memory_ref.order().ok_or(Error::DetachedEntry)? as u32)
641						},
642					};
643
644					exports.push(elements::ExportEntry::new(export.name.to_owned(), internal));
645				}
646			}
647			sections.push(elements::Section::Export(export_section));
648			idx += 1;
649
650			custom_round(&self.other, &mut idx, &mut sections);
651		}
652
653		if let Some(func_ref) = &self.start {
654			// START SECTION (8)
655			sections.push(elements::Section::Start(
656				func_ref.order().ok_or(Error::DetachedEntry)? as u32
657			));
658		}
659
660		if !self.elements.is_empty() {
661			// START SECTION (9)
662			let mut element_section = elements::ElementSection::default();
663			{
664				let element_segments = element_section.entries_mut();
665
666				for element in self.elements.iter() {
667					match &element.location {
668						SegmentLocation::Default(offset_expr) => {
669							let mut elements_map = Vec::new();
670							for f in element.value.iter() {
671								elements_map.push(f.order().ok_or(Error::DetachedEntry)? as u32);
672							}
673
674							element_segments.push(
675								elements::ElementSegment::new(
676									0,
677									Some(elements::InitExpr::new(self.generate_instructions(&offset_expr[..]))),
678									elements_map,
679								)
680							);
681						},
682						_ => unreachable!("Other segment location types are never added"),
683					}
684				}
685			}
686
687			sections.push(elements::Section::Element(element_section));
688			idx += 1;
689
690			custom_round(&self.other, &mut idx, &mut sections);
691		}
692
693		if !self.funcs.is_empty() {
694			// CODE SECTION (10)
695			let mut code_section = elements::CodeSection::default();
696			{
697				let funcs = code_section.bodies_mut();
698
699				for func in self.funcs.iter() {
700					match &func.read().origin {
701						Declared(body) => {
702							funcs.push(elements::FuncBody::new(
703								body.locals.clone(),
704								elements::Instructions::new(self.generate_instructions(&body.code[..])),
705							));
706						},
707						_ => continue,
708					}
709				}
710			}
711			sections.push(elements::Section::Code(code_section));
712			idx += 1;
713
714			custom_round(&self.other, &mut idx, &mut sections);
715		}
716
717
718		if !self.data.is_empty() {
719			// DATA SECTION (11)
720			let mut data_section = elements::DataSection::default();
721			{
722				let data_segments = data_section.entries_mut();
723
724				for data_entry in self.data.iter() {
725					match &data_entry.location {
726						SegmentLocation::Default(offset_expr) => {
727							data_segments.push(
728								elements::DataSegment::new(
729									0,
730									Some(elements::InitExpr::new(self.generate_instructions(&offset_expr[..]))),
731									data_entry.value.clone(),
732								)
733							);
734						},
735						_ => unreachable!("Other segment location types are never added"),
736					}
737				}
738			}
739
740			sections.push(elements::Section::Data(data_section));
741			idx += 1;
742
743			custom_round(&self.other, &mut idx, &mut sections);
744		}
745
746		Ok(elements::Module::new(sections))
747	}
748}
749
750fn custom_round(
751	map: &BTreeMap<usize, elements::Section>,
752	idx: &mut usize,
753	sections: &mut Vec<elements::Section>,
754) {
755	while let Some(other_section) = map.get(&idx) {
756		sections.push(other_section.clone());
757		*idx += 1;
758	}
759}
760
761/// New module from tetsy-wasm `Module`
762pub fn parse(wasm: &[u8]) -> Result<Module, Error> {
763	Module::from_elements(&::tetsy_wasm::deserialize_buffer(wasm).map_err(Error::Format)?)
764}
765
766/// Generate tetsy-wasm `Module`
767pub fn generate(f: &Module) -> Result<Vec<u8>, Error> {
768	let pm = f.generate()?;
769	::tetsy_wasm::serialize(pm).map_err(Error::Format)
770}
771
772#[cfg(test)]
773mod tests {
774
775	extern crate wabt;
776
777	use tetsy_wasm::elements;
778
779	fn load_sample(wat: &'static str) -> super::Module {
780		super::parse(&wabt::wat2wasm(wat).expect("faled to parse wat!")[..])
781			.expect("error making representation")
782	}
783
784	fn validate_sample(module: &super::Module) {
785		let binary = super::generate(module).expect("Failed to generate binary");
786		wabt::Module::read_binary(&binary, &Default::default())
787			.expect("Wabt failed to read final binary")
788			.validate()
789			.expect("Invalid module");
790	}
791
792	#[test]
793	fn smoky() {
794		let sample = load_sample(indoc!(r#"
795			(module
796				(type (func))
797				(func (type 0))
798				(memory 0 1)
799				(export "simple" (func 0)))"#
800		));
801
802		assert_eq!(sample.types.len(), 1);
803		assert_eq!(sample.funcs.len(), 1);
804		assert_eq!(sample.tables.len(), 0);
805		assert_eq!(sample.memory.len(), 1);
806		assert_eq!(sample.exports.len(), 1);
807
808		assert_eq!(sample.types.get_ref(0).link_count(), 1);
809		assert_eq!(sample.funcs.get_ref(0).link_count(), 1);
810	}
811
812	#[test]
813	fn table() {
814		let mut sample = load_sample(indoc!(r#"
815			(module
816				(import "env" "foo" (func $foo))
817				(func (param i32)
818					get_local 0
819					i32.const 0
820					call $i32.add
821					drop
822				)
823				(func $i32.add (export "i32.add") (param i32 i32) (result i32)
824					get_local 0
825					get_local 1
826					i32.add
827				)
828				(table 10 anyfunc)
829
830				;; Refer all types of functions: imported, defined not exported and defined exported.
831				(elem (i32.const 0) 0 1 2)
832			)"#
833		));
834
835		{
836			let element_func = &sample.elements[0].value[1];
837			let rfunc = element_func.read();
838			let rtype = &**rfunc.type_ref.read();
839			let elements::Type::Function(ftype) = rtype;
840
841			// it's func#1 in the function  space
842			assert_eq!(rfunc.order(), Some(1));
843			// it's type#1
844			assert_eq!(ftype.params().len(), 1);
845		}
846
847		sample.funcs.begin_delete().push(0).done();
848
849		{
850			let element_func = &sample.elements[0].value[1];
851			let rfunc = element_func.read();
852			let rtype = &**rfunc.type_ref.read();
853			let elements::Type::Function(ftype) = rtype;
854
855			// import deleted so now it's func #0
856			assert_eq!(rfunc.order(), Some(0));
857			// type should be the same, #1
858			assert_eq!(ftype.params().len(), 1);
859		}
860	}
861
862	#[test]
863	fn new_import() {
864		let mut sample = load_sample(indoc!(r#"
865			(module
866				(type (;0;) (func))
867				(type (;1;) (func (param i32 i32) (result i32)))
868				(import "env" "foo" (func (type 1)))
869				(func (param i32)
870					get_local 0
871					i32.const 0
872					call 0
873					drop
874				)
875				(func (type 0)
876					i32.const 0
877					call 1
878				)
879			)"#
880		));
881
882		{
883			let type_ref_0 = sample.types.clone_ref(0);
884			let declared_func_2 = sample.funcs.clone_ref(2);
885
886			let mut tx = sample.funcs.begin_insert_not_until(
887				|f| matches!(f.origin, super::ImportedOrDeclared::Imported(_, _))
888			);
889
890			let new_import_func = tx.push(super::Func {
891				type_ref: type_ref_0,
892				origin: super::ImportedOrDeclared::Imported("env".to_owned(), "bar".to_owned()),
893			});
894
895			tx.done();
896
897			assert_eq!(new_import_func.order(), Some(1));
898			assert_eq!(declared_func_2.order(), Some(3));
899			assert_eq!(
900				match &declared_func_2.read().origin {
901					super::ImportedOrDeclared::Declared(body) => {
902						match &body.code[1] {
903							super::Instruction::Call(called_func) => called_func.order(),
904							_ => panic!("instruction #2 should be a call!"),
905						}
906					},
907					_ => panic!("func #3 should be declared!"),
908				},
909				Some(2),
910				"Call should be recalculated to 2"
911			);
912		}
913
914		validate_sample(&sample);
915	}
916
917	#[test]
918	fn simple_opt() {
919		let mut sample = load_sample(indoc!(r#"
920			(module
921				(type (;0;) (func))
922				(type (;1;) (func (param i32 i32) (result i32)))
923				(type (;2;) (func (param i32 i32) (result i32)))
924				(type (;3;) (func (param i32 i32) (result i32)))
925				(import "env" "foo" (func (type 1)))
926				(import "env" "foo2" (func (type 2)))
927				(import "env" "foo3" (func (type 3)))
928				(func (type 0)
929					i32.const 1
930					i32.const 1
931					call 0
932					drop
933				)
934				(func (type 0)
935					i32.const 2
936					i32.const 2
937					call 1
938					drop
939				)
940				(func (type 0)
941					i32.const 3
942					i32.const 3
943					call 2
944					drop
945				)
946				(func (type 0)
947					call 3
948				)
949			)"#
950		));
951
952		validate_sample(&sample);
953
954		// we'll delete functions #4 and #5, nobody references it so it should be fine;
955
956		sample.funcs.begin_delete().push(4).push(5).done();
957		validate_sample(&sample);
958
959		// now we'll delete functions #1 and #2 (imported and called from the deleted above),
960		// should also be fine
961		sample.funcs.begin_delete().push(1).push(2).done();
962		validate_sample(&sample);
963
964		// now the last declared function left should call another one before it (which is index #1)
965		let declared_func_2 = sample.funcs.clone_ref(2);
966		assert_eq!(
967			match &declared_func_2.read().origin {
968				super::ImportedOrDeclared::Declared(body) => {
969					match &body.code[0] {
970						super::Instruction::Call(called_func) => called_func.order(),
971						wrong_instruction => panic!("instruction #2 should be a call but got {:?}!", wrong_instruction),
972					}
973				},
974				_ => panic!("func #0 should be declared!"),
975			},
976			Some(1),
977			"Call should be recalculated to 1"
978		);
979	}
980}