1use crate::linker::DefinitionType;
2use crate::{signatures::SignatureCollection, Engine};
3use anyhow::{anyhow, bail, Result};
4use wasmtime_environ::{
5 EntityType, Global, Memory, ModuleTypes, SignatureIndex, Table, WasmFuncType, WasmType,
6};
7use wasmtime_runtime::VMSharedSignatureIndex;
8
9pub struct MatchCx<'a> {
10 pub signatures: &'a SignatureCollection,
11 pub types: &'a ModuleTypes,
12 pub engine: &'a Engine,
13}
14
15impl MatchCx<'_> {
16 pub fn vmshared_signature_index(
17 &self,
18 expected: SignatureIndex,
19 actual: VMSharedSignatureIndex,
20 ) -> Result<()> {
21 let matches = match self.signatures.shared_signature(expected) {
22 Some(idx) => actual == idx,
23 None => false,
26 };
27 if matches {
28 return Ok(());
29 }
30 let msg = "function types incompatible";
31 let expected = &self.types[expected];
32 let actual = match self.engine.signatures().lookup_type(actual) {
33 Some(ty) => ty,
34 None => {
35 debug_assert!(false, "all signatures should be registered");
36 bail!("{}", msg);
37 }
38 };
39
40 Err(func_ty_mismatch(msg, expected, &actual))
41 }
42
43 pub(crate) fn definition(&self, expected: &EntityType, actual: &DefinitionType) -> Result<()> {
45 match expected {
46 EntityType::Global(expected) => match actual {
47 DefinitionType::Global(actual) => global_ty(expected, actual),
48 _ => bail!("expected global, but found {}", actual.desc()),
49 },
50 EntityType::Table(expected) => match actual {
51 DefinitionType::Table(actual, cur_size) => {
52 table_ty(expected, actual, Some(*cur_size))
53 }
54 _ => bail!("expected table, but found {}", actual.desc()),
55 },
56 EntityType::Memory(expected) => match actual {
57 DefinitionType::Memory(actual, cur_size) => {
58 memory_ty(expected, actual, Some(*cur_size))
59 }
60 _ => bail!("expected memory, but found {}", actual.desc()),
61 },
62 EntityType::Function(expected) => match actual {
63 DefinitionType::Func(actual) => self.vmshared_signature_index(*expected, *actual),
64 _ => bail!("expected func, but found {}", actual.desc()),
65 },
66 EntityType::Tag(_) => unimplemented!(),
67 }
68 }
69}
70
71#[cfg_attr(not(feature = "component-model"), allow(dead_code))]
72pub fn entity_ty(
73 expected: &EntityType,
74 expected_types: &ModuleTypes,
75 actual: &EntityType,
76 actual_types: &ModuleTypes,
77) -> Result<()> {
78 match expected {
79 EntityType::Memory(expected) => match actual {
80 EntityType::Memory(actual) => memory_ty(expected, actual, None),
81 _ => bail!("expected memory found {}", entity_desc(actual)),
82 },
83 EntityType::Global(expected) => match actual {
84 EntityType::Global(actual) => global_ty(expected, actual),
85 _ => bail!("expected global found {}", entity_desc(actual)),
86 },
87 EntityType::Table(expected) => match actual {
88 EntityType::Table(actual) => table_ty(expected, actual, None),
89 _ => bail!("expected table found {}", entity_desc(actual)),
90 },
91 EntityType::Function(expected) => match actual {
92 EntityType::Function(actual) => {
93 let expected = &expected_types[*expected];
94 let actual = &actual_types[*actual];
95 if expected == actual {
96 Ok(())
97 } else {
98 Err(func_ty_mismatch(
99 "function types incompaible",
100 expected,
101 actual,
102 ))
103 }
104 }
105 _ => bail!("expected func found {}", entity_desc(actual)),
106 },
107 EntityType::Tag(_) => unimplemented!(),
108 }
109}
110
111fn func_ty_mismatch(msg: &str, expected: &WasmFuncType, actual: &WasmFuncType) -> anyhow::Error {
112 let render = |ty: &WasmFuncType| {
113 let params = ty
114 .params()
115 .iter()
116 .map(|s| s.to_string())
117 .collect::<Vec<_>>()
118 .join(", ");
119 let returns = ty
120 .returns()
121 .iter()
122 .map(|s| s.to_string())
123 .collect::<Vec<_>>()
124 .join(", ");
125 format!("`({}) -> ({})`", params, returns)
126 };
127 anyhow!(
128 "{msg}: expected func of type {}, found func of type {}",
129 render(expected),
130 render(actual)
131 )
132}
133
134fn global_ty(expected: &Global, actual: &Global) -> Result<()> {
135 match_ty(expected.wasm_ty, actual.wasm_ty, "global")?;
136 match_bool(
137 expected.mutability,
138 actual.mutability,
139 "global",
140 "mutable",
141 "immutable",
142 )?;
143 Ok(())
144}
145
146fn table_ty(expected: &Table, actual: &Table, actual_runtime_size: Option<u32>) -> Result<()> {
147 match_ty(expected.wasm_ty, actual.wasm_ty, "table")?;
148 match_limits(
149 expected.minimum.into(),
150 expected.maximum.map(|i| i.into()),
151 actual_runtime_size.unwrap_or(actual.minimum).into(),
152 actual.maximum.map(|i| i.into()),
153 "table",
154 )?;
155 Ok(())
156}
157
158fn memory_ty(expected: &Memory, actual: &Memory, actual_runtime_size: Option<u64>) -> Result<()> {
159 match_bool(
160 expected.shared,
161 actual.shared,
162 "memory",
163 "shared",
164 "non-shared",
165 )?;
166 match_bool(
167 expected.memory64,
168 actual.memory64,
169 "memory",
170 "64-bit",
171 "32-bit",
172 )?;
173 match_limits(
174 expected.minimum,
175 expected.maximum,
176 actual_runtime_size.unwrap_or(actual.minimum),
177 actual.maximum,
178 "memory",
179 )?;
180 Ok(())
181}
182
183fn match_ty(expected: WasmType, actual: WasmType, desc: &str) -> Result<()> {
184 if expected == actual {
185 return Ok(());
186 }
187 bail!(
188 "{} types incompatible: expected {0} of type `{}`, found {0} of type `{}`",
189 desc,
190 expected,
191 actual,
192 )
193}
194
195fn match_bool(
196 expected: bool,
197 actual: bool,
198 desc: &str,
199 if_true: &str,
200 if_false: &str,
201) -> Result<()> {
202 if expected == actual {
203 return Ok(());
204 }
205 bail!(
206 "{} types incompatible: expected {} {0}, found {} {0}",
207 desc,
208 if expected { if_true } else { if_false },
209 if actual { if_true } else { if_false },
210 )
211}
212
213fn match_limits(
214 expected_min: u64,
215 expected_max: Option<u64>,
216 actual_min: u64,
217 actual_max: Option<u64>,
218 desc: &str,
219) -> Result<()> {
220 if expected_min <= actual_min
221 && match expected_max {
222 Some(expected) => match actual_max {
223 Some(actual) => expected >= actual,
224 None => false,
225 },
226 None => true,
227 }
228 {
229 return Ok(());
230 }
231 let limits = |min: u64, max: Option<u64>| {
232 format!(
233 "min: {}, max: {}",
234 min,
235 max.map(|s| s.to_string()).unwrap_or(String::from("none"))
236 )
237 };
238 bail!(
239 "{} types incompatible: expected {0} limits ({}) doesn't match provided {0} limits ({})",
240 desc,
241 limits(expected_min, expected_max),
242 limits(actual_min, actual_max)
243 )
244}
245
246fn entity_desc(ty: &EntityType) -> &'static str {
247 match ty {
248 EntityType::Global(_) => "global",
249 EntityType::Table(_) => "table",
250 EntityType::Memory(_) => "memory",
251 EntityType::Function(_) => "func",
252 EntityType::Tag(_) => "tag",
253 }
254}