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