radix_wasm_instrument/utils/
module_info.rs

1use crate::utils::{
2	errors::ModuleInfoError,
3	translator::{DefaultTranslator, Translator},
4};
5use alloc::{
6	collections::{BTreeMap, BTreeSet},
7	format,
8	string::String,
9	vec,
10	vec::Vec,
11};
12use core::ops::Range;
13use paste::paste;
14use wasm_encoder::{Encode, ExportKind, SectionId};
15use wasmparser::{
16	Chunk, CodeSectionReader, Element, ElementSectionReader, Export, ExportSectionReader,
17	ExternalKind, FunctionBody, FunctionSectionReader, Global, GlobalSectionReader, GlobalType,
18	Import, ImportSectionReader, MemorySectionReader, MemoryType, Parser, Payload,
19	Result as WasmParserResult, Table, TableSectionReader, TableType, Type, Validator,
20	WasmFeatures,
21};
22
23#[derive(Clone, Debug)]
24pub struct RawSection {
25	/// The id for this section.
26	pub id: u8,
27	/// The offset of this section in the original WASM file.
28	/// If the section is added manually, then set to None
29	pub offset: Option<usize>,
30	/// The raw data for this section.
31	pub data: Vec<u8>,
32}
33
34impl RawSection {
35	pub fn new(id: u8, offset: Option<usize>, data: Vec<u8>) -> Self {
36		RawSection { id, offset, data }
37	}
38}
39
40impl Encode for RawSection {
41	fn encode(&self, sink: &mut Vec<u8>) {
42		self.data.encode(sink);
43	}
44}
45
46impl wasm_encoder::Section for RawSection {
47	fn id(&self) -> u8 {
48		self.id
49	}
50}
51
52impl wasm_encoder::ComponentSection for RawSection {
53	fn id(&self) -> u8 {
54		self.id
55	}
56}
57
58type Result<T> = core::result::Result<T, ModuleInfoError>;
59
60/// Provides module information for future usage during mutation
61/// an instance of ModuleInfo could be user to determine which mutation could be applied
62#[derive(Default, Clone, Debug)]
63#[warn(dead_code)]
64pub struct ModuleInfo {
65	// The following fields are offsets inside the `raw_sections` field.
66	// The main idea is to maintain the order of the sections in the input Wasm.
67	pub export_names: BTreeSet<String>,
68
69	pub code_section_entry_count: u32,
70	pub exports_count: u32,
71	pub exports_global_count: u32,
72
73	pub elements_count: u32,
74	pub data_segments_count: u32,
75	pub start_function: Option<u32>,
76	pub memory_count: u32,
77	pub table_count: u32,
78	pub tag_count: u32,
79
80	pub imported_functions_count: u32,
81	pub imported_globals_count: u32,
82	pub imported_memories_count: u32,
83	pub imported_tables_count: u32,
84	pub imported_tags_count: u32,
85
86	// types for inner functions
87	pub types_map: Vec<Type>,
88
89	// function idx to type idx
90	pub function_map: Vec<u32>,
91	pub global_types: Vec<GlobalType>,
92	pub table_elem_types: Vec<TableType>,
93	pub memory_types: Vec<MemoryType>,
94
95	// raw_sections
96	pub raw_sections: BTreeMap<u8, RawSection>,
97}
98
99macro_rules! add_section_function {
100	($name:ident, $ty:expr) => {
101		paste! {
102			// TODO Consider reworking it to return iterator
103			#[allow(dead_code)]
104			pub fn [< $name:lower _section>](&self) -> Result<Option<Vec<$ty>>> {
105
106				if let Some(section) =  self.raw_sections.get(&SectionId::$name.into()) {
107					let reader = [< $name SectionReader >]::new(&section.data, 0)?;
108					let vec = reader.into_iter().collect::<WasmParserResult<Vec<$ty>>>()?;
109					Ok(Some(vec))
110				}
111				else {
112					Ok(None)
113				}
114			}
115		}
116	};
117}
118
119impl ModuleInfo {
120	/// Parse the given Wasm bytes and fill out a `ModuleInfo` AST for it.
121	pub fn new(input_wasm: &[u8]) -> Result<ModuleInfo> {
122		let mut parser = Parser::new(0);
123		let mut info = ModuleInfo::default();
124		let mut wasm = input_wasm;
125
126		loop {
127			let (payload, consumed) = match parser.parse(wasm, true)? {
128				Chunk::NeedMoreData(hint) => {
129					panic!("Invalid Wasm module {:?}", hint);
130				},
131				Chunk::Parsed { consumed, payload } => (payload, consumed),
132			};
133
134			match payload {
135				Payload::CodeSectionStart { count, range, size: _ } => {
136					info.code_section_entry_count = count;
137					info.section(SectionId::Code.into(), range.clone(), input_wasm)?;
138					parser.skip_section();
139					// update slice, bypass the section
140					wasm = &input_wasm[range.end..];
141
142					continue;
143				},
144				Payload::TypeSection(reader) => {
145					info.section(SectionId::Type.into(), reader.range(), input_wasm)?;
146
147					// Save function types
148					for ty in reader.into_iter() {
149						info.types_map.push(ty?);
150					}
151				},
152				Payload::ImportSection(reader) => {
153					info.section(SectionId::Import.into(), reader.range(), input_wasm)?;
154
155					for import in reader.into_iter() {
156						let import = import?;
157						match import.ty {
158							wasmparser::TypeRef::Func(ty) => {
159								// Save imported functions
160								info.function_map.push(ty);
161								info.imported_functions_count += 1;
162							},
163							wasmparser::TypeRef::Global(ty) => {
164								info.global_types.push(ty);
165								info.imported_globals_count += 1;
166							},
167							wasmparser::TypeRef::Memory(ty) => {
168								info.memory_count += 1;
169								info.imported_memories_count += 1;
170								info.memory_types.push(ty);
171							},
172							wasmparser::TypeRef::Table(ty) => {
173								info.table_count += 1;
174								info.imported_tables_count += 1;
175								info.table_elem_types.push(ty);
176							},
177							wasmparser::TypeRef::Tag(_ty) => {
178								info.tag_count += 1;
179								info.imported_tags_count += 1;
180							},
181						}
182					}
183				},
184				Payload::FunctionSection(reader) => {
185					info.section(SectionId::Function.into(), reader.range(), input_wasm)?;
186
187					for func_idx in reader.into_iter() {
188						info.function_map.push(func_idx?);
189					}
190				},
191				Payload::TableSection(reader) => {
192					info.table_count = reader.count();
193					info.section(SectionId::Table.into(), reader.range(), input_wasm)?;
194
195					for table in reader.into_iter() {
196						let table = table?;
197						info.table_elem_types.push(table.ty);
198					}
199				},
200				Payload::MemorySection(reader) => {
201					info.memory_count = reader.count();
202					info.section(SectionId::Memory.into(), reader.range(), input_wasm)?;
203
204					for ty in reader.into_iter() {
205						info.memory_types.push(ty?);
206					}
207				},
208				Payload::GlobalSection(reader) => {
209					info.section(SectionId::Global.into(), reader.range(), input_wasm)?;
210
211					for global in reader.into_iter() {
212						let global = global?;
213						info.global_types.push(global.ty);
214					}
215				},
216				Payload::ExportSection(reader) => {
217					for export in reader.clone().into_iter() {
218						let export = export?;
219						if !info.export_names.contains(export.name) {
220							if let ExternalKind::Global = export.kind {
221								info.exports_global_count += 1;
222							}
223							info.export_names.insert(export.name.into());
224							info.exports_count += 1;
225						} else {
226							return Err(ModuleInfoError::ExportAlreadyExists(export.name.into()));
227						}
228					}
229					info.section(SectionId::Export.into(), reader.range(), input_wasm)?;
230				},
231				Payload::StartSection { func, range } => {
232					info.start_function = Some(func);
233					info.section(SectionId::Start.into(), range, input_wasm)?;
234				},
235				Payload::ElementSection(reader) => {
236					info.elements_count = reader.count();
237					info.section(SectionId::Element.into(), reader.range(), input_wasm)?;
238				},
239				Payload::DataSection(reader) => {
240					info.data_segments_count = reader.count();
241					info.section(SectionId::Data.into(), reader.range(), input_wasm)?;
242				},
243				#[allow(unused_variables)]
244				Payload::CustomSection(c) => {
245					#[cfg(not(feature = "ignore_custom_section"))]
246					// At the moment only name section supported
247					if c.name() == "name" {
248						info.section(SectionId::Custom.into(), c.range(), input_wasm)?;
249					}
250				},
251
252				Payload::DataCountSection { count: _, range } => {
253					info.section(SectionId::DataCount.into(), range, input_wasm)?;
254				},
255				Payload::Version { .. } => {},
256				Payload::End(_) => break,
257				p => return Err(ModuleInfoError::SectionNotSupported(format!("{:?}", p))),
258			}
259			wasm = &wasm[consumed..];
260		}
261
262		Ok(info)
263	}
264
265	#[cfg(test)]
266	pub fn assert_stats(&self) {
267		// Global section
268		assert_eq!(
269			self.global_section().unwrap().unwrap_or_default().len(),
270			self.num_local_globals() as usize
271		);
272		// Imported globals
273		assert_eq!(
274			self.global_types.len() - self.num_local_globals() as usize,
275			self.num_imported_globals() as usize
276		);
277
278		// Export section
279		assert_eq!(self.export_names.len(), self.exports_count as usize);
280		assert_eq!(
281			self.export_section().unwrap().unwrap_or_default().len(),
282			self.exports_count as usize
283		);
284		// Element section
285		assert_eq!(
286			self.element_section().unwrap().unwrap_or_default().len(),
287			self.elements_count as usize,
288		);
289	}
290
291	/// Validates the WASM binary
292	pub fn validate(&self, features: WasmFeatures) -> Result<()> {
293		if self.code_section_entry_count != self.num_local_functions() {
294			return Err(ModuleInfoError::CodeAndFuncSectionCntMismatch(
295				self.code_section_entry_count,
296				self.num_local_functions(),
297			));
298		}
299
300		// TODO validate_all() creates internal parser and parses the binary again. Rework to
301		// validate while parsing
302		Validator::new_with_features(features).validate_all(&self.bytes())?;
303
304		Ok(())
305	}
306
307	/// Registers a new raw_section in the ModuleInfo
308	pub fn section(&mut self, id: u8, range: Range<usize>, full_wasm: &[u8]) -> Result<()> {
309		if self.raw_sections.get(&id).is_none() {
310			if range.start > full_wasm.len() || range.end > full_wasm.len() {
311				Err(ModuleInfoError::SectionRangeExceedsWasmLength {
312					range,
313					wasm_len: full_wasm.len(),
314				})
315			} else {
316				self.raw_sections
317					.insert(id, RawSection::new(id, Some(range.start), full_wasm[range].to_vec()));
318				Ok(())
319			}
320		} else {
321			Err(ModuleInfoError::SectionAlreadyExists(id))
322		}
323	}
324
325	/// Returns the function type based on the index of the type
326	/// `types[idx]`
327	pub fn get_type_by_idx(&self, type_idx: u32) -> Result<&Type> {
328		if type_idx >= self.types_map.len() as u32 {
329			return Err(ModuleInfoError::TypeDoesNotExist(type_idx));
330		}
331		Ok(&self.types_map[type_idx as usize])
332	}
333
334	/// Returns the function type based on the index of the function
335	/// `types[functions[idx]]`
336	pub fn get_type_by_func_idx(&self, func_idx: u32) -> Result<&Type> {
337		if func_idx >= self.function_map.len() as u32 {
338			return Err(ModuleInfoError::FunctionDoesNotExist(func_idx));
339		}
340		let type_idx = self.function_map[func_idx as usize];
341		self.get_type_by_idx(type_idx)
342	}
343
344	pub fn resolve_type_idx(&self, t: &Type) -> Option<u32> {
345		// TODO: Type::Array(_) part of GC proposal
346		let Type::Func(dt) = t else { todo!("Array type not supported yet") };
347		for (index, ty) in self.types_map.iter().enumerate() {
348			let Type::Func(ot) = ty else { todo!("Array type not supported yet") };
349			if ot.eq(dt) {
350				return Some(index as u32);
351			}
352		}
353		None
354	}
355
356	pub fn add_func_type(&mut self, func_type: &Type) -> Result<u32> {
357		let func_type_index = match self.resolve_type_idx(func_type) {
358			None => self.types_map.len() as u32,
359			Some(index) => return Ok(index),
360		};
361		// Add new type
362		let mut type_builder = wasm_encoder::TypeSection::new();
363		for t in &self.types_map {
364			DefaultTranslator.translate_type_def(t.clone(), &mut type_builder)?;
365		}
366		self.types_map.push(func_type.clone());
367		DefaultTranslator.translate_type_def(func_type.clone(), &mut type_builder)?;
368		self.replace_section(SectionId::Type.into(), &type_builder)?;
369		Ok(func_type_index)
370	}
371
372	/// Replace the `i`th section in this module with the given new section.
373	pub fn replace_section(
374		&mut self,
375		sec_type: u8,
376		new_section: &impl wasm_encoder::Section,
377	) -> Result<()> {
378		// If section was not present before then offset is None
379		let offset = self.raw_sections.get(&sec_type).and_then(|sec| sec.offset);
380
381		self.raw_sections.insert(
382			sec_type,
383			RawSection::new(sec_type, offset, truncate_len_from_encoder(new_section)?),
384		);
385		Ok(())
386	}
387
388	pub fn add_exports(&mut self, exports: &[(String, ExportKind, u32)]) -> Result<()> {
389		let mut section_builder = wasm_encoder::ExportSection::new();
390
391		for export in self.export_section()?.unwrap_or(vec![]) {
392			let export_kind = DefaultTranslator.translate_export_kind(export.kind)?;
393			section_builder.export(export.name, export_kind, export.index);
394		}
395
396		for (name, kind, index) in exports {
397			if !self.export_names.contains(name) {
398				section_builder.export(name, *kind, *index);
399				if let ExportKind::Global = kind {
400					self.exports_global_count += 1;
401				}
402				self.export_names.insert(String::from(name));
403				self.exports_count += 1;
404			} else {
405				return Err(ModuleInfoError::ExportAlreadyExists(String::from(name)));
406			}
407		}
408		self.replace_section(SectionId::Export.into(), &section_builder)
409	}
410
411	pub fn add_global(
412		&mut self,
413		global_type: GlobalType,
414		init_expr: &wasm_encoder::ConstExpr,
415	) -> Result<()> {
416		let mut section_builder = wasm_encoder::GlobalSection::new();
417
418		if let Some(section) = self.raw_sections.get(&SectionId::Global.into()) {
419			let section_reader = wasmparser::GlobalSectionReader::new(&section.data, 0)?;
420
421			for section_item in section_reader {
422				DefaultTranslator.translate_global(section_item?, &mut section_builder)?;
423			}
424		}
425
426		section_builder.global(DefaultTranslator.translate_global_type(&global_type)?, init_expr);
427		self.global_types.push(global_type);
428
429		self.replace_section(SectionId::Global.into(), &section_builder)
430	}
431
432	/// Add functions specified as a vector of tuples: signature and body
433	pub fn add_functions(&mut self, funcs: &[(Type, wasm_encoder::Function)]) -> Result<()> {
434		// Recreate Function section
435		let mut function_builder = wasm_encoder::FunctionSection::new();
436		for function in self.function_section()?.unwrap_or(vec![]) {
437			function_builder.function(function);
438		}
439
440		// Recreate Code section
441		let mut code_builder = wasm_encoder::CodeSection::new();
442		for funcion_body in self.code_section()?.unwrap_or(vec![]) {
443			DefaultTranslator.translate_code(funcion_body, &mut code_builder)?
444		}
445
446		for (func_type, func_body) in funcs {
447			let func_type_index = self.add_func_type(func_type)?;
448
449			// Define a new function in Function section
450			function_builder.function(func_type_index);
451			self.function_map.push(func_type_index);
452
453			// Write new function body in Code section
454			code_builder.function(func_body);
455			self.code_section_entry_count += 1;
456		}
457		self.replace_section(SectionId::Function.into(), &function_builder)?;
458		self.replace_section(SectionId::Code.into(), &code_builder)
459	}
460
461	pub fn add_import_func(
462		&mut self,
463		module: &str,
464		func_name: &str,
465		func_type: Type,
466	) -> Result<()> {
467		let func_type_idx = self.add_func_type(&func_type)?;
468
469		// Recreate Import section
470		let mut import_decoder = wasm_encoder::ImportSection::new();
471		for import in self.import_section()?.unwrap_or(vec![]) {
472			DefaultTranslator.translate_import(import, &mut import_decoder)?;
473		}
474
475		// Define new function import in the Import section.
476		import_decoder.import(module, func_name, wasm_encoder::EntityType::Function(func_type_idx));
477		// function_map consist of:
478		// - imported function first
479		// - local functions then
480		// This is important when getting function Type by its index get_type_by_func_idx()
481		// When adding an import we make sure it is added as the last imported function
482		// but before local functions
483		self.function_map.insert(self.imported_functions_count as usize, func_type_idx);
484
485		self.imported_functions_count += 1;
486		self.replace_section(SectionId::Import.into(), &import_decoder)
487	}
488
489	pub fn add_import_global(
490		&mut self,
491		module: &str,
492		global_name: &str,
493		global_type: GlobalType,
494	) -> Result<()> {
495		// Recreate Import section
496		let mut import_decoder = wasm_encoder::ImportSection::new();
497		for import in self.import_section()?.unwrap_or(vec![]) {
498			DefaultTranslator.translate_import(import, &mut import_decoder)?;
499		}
500
501		// Define new global import in the Import section.
502		import_decoder.import(
503			module,
504			global_name,
505			DefaultTranslator.translate_global_type(&global_type)?,
506		);
507		self.global_types.push(global_type);
508		self.imported_globals_count += 1;
509		self.replace_section(SectionId::Import.into(), &import_decoder)
510	}
511
512	pub fn modify_memory_type(&mut self, mem_index: u32, mem_type: MemoryType) -> Result<()> {
513		let mut memory_builder = wasm_encoder::MemorySection::new();
514		let mut memory_types = vec![];
515		for (index, memory) in self
516			.memory_section()?
517			.ok_or_else(|| ModuleInfoError::NoMemorySection)?
518			.iter()
519			.enumerate()
520		{
521			let encoded_mem_type = if index as u32 != mem_index {
522				memory_types.push(*memory);
523				DefaultTranslator.translate_memory_type(memory)?
524			} else {
525				memory_types.push(mem_type);
526				DefaultTranslator.translate_memory_type(&mem_type)?
527			};
528			memory_builder.memory(encoded_mem_type);
529		}
530		self.memory_types = memory_types;
531		self.replace_section(SectionId::Memory.into(), &memory_builder)
532	}
533
534	pub fn bytes(&self) -> Vec<u8> {
535		let mut module = wasm_encoder::Module::new();
536
537		let section_order = [
538			SectionId::Type,
539			SectionId::Import,
540			SectionId::Function,
541			SectionId::Table,
542			SectionId::Memory,
543			SectionId::Global,
544			SectionId::Export,
545			SectionId::Start,
546			SectionId::Element,
547			SectionId::DataCount, /* datacount goes before code, see: https://webassembly.github.io/spec/core/binary/modules.html#data-count-section */
548			SectionId::Code,
549			SectionId::Data,
550			SectionId::Custom, /* custom Name section after data section, see: https://webassembly.github.io/spec/core/appendix/custom.html#name-section */
551			SectionId::Tag,
552		];
553
554		for s in section_order {
555			if let Some(sec) = self.raw_sections.get(&s.into()) {
556				module.section(sec);
557			}
558		}
559		module.finish()
560	}
561
562	#[allow(dead_code)]
563	pub fn num_functions(&self) -> u32 {
564		self.function_map.len() as u32
565	}
566
567	#[allow(dead_code)]
568	pub fn num_local_functions(&self) -> u32 {
569		self.num_functions() - self.num_imported_functions()
570	}
571
572	#[allow(dead_code)]
573	pub fn num_imported_functions(&self) -> u32 {
574		self.imported_functions_count
575	}
576
577	#[allow(dead_code)]
578	pub fn num_tables(&self) -> u32 {
579		self.table_count
580	}
581
582	#[allow(dead_code)]
583	pub fn num_imported_tables(&self) -> u32 {
584		self.imported_tables_count
585	}
586
587	#[allow(dead_code)]
588	pub fn num_memories(&self) -> u32 {
589		self.memory_count
590	}
591
592	#[allow(dead_code)]
593	pub fn num_imported_memories(&self) -> u32 {
594		self.imported_memories_count
595	}
596
597	/// Returns the number of globals: local and imported
598	#[allow(dead_code)]
599	pub fn num_globals(&self) -> u32 {
600		self.global_types.len() as u32
601	}
602
603	#[allow(dead_code)]
604	pub fn num_imported_globals(&self) -> u32 {
605		self.imported_globals_count
606	}
607
608	#[allow(dead_code)]
609	pub fn num_local_globals(&self) -> u32 {
610		self.global_types.len() as u32 - self.imported_globals_count
611	}
612
613	#[allow(dead_code)]
614	pub fn num_tags(&self) -> u32 {
615		self.tag_count
616	}
617
618	#[allow(dead_code)]
619	pub fn num_imported_tags(&self) -> u32 {
620		self.imported_tags_count
621	}
622
623	#[allow(dead_code)]
624	pub fn num_data(&self) -> u32 {
625		self.data_segments_count
626	}
627
628	#[allow(dead_code)]
629	pub fn num_elements(&self) -> u32 {
630		self.elements_count
631	}
632
633	#[allow(dead_code)]
634	pub fn num_types(&self) -> u32 {
635		self.types_map.len() as u32
636	}
637
638	#[allow(dead_code)]
639	pub fn num_export_global(&self) -> u32 {
640		self.exports_global_count
641	}
642
643	add_section_function!(Export, Export);
644	add_section_function!(Global, Global);
645	add_section_function!(Import, Import);
646	add_section_function!(Function, u32);
647	add_section_function!(Memory, MemoryType);
648	add_section_function!(Table, Table);
649	add_section_function!(Code, FunctionBody);
650	add_section_function!(Element, Element);
651}
652
653// Then insert metering calls into a sequence of instructions given the block locations and costs.
654pub fn copy_locals(func_body: &FunctionBody) -> Result<Vec<(u32, wasm_encoder::ValType)>> {
655	let local_reader = func_body.get_locals_reader()?;
656	let mut current_locals = vec![];
657	for local in local_reader.into_iter() {
658		let (count, ty) = local?;
659		current_locals.push((count, DefaultTranslator.translate_ty(&ty)?));
660	}
661	Ok(current_locals)
662}
663
664// TODO unable to get function encoder body directly, remove this after option wasmparser
665pub fn truncate_len_from_encoder(func_builder: &dyn wasm_encoder::Encode) -> Result<Vec<u8>> {
666	let mut d = vec![];
667	func_builder.encode(&mut d);
668	let mut r = wasmparser::BinaryReader::new(&d);
669	let size = r.read_var_u32()?;
670	Ok(r.read_bytes(size as usize)?.to_vec())
671}
672
673#[cfg(test)]
674mod tests {
675	use super::*;
676	use crate::{gas_metering, gas_metering::ConstantCostRules, stack_limiter};
677	use wasm_encoder::ExportKind;
678	use wasmparser::{FuncType, ValType};
679
680	fn wasm_to_wat(bytes: &[u8]) -> String {
681		String::from_utf8(
682			wabt::Wasm2Wat::new()
683				.read_debug_names(true)
684				.convert(bytes)
685				.unwrap()
686				.as_ref()
687				.to_vec(),
688		)
689		.unwrap()
690	}
691
692	fn wat_to_wasm(code: &str) -> Vec<u8> {
693		#[cfg(not(feature = "ignore_custom_section"))]
694		let write_debug_names = true;
695		#[cfg(feature = "ignore_custom_section")]
696		let write_debug_names = false;
697
698		wabt::Wat2Wasm::new()
699			.write_debug_names(write_debug_names)
700			.convert(code)
701			.unwrap()
702			.as_ref()
703			.to_vec()
704	}
705
706	const WAT: &str = r#"
707		(module
708
709			(func $Test_f (param $0 i64) (result i64)
710			  ;; Grow memory
711			  (drop
712				(memory.grow (i32.const 1000000))
713			  )
714
715			  ;; Encode () in SBOR at address 0x0
716			  (i32.const 0)
717			  (i32.const 92)  ;; prefix
718			  (i32.store8)
719			  (i32.const 1)
720			  (i32.const 33)  ;; tuple value kind
721			  (i32.store8)
722			  (i32.const 2)
723			  (i32.const 0)  ;; tuple length
724			  (i32.store8)
725
726			  ;; Return slice (ptr = 0, len = 3)
727			  (i64.const 3)
728			)
729
730			(memory $0 1)
731			(export "memory" (memory $0))
732			(export "Test_f" (func $Test_f))
733		)
734		"#;
735
736	#[test]
737	fn test_check_wasm_wat_conversion() {
738		let bytes = wat_to_wasm(WAT);
739		let expected_wat = wasm_to_wat(&bytes);
740
741		let module = ModuleInfo::new(&bytes).unwrap();
742		let bytes = module.bytes();
743		let wat = wasm_to_wat(&bytes);
744
745		assert_eq!(expected_wat, wat)
746	}
747
748	#[test]
749	fn test_module_info_stats() {
750		let bytes = wat_to_wasm(WAT);
751		let mut module = ModuleInfo::new(&bytes).unwrap();
752
753		module.assert_stats();
754
755		module
756			.add_global(
757				GlobalType { content_type: ValType::I64, mutable: true },
758				&wasm_encoder::ConstExpr::i64_const(0),
759			)
760			.unwrap();
761
762		module
763			.add_import_global(
764				"env",
765				"some_global",
766				GlobalType { content_type: ValType::I64, mutable: true },
767			)
768			.unwrap();
769
770		let func_type = Type::Func(FuncType::new(vec![ValType::I64], vec![]));
771		module.add_func_type(&func_type).unwrap();
772
773		// Add import of function of type that already exists
774		module.add_import_func("env", "some_func", func_type).unwrap();
775
776		let func_type =
777			Type::Func(FuncType::new(vec![ValType::I64, ValType::I64], vec![ValType::I32]));
778
779		// Add import with function type that does not exist yet
780		module.add_import_func("env", "some_func_2", func_type).unwrap();
781
782		module
783			.add_exports(&[
784				("export_global".to_string(), ExportKind::Global, 0),
785				("export_func".to_string(), ExportKind::Func, 0),
786				("export_memory".to_string(), ExportKind::Memory, 0),
787				("export_table".to_string(), ExportKind::Table, 0),
788			])
789			.unwrap();
790
791		module.assert_stats();
792	}
793
794	#[test]
795	fn test_instrument_vs_stats() {
796		let bytes = include_bytes!("../../benches/fixtures/wasm/scrypto.wasm");
797		let mut module = ModuleInfo::new(bytes).unwrap();
798
799		module.assert_stats();
800
801		let backend = gas_metering::host_function::Injector::new("env", "gas");
802		let _injected_wasm =
803			gas_metering::inject(&mut module, backend, &ConstantCostRules::default()).unwrap();
804		module.assert_stats();
805
806		let _stack_limited_wasm = stack_limiter::inject(&mut module, 1024).unwrap();
807		module.assert_stats();
808
809		let mut module = ModuleInfo::new(bytes).unwrap();
810
811		module.assert_stats();
812
813		let backend = gas_metering::mutable_global::Injector::new("env", "gas_left");
814		let _injected_wasm =
815			gas_metering::inject(&mut module, backend, &ConstantCostRules::default()).unwrap();
816		module.assert_stats();
817
818		let _stack_limited_wasm = stack_limiter::inject(&mut module, 1024).unwrap();
819		module.assert_stats();
820	}
821
822	macro_rules! test_module_info_stats {
823	    ($name:expr) => {
824			paste! {
825				#[test]
826				fn [< test_module_info_stats_ $name:lower >]() {
827					let bytes = include_bytes!(concat!("../../benches/fixtures/wasm/", stringify!($name), ".wasm"));
828					let mut module = ModuleInfo::new(bytes).unwrap();
829
830					module.assert_stats();
831
832					let backend = gas_metering::host_function::Injector::new("env", "gas");
833					let _injected_wasm =
834						gas_metering::inject(&mut module, backend, &ConstantCostRules::default()).unwrap();
835					module.assert_stats();
836
837					let _stack_limited_wasm = stack_limiter::inject(&mut module, 1024).unwrap();
838					module.assert_stats();
839
840					let mut module = ModuleInfo::new(bytes).unwrap();
841
842					module.assert_stats();
843
844					let backend = gas_metering::mutable_global::Injector::new("env", "gas_left");
845					let _injected_wasm =
846						gas_metering::inject(&mut module, backend, &ConstantCostRules::default()).unwrap();
847					module.assert_stats();
848
849					let _stack_limited_wasm = stack_limiter::inject(&mut module, 1024).unwrap();
850					module.assert_stats();
851				}
852			}
853	    };
854	}
855	test_module_info_stats!(contract_terminate);
856	test_module_info_stats!(contract_transfer);
857	test_module_info_stats!(coremark_minimal);
858	test_module_info_stats!(dns);
859	test_module_info_stats!(erc1155);
860	test_module_info_stats!(erc20);
861	test_module_info_stats!(erc721);
862	test_module_info_stats!(many_blocks);
863	test_module_info_stats!(multisig);
864	test_module_info_stats!(proxy);
865	test_module_info_stats!(rand_extension);
866	test_module_info_stats!(scrypto);
867	test_module_info_stats!(trait_erc20);
868	test_module_info_stats!(wasm_kernel);
869}