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<'_> {
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 
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                 .exports
123                 .get(name)
124                 .ok_or_else(|| anyhow!("module export `{name}` not defined"))?;
125             let actual = actual.type_of(*idx);
126             matching::entity_ty(self.engine, expected, &actual)
127                 .with_context(|| format!("module export `{name}` has the wrong type"))?;
128         }
129 
130         // Note the opposite order of checks here. Every import that the actual
131         // module expects should be imported by the expected module since the
132         // expected module has the set of items given to the actual module.
133         // Additionally the "matches" check is inverted here.
134         for (module, name, actual) in actual.imports() {
135             // TODO: shouldn't need a `.to_string()` here ideally
136             let expected = expected
137                 .imports
138                 .get(&(module.to_string(), name.to_string()))
139                 .ok_or_else(|| anyhow!("module import `{module}::{name}` not defined"))?;
140             matching::entity_ty(self.engine, &actual, expected)
141                 .with_context(|| format!("module import `{module}::{name}` has the wrong type"))?;
142         }
143         Ok(())
144     }
145 
146     fn instance(
147         &mut self,
148         expected: &TypeComponentInstance,
149         actual: Option<&NameMap<usize, Definition>>,
150     ) -> Result<()> {
151         // Like modules, every export in the expected type must be present in
152         // the actual type. It's ok, though, to have extra exports in the actual
153         // type.
154         for (name, expected) in expected.exports.iter() {
155             // Interface types may be exported from a component in order to give them a name, but
156             // they don't have a definition in the sense that this search is interested in, so
157             // ignore them.
158             if let TypeDef::Interface(_) = expected {
159                 continue;
160             }
161             let actual = actual.and_then(|map| map.get(name, self.strings));
162             self.definition(expected, actual)
163                 .with_context(|| format!("instance export `{name}` has the wrong type"))?;
164         }
165         Ok(())
166     }
167 
168     fn func(&self, expected: TypeFuncIndex, actual: &HostFunc) -> Result<()> {
169         let instance_type = InstanceType {
170             types: self.types,
171             resources: &self.imported_resources,
172         };
173         actual.typecheck(expected, &instance_type)
174     }
175 }
176 
177 impl Definition {
178     fn desc(&self) -> &'static str {
179         match self {
180             Definition::Module(_) => "module",
181             Definition::Func(_) => "func",
182             Definition::Instance(_) => "instance",
183             Definition::Resource(..) => "resource",
184         }
185     }
186 }
187 
188 impl<'a> InstanceType<'a> {
189     pub fn new(instance: &'a ComponentInstance) -> InstanceType<'a> {
190         InstanceType {
191             types: instance.component().types(),
192             resources: instance.resource_types(),
193         }
194     }
195 
196     pub fn resource_type(&self, index: TypeResourceTableIndex) -> ResourceType {
197         match self.types[index] {
198             TypeResourceTable::Concrete { ty, .. } => self
199                 .resources
200                 .get(ty)
201                 .copied()
202                 .unwrap_or_else(|| ResourceType::uninstantiated(&self.types, ty)),
203             TypeResourceTable::Abstract(ty) => ResourceType::abstract_(&self.types, ty),
204         }
205     }
206 
207     pub fn future_type(&self, index: TypeFutureTableIndex) -> FutureType {
208         FutureType::from(self.types[index].ty, self)
209     }
210 
211     pub fn stream_type(&self, index: TypeStreamTableIndex) -> StreamType {
212         StreamType::from(self.types[index].ty, self)
213     }
214 }
215