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(msg: &str, expected: &WasmSubType, actual: &WasmSubType) -> crate::Error {
104 format_err!("{msg}: expected type `{expected}`, found type `{actual}`")
105}
106
107fn global_ty(engine: &Engine, expected: &Global, actual: &Global) -> Result<()> {
108 if expected.mutability || actual.mutability {
112 equal_ty(expected.wasm_ty, actual.wasm_ty, "global")?;
113 } else {
114 match_ty(engine, expected.wasm_ty, actual.wasm_ty, "global")?;
115 }
116 match_bool(
117 expected.mutability,
118 actual.mutability,
119 "global",
120 "mutable",
121 "immutable",
122 )?;
123 Ok(())
124}
125
126fn table_ty(expected: &Table, actual: &Table, actual_runtime_size: Option<u64>) -> Result<()> {
127 equal_ty(
128 WasmValType::Ref(expected.ref_type),
129 WasmValType::Ref(actual.ref_type),
130 "table",
131 )?;
132 match_index(expected.idx_type, actual.idx_type, "table")?;
133 match_limits(
134 expected.limits.min,
135 expected.limits.max,
136 actual_runtime_size.unwrap_or(actual.limits.min),
137 actual.limits.max,
138 "table",
139 )?;
140 Ok(())
141}
142
143fn memory_ty(expected: &Memory, actual: &Memory, actual_runtime_size: Option<u64>) -> Result<()> {
144 match_bool(
145 expected.shared,
146 actual.shared,
147 "memory",
148 "shared",
149 "non-shared",
150 )?;
151 match_index(expected.idx_type, actual.idx_type, "memory")?;
152 match_limits(
153 expected.limits.min,
154 expected.limits.max,
155 actual_runtime_size.unwrap_or(actual.limits.min),
156 actual.limits.max,
157 "memory",
158 )?;
159 if expected.page_size_log2 != actual.page_size_log2 {
160 bail!(
161 "memory types incompatible: expected a memory with a page size of \
162 {}, but received a memory with a page size of {}",
163 expected.page_size(),
164 actual.page_size(),
165 )
166 }
167 Ok(())
168}
169
170fn tag_ty(expected: &Tag, actual: &Tag) -> Result<()> {
171 if expected.signature == actual.signature {
172 Ok(())
173 } else {
174 bail!("incompatible tag types")
175 }
176}
177
178fn match_heap(
179 engine: &Engine,
180 expected: WasmHeapType,
181 actual: WasmHeapType,
182 desc: &str,
183) -> Result<()> {
184 use WasmHeapType as H;
185 let result = match (actual, expected) {
186 (H::ConcreteArray(actual), H::ConcreteArray(expected))
187 | (H::ConcreteFunc(actual), H::ConcreteFunc(expected))
188 | (H::ConcreteStruct(actual), H::ConcreteStruct(expected))
189 | (H::ConcreteCont(actual), H::ConcreteCont(expected))
190 | (H::ConcreteExn(actual), H::ConcreteExn(expected)) => {
191 let actual = actual.unwrap_engine_type_index();
192 let expected = expected.unwrap_engine_type_index();
193 engine.signatures().is_subtype(actual, expected)
194 }
195
196 (H::NoFunc, H::NoFunc) => true,
197 (_, H::NoFunc) => false,
198
199 (H::NoFunc, H::ConcreteFunc(_)) => true,
200 (_, H::ConcreteFunc(_)) => false,
201
202 (H::NoFunc | H::ConcreteFunc(_) | H::Func, H::Func) => true,
203 (_, H::Func) => false,
204
205 (H::Extern | H::NoExtern, H::Extern) => true,
206 (_, H::Extern) => false,
207
208 (H::NoExtern, H::NoExtern) => true,
209 (_, H::NoExtern) => false,
210
211 (
212 H::Any
213 | H::Eq
214 | H::I31
215 | H::Array
216 | H::ConcreteArray(_)
217 | H::Struct
218 | H::ConcreteStruct(_)
219 | H::None,
220 H::Any,
221 ) => true,
222 (_, H::Any) => false,
223
224 (
225 H::Eq
226 | H::I31
227 | H::Array
228 | H::ConcreteArray(_)
229 | H::Struct
230 | H::ConcreteStruct(_)
231 | H::None,
232 H::Eq,
233 ) => true,
234 (_, H::Eq) => false,
235
236 (H::I31 | H::None, H::I31) => true,
237 (_, H::I31) => false,
238
239 (H::Array | H::ConcreteArray(_) | H::None, H::Array) => true,
240 (_, H::Array) => false,
241
242 (H::None, H::ConcreteArray(_)) => true,
243 (_, H::ConcreteArray(_)) => false,
244
245 (H::Struct | H::ConcreteStruct(_) | H::None, H::Struct) => true,
246 (_, H::Struct) => false,
247
248 (H::None, H::ConcreteStruct(_)) => true,
249 (_, H::ConcreteStruct(_)) => false,
250
251 (H::NoCont | H::ConcreteCont(_) | H::Cont, H::Cont) => true,
252 (_, H::Cont) => false,
253
254 (H::NoCont, H::ConcreteCont(_)) => true,
255 (H::NoCont, H::NoCont) => true,
256
257 (_, H::NoCont) => false,
258 (_, H::ConcreteCont(_)) => false,
259
260 (H::NoExn | H::ConcreteExn(_) | H::Exn, H::Exn) => true,
261 (_, H::Exn) => false,
262
263 (H::NoExn, H::ConcreteExn(_)) => true,
264 (H::NoExn, H::NoExn) => true,
265
266 (_, H::NoExn) => false,
267 (_, H::ConcreteExn(_)) => false,
268
269 (H::None, H::None) => true,
270 (_, H::None) => false,
271 };
272 if result {
273 Ok(())
274 } else {
275 bail!(
276 "{desc} types incompatible: expected {desc} of type `{expected}`, \
277 found {desc} of type `{actual}`",
278 )
279 }
280}
281
282fn match_ref(
283 engine: &Engine,
284 expected: WasmRefType,
285 actual: WasmRefType,
286 desc: &str,
287) -> Result<()> {
288 if actual.nullable == expected.nullable || expected.nullable {
289 return match_heap(engine, expected.heap_type, actual.heap_type, desc);
290 }
291 bail!(
292 "{desc} types incompatible: expected {desc} of type `{expected}`, \
293 found {desc} of type `{actual}`",
294 )
295}
296
297fn match_ty(engine: &Engine, expected: WasmValType, actual: WasmValType, desc: &str) -> Result<()> {
300 debug_assert!(
303 expected.is_canonicalized_for_runtime_usage(),
304 "expected type should be canonicalized for runtime usage: {expected:?}"
305 );
306 debug_assert!(
307 actual.is_canonicalized_for_runtime_usage(),
308 "actual type should be canonicalized for runtime usage: {actual:?}"
309 );
310
311 match (actual, expected) {
312 (WasmValType::Ref(actual), WasmValType::Ref(expected)) => {
313 match_ref(engine, expected, actual, desc)
314 }
315 (actual, expected) => equal_ty(expected, actual, desc),
316 }
317}
318
319fn equal_ty(expected: WasmValType, actual: WasmValType, desc: &str) -> Result<()> {
320 debug_assert!(
323 expected.is_canonicalized_for_runtime_usage(),
324 "expected type should be canonicalized for runtime usage: {expected:?}"
325 );
326 debug_assert!(
327 actual.is_canonicalized_for_runtime_usage(),
328 "actual type should be canonicalized for runtime usage: {actual:?}"
329 );
330
331 if expected == actual {
332 return Ok(());
333 }
334 bail!(
335 "{desc} types incompatible: expected {desc} of type `{expected}`, \
336 found {desc} of type `{actual}`",
337 )
338}
339
340fn match_bool(
341 expected: bool,
342 actual: bool,
343 desc: &str,
344 if_true: &str,
345 if_false: &str,
346) -> Result<()> {
347 if expected == actual {
348 return Ok(());
349 }
350 let expected = if expected { if_true } else { if_false };
351 let actual = if actual { if_true } else { if_false };
352 bail!(
353 "{desc} types incompatible: expected {expected} {desc}, \
354 found {actual} {desc}",
355 )
356}
357
358fn match_index(expected: IndexType, actual: IndexType, desc: &str) -> Result<()> {
359 if expected == actual {
360 return Ok(());
361 }
362 const S64: &str = "64-bit";
363 const S32: &str = "32-bit";
364 let expected = if matches!(expected, IndexType::I64) {
365 S64
366 } else {
367 S32
368 };
369 let actual = if matches!(actual, IndexType::I64) {
370 S64
371 } else {
372 S32
373 };
374 bail!(
375 "{desc} types incompatible: expected {expected} {desc}, \
376 found {actual} {desc}",
377 )
378}
379
380fn match_limits(
381 expected_min: u64,
382 expected_max: Option<u64>,
383 actual_min: u64,
384 actual_max: Option<u64>,
385 desc: &str,
386) -> Result<()> {
387 if expected_min <= actual_min
388 && match expected_max {
389 Some(expected) => match actual_max {
390 Some(actual) => expected >= actual,
391 None => false,
392 },
393 None => true,
394 }
395 {
396 return Ok(());
397 }
398 let limits = |min: u64, max: Option<u64>| {
399 format!(
400 "min: {}, max: {}",
401 min,
402 max.map(|s| s.to_string()).unwrap_or(String::from("none"))
403 )
404 };
405 bail!(
406 "{} types incompatible: expected {0} limits ({}) doesn't match provided {0} limits ({})",
407 desc,
408 limits(expected_min, expected_max),
409 limits(actual_min, actual_max)
410 )
411}
412
413#[cfg(feature = "component-model")]
414fn entity_desc(ty: &EntityType) -> &'static str {
415 match ty {
416 EntityType::Global(_) => "global",
417 EntityType::Table(_) => "table",
418 EntityType::Memory(_) => "memory",
419 EntityType::Function(_) => "func",
420 EntityType::Tag(_) => "tag",
421 }
422}