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