1use std::fmt;
2use std::io::Write;
3use walrus::Module;
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8use crate::{utils::*, Error};
9
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct WasmInfo {
13 language: LanguageSpecificInfo,
14 number_of_types: usize,
15 number_of_globals: usize,
16 number_of_data_sections: usize,
17 size_of_data_sections: usize,
18 number_of_functions: usize,
19 number_of_callbacks: usize,
20 start_function: Option<String>,
21 exported_methods: Vec<ExportedMethodInfo>,
22 imported_ic0_system_api: Vec<String>,
23 custom_sections: Vec<CustomSectionInfo>,
24}
25
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub enum LanguageSpecificInfo {
29 Motoko {
30 embedded_wasm: Vec<(String, WasmInfo)>,
31 },
32 Unknown,
33}
34
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37#[derive(Debug)]
38pub struct ExportedMethodInfo {
39 pub name: String,
40 pub internal_name: String,
41}
42
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45pub struct CustomSectionInfo {
46 name: String,
47 size: usize,
48}
49
50impl From<&Module> for WasmInfo {
51 fn from(m: &Module) -> WasmInfo {
52 let (number_of_data_sections, size_of_data_sections) = m
53 .data
54 .iter()
55 .fold((0, 0), |(count, size), d| (count + 1, size + d.value.len()));
56
57 WasmInfo {
58 language: LanguageSpecificInfo::from(m),
59 number_of_types: m.types.iter().count(),
60 number_of_globals: m.globals.iter().count(),
61 number_of_data_sections,
62 size_of_data_sections,
63 number_of_functions: m.funcs.iter().count(),
64 number_of_callbacks: m.elements.iter().count(),
65 start_function: m.start.map(|id| get_func_name(m, id)),
66 exported_methods: get_exported_methods(m),
67 imported_ic0_system_api: m
68 .imports
69 .iter()
70 .filter(|i| i.module == "ic0")
71 .map(|i| i.name.clone())
72 .collect(),
73 custom_sections: m
74 .customs
75 .iter()
76 .map(|(_, s)| CustomSectionInfo {
77 name: s.name().to_string(),
78 size: s.data(&Default::default()).len(),
79 })
80 .collect(),
81 }
82 }
83}
84
85impl From<&Module> for LanguageSpecificInfo {
86 fn from(m: &Module) -> LanguageSpecificInfo {
87 if is_motoko_canister(m) {
88 let mut embedded_wasm = Vec::new();
89 for (data_id, embedded_module) in get_motoko_wasm_data_sections(m) {
90 embedded_wasm.push((format!("{data_id:?}"), WasmInfo::from(&embedded_module)));
91 }
92 return LanguageSpecificInfo::Motoko { embedded_wasm };
93 }
94 LanguageSpecificInfo::Unknown
95 }
96}
97
98impl fmt::Display for WasmInfo {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 write!(f, "{}", self.language)?;
101 writeln!(f, "Number of types: {}", self.number_of_types)?;
102 writeln!(f, "Number of globals: {}", self.number_of_globals)?;
103 writeln!(f)?;
104 writeln!(
105 f,
106 "Number of data sections: {}",
107 self.number_of_data_sections
108 )?;
109 writeln!(
110 f,
111 "Size of data sections: {} bytes",
112 self.size_of_data_sections
113 )?;
114 writeln!(f)?;
115 writeln!(f, "Number of functions: {}", self.number_of_functions)?;
116 writeln!(f, "Number of callbacks: {}", self.number_of_callbacks)?;
117 writeln!(f, "Start function: {:?}", self.start_function)?;
118 let exports: Vec<_> = self
119 .exported_methods
120 .iter()
121 .map(
122 |ExportedMethodInfo {
123 name,
124 internal_name,
125 }| {
126 if name == internal_name {
127 internal_name.clone()
128 } else {
129 format!("{name} ({internal_name})")
130 }
131 },
132 )
133 .collect();
134 writeln!(f, "Exported methods: {exports:#?}")?;
135 writeln!(f)?;
136 writeln!(
137 f,
138 "Imported IC0 System API: {:#?}",
139 self.imported_ic0_system_api
140 )?;
141 writeln!(f)?;
142 let customs: Vec<_> = self
143 .custom_sections
144 .iter()
145 .map(|section_info| format!("{} ({} bytes)", section_info.name, section_info.size))
146 .collect();
147 writeln!(f, "Custom sections with size: {customs:#?}")?;
148 Ok(())
149 }
150}
151
152impl fmt::Display for LanguageSpecificInfo {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154 match self {
155 LanguageSpecificInfo::Motoko { embedded_wasm } => {
156 writeln!(f, "This is a Motoko canister")?;
157 for (_, wasm_info) in embedded_wasm {
158 writeln!(f, "--- Start decoding an embedded Wasm ---")?;
159 write!(f, "{wasm_info}")?;
160 writeln!(f, "--- End of decoding ---")?;
161 }
162 writeln!(f)
163 }
164 LanguageSpecificInfo::Unknown => Ok(()),
165 }
166 }
167}
168
169pub fn info(m: &Module, output: &mut dyn Write) -> Result<(), Error> {
171 write!(output, "{}", WasmInfo::from(m))?;
172 Ok(())
173}