1use crate::prelude::*;
2use crate::{Engine, linker::DefinitionType};
3use wasmtime_environ::{
4 EntityType, Global, IndexType, Memory, Table, Tag, TypeTrace, VMSharedTypeIndex, WasmHeapType,
5 WasmRefType, WasmSubType, WasmValType,
6};
7
8pub struct MatchCx<'a> {
9 engine: &'a Engine,
10}
11
12impl MatchCx<'_> {
13 pub fn new(engine: &Engine) -> MatchCx<'_> {
15 MatchCx { engine }
16 }
17
18 pub(crate) fn definition(&self, expected: &EntityType, actual: &DefinitionType) -> Result<()> {
20 match expected {
21 EntityType::Global(expected) => match actual {
22 DefinitionType::Global(actual) => global_ty(self.engine, expected, actual),
23 _ => bail!("expected global, but found {}", actual.desc()),
24 },
25 EntityType::Table(expected) => match actual {
26 DefinitionType::Table(actual, cur_size) => {
27 table_ty(expected, actual, Some(*cur_size))
28 }
29 _ => bail!("expected table, but found {}", actual.desc()),
30 },
31 EntityType::Memory(expected) => match actual {
32 DefinitionType::Memory(actual, cur_size) => {
33 memory_ty(expected, actual, Some(*cur_size))
34 }
35 _ => bail!("expected memory, but found {}", actual.desc()),
36 },
37 EntityType::Function(expected) => match actual {
38 DefinitionType::Func(actual) => {
39 type_reference(self.engine, expected.unwrap_engine_type_index(), *actual)
40 }
41 _ => bail!("expected func, but found {}", actual.desc()),
42 },
43 EntityType::Tag(expected) => match actual {
44 DefinitionType::Tag(actual) => tag_ty(expected, actual),
45 _ => bail!("expected tag, but found {}", actual.desc()),
46 },
47 }
48 }
49}
50
51fn type_reference(
52 engine: &Engine,
53 expected: VMSharedTypeIndex,
54 actual: VMSharedTypeIndex,
55) -> Result<()> {
56 if engine.signatures().is_subtype(actual, expected) {
57 return Ok(());
58 }
59
60 let msg = "types incompatible";
61 let expected = match engine.signatures().borrow(expected) {
62 Some(ty) => ty,
63 None => panic!("{expected:?} is not registered"),
64 };
65 let actual = match engine.signatures().borrow(actual) {
66 Some(ty) => ty,
67 None => panic!("{actual:?} is not registered"),
68 };
69
70 Err(concrete_type_mismatch(msg, &expected, &actual))
71}
72
73#[cfg(feature = "component-model")]
74pub fn entity_ty(engine: &Engine, expected: &EntityType, actual: &EntityType) -> Result<()> {
75 match expected {
76 EntityType::Memory(expected) => match actual {
77 EntityType::Memory(actual) => memory_ty(expected, actual, None),
78 _ => bail!("expected memory found {}", entity_desc(actual)),
79 },
80 EntityType::Global(expected) => match actual {
81 EntityType::Global(actual) => global_ty(engine, expected, actual),
82 _ => bail!("expected global found {}", entity_desc(actual)),
83 },
84 EntityType::Table(expected) => match actual {
85 EntityType::Table(actual) => table_ty(expected, actual, None),
86 _ => bail!("expected table found {}", entity_desc(actual)),
87 },
88 EntityType::Function(expected) => match actual {
89 EntityType::Function(actual) => {
90 let expected = expected.unwrap_engine_type_index();
91 let actual = actual.unwrap_engine_type_index();
92 type_reference(engine, expected, actual)
93 }
94 _ => bail!("expected func found {}", entity_desc(actual)),
95 },
96 EntityType::Tag(expected) => match actual {
97 EntityType::Tag(actual) => tag_ty(expected, actual),
98 _ => bail!("expected tag found {}", entity_desc(actual)),
99 },
100 }
101}
102
103fn concrete_type_mismatch(
104 msg: &str,
105 expected: &WasmSubType,
106 actual: &WasmSubType,
107) -> anyhow::Error {
108 anyhow!("{msg}: expected type `{expected}`, found type `{actual}`")
109}
110
111fn global_ty(engine: &Engine, expected: &Global, actual: &Global) -> Result<()> {
112 if expected.mutability || actual.mutability {
116 equal_ty(expected.wasm_ty, actual.wasm_ty, "global")?;
117 } else {
118 match_ty(engine, expected.wasm_ty, actual.wasm_ty, "global")?;
119 }
120 match_bool(
121 expected.mutability,
122 actual.mutability,
123 "global",
124 "mutable",
125 "immutable",
126 )?;
127 Ok(())
128}
129
130fn table_ty(expected: &Table, actual: &Table, actual_runtime_size: Option<u64>) -> Result<()> {
131 equal_ty(
132 WasmValType::Ref(expected.ref_type),
133 WasmValType::Ref(actual.ref_type),
134 "table",
135 )?;
136 match_index(expected.idx_type, actual.idx_type, "table")?;
137 match_limits(
138 expected.limits.min,
139 expected.limits.max,
140 actual_runtime_size.unwrap_or(actual.limits.min),
141 actual.limits.max,
142 "table",
143 )?;
144 Ok(())
145}
146
147fn memory_ty(expected: &Memory, actual: &Memory, actual_runtime_size: Option<u64>) -> Result<()> {
148 match_bool(
149 expected.shared,
150 actual.shared,
151 "memory",
152 "shared",
153 "non-shared",
154 )?;
155 match_index(expected.idx_type, actual.idx_type, "memory")?;
156 match_limits(
157 expected.limits.min,
158 expected.limits.max,
159 actual_runtime_size.unwrap_or(actual.limits.min),
160 actual.limits.max,
161 "memory",
162 )?;
163 if expected.page_size_log2 != actual.page_size_log2 {
164 bail!(
165 "memory types incompatible: expected a memory with a page size of \
166 {}, but received a memory with a page size of {}",
167 expected.page_size(),
168 actual.page_size(),
169 )
170 }
171 Ok(())
172}
173
174fn tag_ty(expected: &Tag, actual: &Tag) -> Result<()> {
175 if expected.signature == actual.signature {
176 Ok(())
177 } else {
178 bail!("incompatible tag types")
179 }
180}
181
182fn match_heap(
183 engine: &Engine,
184 expected: WasmHeapType,
185 actual: WasmHeapType,
186 desc: &str,
187) -> Result<()> {
188 use WasmHeapType as H;
189 let result = match (actual, expected) {
190 (H::ConcreteArray(actual), H::ConcreteArray(expected))
191 | (H::ConcreteFunc(actual), H::ConcreteFunc(expected))
192 | (H::ConcreteStruct(actual), H::ConcreteStruct(expected))
193 | (H::ConcreteCont(actual), H::ConcreteCont(expected))
194 | (H::ConcreteExn(actual), H::ConcreteExn(expected)) => {
195 let actual = actual.unwrap_engine_type_index();
196 let expected = expected.unwrap_engine_type_index();
197 engine.signatures().is_subtype(actual, expected)
198 }
199
200 (H::NoFunc, H::NoFunc) => true,
201 (_, H::NoFunc) => false,
202
203 (H::NoFunc, H::ConcreteFunc(_)) => true,
204 (_, H::ConcreteFunc(_)) => false,
205
206 (H::NoFunc | H::ConcreteFunc(_) | H::Func, H::Func) => true,
207 (_, H::Func) => false,
208
209 (H::Extern | H::NoExtern, H::Extern) => true,
210 (_, H::Extern) => false,
211
212 (H::NoExtern, H::NoExtern) => true,
213 (_, H::NoExtern) => false,
214
215 (
216 H::Any
217 | H::Eq
218 | H::I31
219 | H::Array
220 | H::ConcreteArray(_)
221 | H::Struct
222 | H::ConcreteStruct(_)
223 | H::None,
224 H::Any,
225 ) => true,
226 (_, H::Any) => false,
227
228 (
229 H::Eq
230 | H::I31
231 | H::Array
232 | H::ConcreteArray(_)
233 | H::Struct
234 | H::ConcreteStruct(_)
235 | H::None,
236 H::Eq,
237 ) => true,
238 (_, H::Eq) => false,
239
240 (H::I31 | H::None, H::I31) => true,
241 (_, H::I31) => false,
242
243 (H::Array | H::ConcreteArray(_) | H::None, H::Array) => true,
244 (_, H::Array) => false,
245
246 (H::None, H::ConcreteArray(_)) => true,
247 (_, H::ConcreteArray(_)) => false,
248
249 (H::Struct | H::ConcreteStruct(_) | H::None, H::Struct) => true,
250 (_, H::Struct) => false,
251
252 (H::None, H::ConcreteStruct(_)) => true,
253 (_, H::ConcreteStruct(_)) => false,
254
255 (H::NoCont | H::ConcreteCont(_) | H::Cont, H::Cont) => true,
256 (_, H::Cont) => false,
257
258 (H::NoCont, H::ConcreteCont(_)) => true,
259 (H::NoCont, H::NoCont) => true,
260
261 (_, H::NoCont) => false,
262 (_, H::ConcreteCont(_)) => false,
263
264 (H::NoExn | H::ConcreteExn(_) | H::Exn, H::Exn) => true,
265 (_, H::Exn) => false,
266
267 (H::NoExn, H::ConcreteExn(_)) => true,
268 (H::NoExn, H::NoExn) => true,
269
270 (_, H::NoExn) => false,
271 (_, H::ConcreteExn(_)) => false,
272
273 (H::None, H::None) => true,
274 (_, H::None) => false,
275 };
276 if result {
277 Ok(())
278 } else {
279 bail!(
280 "{desc} types incompatible: expected {desc} of type `{expected}`, \
281 found {desc} of type `{actual}`",
282 )
283 }
284}
285
286fn match_ref(
287 engine: &Engine,
288 expected: WasmRefType,
289 actual: WasmRefType,
290 desc: &str,
291) -> Result<()> {
292 if actual.nullable == expected.nullable || expected.nullable {
293 return match_heap(engine, expected.heap_type, actual.heap_type, desc);
294 }
295 bail!(
296 "{desc} types incompatible: expected {desc} of type `{expected}`, \
297 found {desc} of type `{actual}`",
298 )
299}
300
301fn match_ty(engine: &Engine, expected: WasmValType, actual: WasmValType, desc: &str) -> Result<()> {
304 debug_assert!(
307 expected.is_canonicalized_for_runtime_usage(),
308 "expected type should be canonicalized for runtime usage: {expected:?}"
309 );
310 debug_assert!(
311 actual.is_canonicalized_for_runtime_usage(),
312 "actual type should be canonicalized for runtime usage: {actual:?}"
313 );
314
315 match (actual, expected) {
316 (WasmValType::Ref(actual), WasmValType::Ref(expected)) => {
317 match_ref(engine, expected, actual, desc)
318 }
319 (actual, expected) => equal_ty(expected, actual, desc),
320 }
321}
322
323fn equal_ty(expected: WasmValType, actual: WasmValType, desc: &str) -> Result<()> {
324 debug_assert!(
327 expected.is_canonicalized_for_runtime_usage(),
328 "expected type should be canonicalized for runtime usage: {expected:?}"
329 );
330 debug_assert!(
331 actual.is_canonicalized_for_runtime_usage(),
332 "actual type should be canonicalized for runtime usage: {actual:?}"
333 );
334
335 if expected == actual {
336 return Ok(());
337 }
338 bail!(
339 "{desc} types incompatible: expected {desc} of type `{expected}`, \
340 found {desc} of type `{actual}`",
341 )
342}
343
344fn match_bool(
345 expected: bool,
346 actual: bool,
347 desc: &str,
348 if_true: &str,
349 if_false: &str,
350) -> Result<()> {
351 if expected == actual {
352 return Ok(());
353 }
354 let expected = if expected { if_true } else { if_false };
355 let actual = if actual { if_true } else { if_false };
356 bail!(
357 "{desc} types incompatible: expected {expected} {desc}, \
358 found {actual} {desc}",
359 )
360}
361
362fn match_index(expected: IndexType, actual: IndexType, desc: &str) -> Result<()> {
363 if expected == actual {
364 return Ok(());
365 }
366 const S64: &str = "64-bit";
367 const S32: &str = "32-bit";
368 let expected = if matches!(expected, IndexType::I64) {
369 S64
370 } else {
371 S32
372 };
373 let actual = if matches!(actual, IndexType::I64) {
374 S64
375 } else {
376 S32
377 };
378 bail!(
379 "{desc} types incompatible: expected {expected} {desc}, \
380 found {actual} {desc}",
381 )
382}
383
384fn match_limits(
385 expected_min: u64,
386 expected_max: Option<u64>,
387 actual_min: u64,
388 actual_max: Option<u64>,
389 desc: &str,
390) -> Result<()> {
391 if expected_min <= actual_min
392 && match expected_max {
393 Some(expected) => match actual_max {
394 Some(actual) => expected >= actual,
395 None => false,
396 },
397 None => true,
398 }
399 {
400 return Ok(());
401 }
402 let limits = |min: u64, max: Option<u64>| {
403 format!(
404 "min: {}, max: {}",
405 min,
406 max.map(|s| s.to_string()).unwrap_or(String::from("none"))
407 )
408 };
409 bail!(
410 "{} types incompatible: expected {0} limits ({}) doesn't match provided {0} limits ({})",
411 desc,
412 limits(expected_min, expected_max),
413 limits(actual_min, actual_max)
414 )
415}
416
417#[cfg(feature = "component-model")]
418fn entity_desc(ty: &EntityType) -> &'static str {
419 match ty {
420 EntityType::Global(_) => "global",
421 EntityType::Table(_) => "table",
422 EntityType::Memory(_) => "memory",
423 EntityType::Function(_) => "func",
424 EntityType::Tag(_) => "tag",
425 }
426}