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