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 pub id: u8,
27 pub offset: Option<usize>,
30 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#[derive(Default, Clone, Debug)]
63#[warn(dead_code)]
64pub struct ModuleInfo {
65 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 pub types_map: Vec<Type>,
88
89 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 pub raw_sections: BTreeMap<u8, RawSection>,
97}
98
99macro_rules! add_section_function {
100 ($name:ident, $ty:expr) => {
101 paste! {
102 #[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(§ion.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 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 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 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 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 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 assert_eq!(
269 self.global_section().unwrap().unwrap_or_default().len(),
270 self.num_local_globals() as usize
271 );
272 assert_eq!(
274 self.global_types.len() - self.num_local_globals() as usize,
275 self.num_imported_globals() as usize
276 );
277
278 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 assert_eq!(
286 self.element_section().unwrap().unwrap_or_default().len(),
287 self.elements_count as usize,
288 );
289 }
290
291 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 Validator::new_with_features(features).validate_all(&self.bytes())?;
303
304 Ok(())
305 }
306
307 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 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 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 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 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 pub fn replace_section(
374 &mut self,
375 sec_type: u8,
376 new_section: &impl wasm_encoder::Section,
377 ) -> Result<()> {
378 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(), §ion_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(§ion.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(), §ion_builder)
430 }
431
432 pub fn add_functions(&mut self, funcs: &[(Type, wasm_encoder::Function)]) -> Result<()> {
434 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 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 function_builder.function(func_type_index);
451 self.function_map.push(func_type_index);
452
453 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 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 import_decoder.import(module, func_name, wasm_encoder::EntityType::Function(func_type_idx));
477 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 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 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, SectionId::Code,
549 SectionId::Data,
550 SectionId::Custom, 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 #[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
653pub 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
664pub 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 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 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}