wasmtime/runtime/component/
matching.rs

1use crate::Module;
2use crate::component::ResourceType;
3use crate::component::func::HostFunc;
4use crate::component::linker::{Definition, Strings};
5use crate::runtime::vm::component::ComponentInstance;
6use crate::types::matching;
7use crate::{Engine, prelude::*};
8use alloc::sync::Arc;
9use wasmtime_environ::PrimaryMap;
10use wasmtime_environ::component::{
11    ComponentTypes, NameMap, ResourceIndex, TypeComponentInstance, TypeDef, TypeFuncIndex,
12    TypeModule, TypeResourceTableIndex,
13};
14
15pub struct TypeChecker<'a> {
16    pub engine: &'a Engine,
17    pub types: &'a Arc<ComponentTypes>,
18    pub strings: &'a Strings,
19    pub imported_resources: Arc<PrimaryMap<ResourceIndex, ResourceType>>,
20}
21
22#[derive(Copy, Clone)]
23#[doc(hidden)]
24pub struct InstanceType<'a> {
25    pub types: &'a Arc<ComponentTypes>,
26    pub resources: &'a Arc<PrimaryMap<ResourceIndex, ResourceType>>,
27}
28
29impl TypeChecker<'_> {
30    pub(crate) fn definition(
31        &mut self,
32        expected: &TypeDef,
33        actual: Option<&Definition>,
34    ) -> Result<()> {
35        match *expected {
36            TypeDef::Module(t) => match actual {
37                Some(Definition::Module(actual)) => self.module(&self.types[t], actual),
38                Some(actual) => bail!("expected module found {}", actual.desc()),
39                None => bail!("module implementation is missing"),
40            },
41            TypeDef::ComponentInstance(t) => match actual {
42                Some(Definition::Instance(actual)) => self.instance(&self.types[t], Some(actual)),
43                None => self.instance(&self.types[t], None),
44                Some(actual) => bail!("expected instance found {}", actual.desc()),
45            },
46            TypeDef::ComponentFunc(t) => match actual {
47                Some(Definition::Func(actual)) => self.func(t, actual),
48                Some(actual) => bail!("expected function found {}", actual.desc()),
49                None => bail!("function implementation is missing"),
50            },
51            TypeDef::Component(_) => match actual {
52                Some(actual) => bail!("expected component found {}", actual.desc()),
53                None => bail!("component implementation is missing"),
54            },
55            TypeDef::Interface(_) => match actual {
56                Some(actual) => bail!("expected type found {}", actual.desc()),
57                None => bail!("type implementation is missing"),
58            },
59
60            TypeDef::Resource(i) => {
61                let i = self.types[i].ty;
62                let actual = match actual {
63                    Some(Definition::Resource(actual, _dtor)) => actual,
64
65                    // If a resource is imported yet nothing was supplied then
66                    // that's only successful if the resource has itself
67                    // already been defined. If it's already defined then that
68                    // means that this is an `(eq ...)` import which is not
69                    // required to be satisfied via `Linker` definitions in the
70                    // Wasmtime API.
71                    None if self.imported_resources.get(i).is_some() => return Ok(()),
72
73                    Some(actual) => bail!("expected resource found {}", actual.desc()),
74                    None => bail!("resource implementation is missing"),
75                };
76
77                match self.imported_resources.get(i) {
78                    // If `i` hasn't been pushed onto `imported_resources` yet
79                    // then that means that it's the first time a new resource
80                    // was introduced, so record the type of this resource.  It
81                    // should always be the case that the next index assigned
82                    // is equal to `i` since types should be checked in the
83                    // same order they were assigned into the `Component` type.
84                    //
85                    // Note the `get_mut` here which is expected to always
86                    // succeed since `imported_resources` has not yet been
87                    // cloned.
88                    None => {
89                        let resources = Arc::get_mut(&mut self.imported_resources).unwrap();
90                        let id = resources.push(*actual);
91                        assert_eq!(id, i);
92                    }
93
94                    // If `i` has been defined, however, then that means that
95                    // this is an `(eq ..)` bounded type imported because it's
96                    // referring to a previously defined type.  In this
97                    // situation it's not required to provide a type import but
98                    // if it's supplied then it must be equal. In this situation
99                    // it's supplied, so test for equality.
100                    Some(expected) => {
101                        if expected != actual {
102                            bail!("mismatched resource types");
103                        }
104                    }
105                }
106                Ok(())
107            }
108
109            // not possible for valid components to import
110            TypeDef::CoreFunc(_) => unreachable!(),
111        }
112    }
113
114    fn module(&self, expected: &TypeModule, actual: &Module) -> Result<()> {
115        let actual = actual.env_module();
116
117        // Every export that is expected should be in the actual module we have
118        for (name, expected) in expected.exports.iter() {
119            let idx = actual
120                .exports
121                .get(name)
122                .ok_or_else(|| anyhow!("module export `{name}` not defined"))?;
123            let actual = actual.type_of(*idx);
124            matching::entity_ty(self.engine, expected, &actual)
125                .with_context(|| format!("module export `{name}` has the wrong type"))?;
126        }
127
128        // Note the opposite order of checks here. Every import that the actual
129        // module expects should be imported by the expected module since the
130        // expected module has the set of items given to the actual module.
131        // Additionally the "matches" check is inverted here.
132        for (module, name, actual) in actual.imports() {
133            // TODO: shouldn't need a `.to_string()` here ideally
134            let expected = expected
135                .imports
136                .get(&(module.to_string(), name.to_string()))
137                .ok_or_else(|| anyhow!("module import `{module}::{name}` not defined"))?;
138            matching::entity_ty(self.engine, &actual, expected)
139                .with_context(|| format!("module import `{module}::{name}` has the wrong type"))?;
140        }
141        Ok(())
142    }
143
144    fn instance(
145        &mut self,
146        expected: &TypeComponentInstance,
147        actual: Option<&NameMap<usize, Definition>>,
148    ) -> Result<()> {
149        // Like modules, every export in the expected type must be present in
150        // the actual type. It's ok, though, to have extra exports in the actual
151        // type.
152        for (name, expected) in expected.exports.iter() {
153            // Interface types may be exported from a component in order to give them a name, but
154            // they don't have a definition in the sense that this search is interested in, so
155            // ignore them.
156            if let TypeDef::Interface(_) = expected {
157                continue;
158            }
159            let actual = actual.and_then(|map| map.get(name, self.strings));
160            self.definition(expected, actual)
161                .with_context(|| format!("instance export `{name}` has the wrong type"))?;
162        }
163        Ok(())
164    }
165
166    fn func(&self, expected: TypeFuncIndex, actual: &HostFunc) -> Result<()> {
167        let instance_type = InstanceType {
168            types: self.types,
169            resources: &self.imported_resources,
170        };
171        actual.typecheck(expected, &instance_type)
172    }
173}
174
175impl Definition {
176    fn desc(&self) -> &'static str {
177        match self {
178            Definition::Module(_) => "module",
179            Definition::Func(_) => "func",
180            Definition::Instance(_) => "instance",
181            Definition::Resource(..) => "resource",
182        }
183    }
184}
185
186impl<'a> InstanceType<'a> {
187    pub fn new(instance: &'a ComponentInstance) -> InstanceType<'a> {
188        InstanceType {
189            types: instance.component().types(),
190            resources: instance.resource_types(),
191        }
192    }
193
194    pub fn resource_type(&self, index: TypeResourceTableIndex) -> ResourceType {
195        let index = self.types[index].ty;
196        self.resources
197            .get(index)
198            .copied()
199            .unwrap_or_else(|| ResourceType::uninstantiated(&self.types, index))
200    }
201}