wasm_graph/
graph.rs

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