wasmtime/types/
matching.rs

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            // If our expected signature isn't registered, then there's no way
24            // that `actual` can match it.
25            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    /// Validates that the `expected` type matches the type of `actual`
44    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}