| use crate::*; |
| use anyhow::{anyhow, bail}; |
| use indexmap::IndexSet; |
| use std::mem; |
| use std::{collections::HashMap, io::Read}; |
| use wasmparser::Chunk; |
| use wasmparser::{ |
| component_types::{ |
| ComponentAnyTypeId, ComponentDefinedType, ComponentEntityType, ComponentFuncType, |
| ComponentInstanceType, ComponentType, ComponentValType, |
| }, |
| names::{ComponentName, ComponentNameKind}, |
| types, |
| types::Types, |
| ComponentExternalKind, Parser, Payload, PrimitiveValType, ValidPayload, Validator, |
| }; |
| |
| /// Represents information about a decoded WebAssembly component. |
| struct ComponentInfo { |
| /// Wasmparser-defined type information learned after a component is fully |
| /// validated. |
| types: types::Types, |
| /// List of all imports and exports from this component. |
| externs: Vec<(String, Extern)>, |
| /// Decoded package metadata |
| package_metadata: Option<PackageMetadata>, |
| } |
| |
| struct DecodingExport { |
| name: String, |
| kind: ComponentExternalKind, |
| index: u32, |
| } |
| |
| enum Extern { |
| Import(String), |
| Export(DecodingExport), |
| } |
| |
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| enum WitEncodingVersion { |
| V1, |
| V2, |
| } |
| |
| impl ComponentInfo { |
| /// Creates a new component info by parsing the given WebAssembly component bytes. |
| |
| fn from_reader(mut reader: impl Read) -> Result<Self> { |
| let mut validator = Validator::new(); |
| let mut externs = Vec::new(); |
| let mut depth = 1; |
| let mut types = None; |
| let mut _package_metadata = None; |
| let mut cur = Parser::new(0); |
| let mut eof = false; |
| let mut stack = Vec::new(); |
| let mut buffer = Vec::new(); |
| |
| loop { |
| let chunk = cur.parse(&buffer, eof)?; |
| let (payload, consumed) = match chunk { |
| Chunk::NeedMoreData(hint) => { |
| assert!(!eof); // otherwise an error would be returned |
| |
| // Use the hint to preallocate more space, then read |
| // some more data into our buffer. |
| // |
| // Note that the buffer management here is not ideal, |
| // but it's compact enough to fit in an example! |
| let len = buffer.len(); |
| buffer.extend((0..hint).map(|_| 0u8)); |
| let n = reader.read(&mut buffer[len..])?; |
| buffer.truncate(len + n); |
| eof = n == 0; |
| continue; |
| } |
| |
| Chunk::Parsed { consumed, payload } => (payload, consumed), |
| }; |
| match validator.payload(&payload)? { |
| ValidPayload::Ok => {} |
| ValidPayload::Parser(_) => depth += 1, |
| ValidPayload::End(t) => { |
| depth -= 1; |
| if depth == 0 { |
| types = Some(t); |
| } |
| } |
| ValidPayload::Func(..) => {} |
| } |
| |
| match payload { |
| Payload::ComponentImportSection(s) if depth == 1 => { |
| for import in s { |
| let import = import?; |
| externs.push(( |
| import.name.0.to_string(), |
| Extern::Import(import.name.0.to_string()), |
| )); |
| } |
| } |
| Payload::ComponentExportSection(s) if depth == 1 => { |
| for export in s { |
| let export = export?; |
| externs.push(( |
| export.name.0.to_string(), |
| Extern::Export(DecodingExport { |
| name: export.name.0.to_string(), |
| kind: export.kind, |
| index: export.index, |
| }), |
| )); |
| } |
| } |
| #[cfg(feature = "serde")] |
| Payload::CustomSection(s) if s.name() == PackageMetadata::SECTION_NAME => { |
| if _package_metadata.is_some() { |
| bail!("multiple {:?} sections", PackageMetadata::SECTION_NAME); |
| } |
| _package_metadata = Some(PackageMetadata::decode(s.data())?); |
| } |
| Payload::ModuleSection { parser, .. } |
| | Payload::ComponentSection { parser, .. } => { |
| stack.push(cur.clone()); |
| cur = parser.clone(); |
| } |
| Payload::End(_) => { |
| if let Some(parent_parser) = stack.pop() { |
| cur = parent_parser.clone(); |
| } else { |
| break; |
| } |
| } |
| _ => {} |
| } |
| |
| // once we're done processing the payload we can forget the |
| // original. |
| buffer.drain(..consumed); |
| } |
| |
| Ok(Self { |
| types: types.unwrap(), |
| externs, |
| package_metadata: _package_metadata, |
| }) |
| } |
| |
| fn is_wit_package(&self) -> Option<WitEncodingVersion> { |
| // all wit package exports must be component types, and there must be at |
| // least one |
| if self.externs.is_empty() { |
| return None; |
| } |
| |
| if !self.externs.iter().all(|(_, item)| { |
| let export = match item { |
| Extern::Export(e) => e, |
| _ => return false, |
| }; |
| match export.kind { |
| ComponentExternalKind::Type => matches!( |
| self.types.as_ref().component_any_type_at(export.index), |
| ComponentAnyTypeId::Component(_) |
| ), |
| _ => false, |
| } |
| }) { |
| return None; |
| } |
| |
| // The distinction between v1 and v2 encoding formats is the structure of the export |
| // strings for each component. The v1 format uses "<namespace>:<package>/wit" as the name |
| // for the top-level exports, while the v2 format uses the unqualified name of the encoded |
| // entity. |
| match ComponentName::new(&self.externs[0].0, 0).ok()?.kind() { |
| ComponentNameKind::Interface(name) if name.interface().as_str() == "wit" => { |
| Some(WitEncodingVersion::V1) |
| } |
| ComponentNameKind::Label(_) => Some(WitEncodingVersion::V2), |
| _ => None, |
| } |
| } |
| |
| fn decode_wit_v1_package(&self) -> Result<(Resolve, PackageId)> { |
| let mut decoder = WitPackageDecoder::new(&self.types); |
| |
| let mut pkg = None; |
| for (name, item) in self.externs.iter() { |
| let export = match item { |
| Extern::Export(e) => e, |
| _ => unreachable!(), |
| }; |
| let id = self.types.as_ref().component_type_at(export.index); |
| let ty = &self.types[id]; |
| if pkg.is_some() { |
| bail!("more than one top-level exported component type found"); |
| } |
| let name = ComponentName::new(name, 0).unwrap(); |
| pkg = Some( |
| decoder |
| .decode_v1_package(&name, ty) |
| .with_context(|| format!("failed to decode document `{name}`"))?, |
| ); |
| } |
| |
| let pkg = pkg.ok_or_else(|| anyhow!("no exported component type found"))?; |
| let (mut resolve, package) = decoder.finish(pkg); |
| if let Some(package_metadata) = &self.package_metadata { |
| package_metadata.inject(&mut resolve, package)?; |
| } |
| Ok((resolve, package)) |
| } |
| |
| fn decode_wit_v2_package(&self) -> Result<(Resolve, PackageId)> { |
| let mut decoder = WitPackageDecoder::new(&self.types); |
| |
| let mut pkg_name = None; |
| |
| let mut interfaces = IndexMap::new(); |
| let mut worlds = IndexMap::new(); |
| let mut fields = PackageFields { |
| interfaces: &mut interfaces, |
| worlds: &mut worlds, |
| }; |
| |
| for (_, item) in self.externs.iter() { |
| let export = match item { |
| Extern::Export(e) => e, |
| _ => unreachable!(), |
| }; |
| |
| let index = export.index; |
| let id = self.types.as_ref().component_type_at(index); |
| let component = &self.types[id]; |
| |
| // The single export of this component will determine if it's a world or an interface: |
| // worlds export a component, while interfaces export an instance. |
| if component.exports.len() != 1 { |
| bail!( |
| "Expected a single export, but found {} instead", |
| component.exports.len() |
| ); |
| } |
| |
| let name = component.exports.keys().nth(0).unwrap(); |
| |
| let name = match component.exports[name] { |
| ComponentEntityType::Component(ty) => { |
| let package_name = |
| decoder.decode_world(name.as_str(), &self.types[ty], &mut fields)?; |
| package_name |
| } |
| ComponentEntityType::Instance(ty) => { |
| let package_name = decoder.decode_interface( |
| name.as_str(), |
| &component.imports, |
| &self.types[ty], |
| &mut fields, |
| )?; |
| package_name |
| } |
| _ => unreachable!(), |
| }; |
| |
| if let Some(pkg_name) = pkg_name.as_ref() { |
| // TODO: when we have fully switched to the v2 format, we should switch to parsing |
| // multiple wit documents instead of bailing. |
| if pkg_name != &name { |
| bail!("item defined with mismatched package name") |
| } |
| } else { |
| pkg_name.replace(name); |
| } |
| } |
| |
| let pkg = if let Some(name) = pkg_name { |
| Package { |
| name, |
| docs: Docs::default(), |
| interfaces, |
| worlds, |
| } |
| } else { |
| bail!("no exported component type found"); |
| }; |
| |
| let (mut resolve, package) = decoder.finish(pkg); |
| if let Some(package_metadata) = &self.package_metadata { |
| package_metadata.inject(&mut resolve, package)?; |
| } |
| Ok((resolve, package)) |
| } |
| |
| fn decode_component(&self) -> Result<(Resolve, WorldId)> { |
| assert!(self.is_wit_package().is_none()); |
| let mut decoder = WitPackageDecoder::new(&self.types); |
| // Note that this name is arbitrarily chosen. We may one day perhaps |
| // want to encode this in the component binary format itself, but for |
| // now it shouldn't be an issue to have a defaulted name here. |
| let world_name = "root"; |
| let world = decoder.resolve.worlds.alloc(World { |
| name: world_name.to_string(), |
| docs: Default::default(), |
| imports: Default::default(), |
| exports: Default::default(), |
| package: None, |
| includes: Default::default(), |
| include_names: Default::default(), |
| stability: Default::default(), |
| }); |
| let mut package = Package { |
| // Similar to `world_name` above this is arbitrarily chosen as it's |
| // not otherwise encoded in a binary component. This theoretically |
| // shouldn't cause issues, however. |
| name: PackageName { |
| namespace: "root".to_string(), |
| version: None, |
| name: "component".to_string(), |
| }, |
| docs: Default::default(), |
| worlds: [(world_name.to_string(), world)].into_iter().collect(), |
| interfaces: Default::default(), |
| }; |
| |
| let mut fields = PackageFields { |
| worlds: &mut package.worlds, |
| interfaces: &mut package.interfaces, |
| }; |
| |
| for (_name, item) in self.externs.iter() { |
| match item { |
| Extern::Import(import) => { |
| decoder.decode_component_import(import, world, &mut fields)? |
| } |
| Extern::Export(export) => { |
| decoder.decode_component_export(export, world, &mut fields)? |
| } |
| } |
| } |
| |
| let (resolve, _) = decoder.finish(package); |
| Ok((resolve, world)) |
| } |
| } |
| |
| /// Result of the [`decode`] function. |
| pub enum DecodedWasm { |
| /// The input to [`decode`] was one or more binary-encoded WIT package(s). |
| /// |
| /// The full resolve graph is here plus the identifier of the packages that |
| /// were encoded. Note that other packages may be within the resolve if any |
| /// of the main packages refer to other, foreign packages. |
| WitPackage(Resolve, PackageId), |
| |
| /// The input to [`decode`] was a component and its interface is specified |
| /// by the world here. |
| Component(Resolve, WorldId), |
| } |
| |
| impl DecodedWasm { |
| /// Returns the [`Resolve`] for WIT types contained. |
| pub fn resolve(&self) -> &Resolve { |
| match self { |
| DecodedWasm::WitPackage(resolve, _) => resolve, |
| DecodedWasm::Component(resolve, _) => resolve, |
| } |
| } |
| |
| /// Returns the main packages of what was decoded. |
| pub fn package(&self) -> PackageId { |
| match self { |
| DecodedWasm::WitPackage(_, id) => *id, |
| DecodedWasm::Component(resolve, world) => resolve.worlds[*world].package.unwrap(), |
| } |
| } |
| } |
| |
| /// Decode for incremental reading |
| pub fn decode_reader(reader: impl Read) -> Result<DecodedWasm> { |
| let info = ComponentInfo::from_reader(reader)?; |
| |
| if let Some(version) = info.is_wit_package() { |
| match version { |
| WitEncodingVersion::V1 => { |
| log::debug!("decoding a v1 WIT package encoded as wasm"); |
| let (resolve, pkg) = info.decode_wit_v1_package()?; |
| Ok(DecodedWasm::WitPackage(resolve, pkg)) |
| } |
| WitEncodingVersion::V2 => { |
| log::debug!("decoding a v2 WIT package encoded as wasm"); |
| let (resolve, pkg) = info.decode_wit_v2_package()?; |
| Ok(DecodedWasm::WitPackage(resolve, pkg)) |
| } |
| } |
| } else { |
| log::debug!("inferring the WIT of a concrete component"); |
| let (resolve, world) = info.decode_component()?; |
| Ok(DecodedWasm::Component(resolve, world)) |
| } |
| } |
| /// Decodes an in-memory WebAssembly binary into a WIT [`Resolve`] and |
| /// associated metadata. |
| /// |
| /// The WebAssembly binary provided here can either be a |
| /// WIT-package-encoded-as-binary or an actual component itself. A [`Resolve`] |
| /// is always created and the return value indicates which was detected. |
| pub fn decode(bytes: &[u8]) -> Result<DecodedWasm> { |
| decode_reader(bytes) |
| } |
| |
| /// Decodes the single component type `world` specified as a WIT world. |
| /// |
| /// The `world` should be an exported component type. The `world` must have been |
| /// previously created via `encode_world` meaning that it is a component that |
| /// itself imports nothing and exports a single component, and the single |
| /// component export represents the world. The name of the export is also the |
| /// name of the package/world/etc. |
| pub fn decode_world(wasm: &[u8]) -> Result<(Resolve, WorldId)> { |
| let mut validator = Validator::new(); |
| let mut exports = Vec::new(); |
| let mut depth = 1; |
| let mut types = None; |
| |
| for payload in Parser::new(0).parse_all(wasm) { |
| let payload = payload?; |
| |
| match validator.payload(&payload)? { |
| ValidPayload::Ok => {} |
| ValidPayload::Parser(_) => depth += 1, |
| ValidPayload::End(t) => { |
| depth -= 1; |
| if depth == 0 { |
| types = Some(t); |
| } |
| } |
| ValidPayload::Func(..) => {} |
| } |
| |
| match payload { |
| Payload::ComponentExportSection(s) if depth == 1 => { |
| for export in s { |
| exports.push(export?); |
| } |
| } |
| _ => {} |
| } |
| } |
| |
| if exports.len() != 1 { |
| bail!("expected one export in component"); |
| } |
| if exports[0].kind != ComponentExternalKind::Type { |
| bail!("expected an export of a type"); |
| } |
| if exports[0].ty.is_some() { |
| bail!("expected an un-ascribed exported type"); |
| } |
| let types = types.as_ref().unwrap(); |
| let world = match types.as_ref().component_any_type_at(exports[0].index) { |
| ComponentAnyTypeId::Component(c) => c, |
| _ => bail!("expected an exported component type"), |
| }; |
| |
| let mut decoder = WitPackageDecoder::new(types); |
| let mut interfaces = IndexMap::new(); |
| let mut worlds = IndexMap::new(); |
| let ty = &types[world]; |
| assert_eq!(ty.imports.len(), 0); |
| assert_eq!(ty.exports.len(), 1); |
| let name = ty.exports.keys().nth(0).unwrap(); |
| let ty = match ty.exports[0] { |
| ComponentEntityType::Component(ty) => ty, |
| _ => unreachable!(), |
| }; |
| let name = decoder.decode_world( |
| name, |
| &types[ty], |
| &mut PackageFields { |
| interfaces: &mut interfaces, |
| worlds: &mut worlds, |
| }, |
| )?; |
| let (resolve, pkg) = decoder.finish(Package { |
| name, |
| interfaces, |
| worlds, |
| docs: Default::default(), |
| }); |
| // The package decoded here should only have a single world so extract that |
| // here to return. |
| let world = *resolve.packages[pkg].worlds.iter().next().unwrap().1; |
| Ok((resolve, world)) |
| } |
| |
| struct PackageFields<'a> { |
| interfaces: &'a mut IndexMap<String, InterfaceId>, |
| worlds: &'a mut IndexMap<String, WorldId>, |
| } |
| |
| struct WitPackageDecoder<'a> { |
| resolve: Resolve, |
| types: &'a Types, |
| foreign_packages: IndexMap<String, Package>, |
| iface_to_package_index: HashMap<InterfaceId, usize>, |
| named_interfaces: HashMap<String, InterfaceId>, |
| |
| /// A map which tracks named resources to what their corresponding `TypeId` |
| /// is. This first layer of key in this map is the owner scope of a |
| /// resource, more-or-less the `world` or `interface` that it's defined |
| /// within. The second layer of this map is keyed by name of the resource |
| /// and points to the actual ID of the resource. |
| /// |
| /// This map is populated in `register_type_export`. |
| resources: HashMap<TypeOwner, HashMap<String, TypeId>>, |
| |
| /// A map from a type id to what it's been translated to. |
| type_map: HashMap<ComponentAnyTypeId, TypeId>, |
| } |
| |
| impl WitPackageDecoder<'_> { |
| fn new<'a>(types: &'a Types) -> WitPackageDecoder<'a> { |
| WitPackageDecoder { |
| resolve: Resolve::default(), |
| types, |
| type_map: HashMap::new(), |
| foreign_packages: Default::default(), |
| iface_to_package_index: Default::default(), |
| named_interfaces: Default::default(), |
| resources: Default::default(), |
| } |
| } |
| |
| fn decode_v1_package(&mut self, name: &ComponentName, ty: &ComponentType) -> Result<Package> { |
| // Process all imports for this package first, where imports are |
| // importing from remote packages. |
| for (name, ty) in ty.imports.iter() { |
| let ty = match ty { |
| ComponentEntityType::Instance(idx) => &self.types[*idx], |
| _ => bail!("import `{name}` is not an instance"), |
| }; |
| self.register_import(name, ty) |
| .with_context(|| format!("failed to process import `{name}`"))?; |
| } |
| |
| let mut package = Package { |
| // The name encoded for packages must be of the form `foo:bar/wit` |
| // where "wit" is just a placeholder for now. The package name in |
| // this case would be `foo:bar`. |
| name: match name.kind() { |
| ComponentNameKind::Interface(name) if name.interface().as_str() == "wit" => { |
| name.to_package_name() |
| } |
| _ => bail!("package name is not a valid id: {name}"), |
| }, |
| docs: Default::default(), |
| interfaces: Default::default(), |
| worlds: Default::default(), |
| }; |
| |
| let mut fields = PackageFields { |
| interfaces: &mut package.interfaces, |
| worlds: &mut package.worlds, |
| }; |
| |
| for (name, ty) in ty.exports.iter() { |
| match ty { |
| ComponentEntityType::Instance(idx) => { |
| let ty = &self.types[*idx]; |
| self.register_interface(name.as_str(), ty, &mut fields) |
| .with_context(|| format!("failed to process export `{name}`"))?; |
| } |
| ComponentEntityType::Component(idx) => { |
| let ty = &self.types[*idx]; |
| self.register_world(name.as_str(), ty, &mut fields) |
| .with_context(|| format!("failed to process export `{name}`"))?; |
| } |
| _ => bail!("component export `{name}` is not an instance or component"), |
| } |
| } |
| Ok(package) |
| } |
| |
| fn decode_interface<'a>( |
| &mut self, |
| name: &str, |
| imports: &wasmparser::collections::IndexMap<String, ComponentEntityType>, |
| ty: &ComponentInstanceType, |
| fields: &mut PackageFields<'a>, |
| ) -> Result<PackageName> { |
| let component_name = self |
| .parse_component_name(name) |
| .context("expected world name to have an ID form")?; |
| |
| let package = match component_name.kind() { |
| ComponentNameKind::Interface(name) => name.to_package_name(), |
| _ => bail!("expected world name to be fully qualified"), |
| }; |
| |
| for (name, ty) in imports.iter() { |
| let ty = match ty { |
| ComponentEntityType::Instance(idx) => &self.types[*idx], |
| _ => bail!("import `{name}` is not an instance"), |
| }; |
| self.register_import(name, ty) |
| .with_context(|| format!("failed to process import `{name}`"))?; |
| } |
| |
| let _ = self.register_interface(name, ty, fields)?; |
| |
| Ok(package) |
| } |
| |
| fn decode_world<'a>( |
| &mut self, |
| name: &str, |
| ty: &ComponentType, |
| fields: &mut PackageFields<'a>, |
| ) -> Result<PackageName> { |
| let kebab_name = self |
| .parse_component_name(name) |
| .context("expected world name to have an ID form")?; |
| |
| let package = match kebab_name.kind() { |
| ComponentNameKind::Interface(name) => name.to_package_name(), |
| _ => bail!("expected world name to be fully qualified"), |
| }; |
| |
| let _ = self.register_world(name, ty, fields)?; |
| |
| Ok(package) |
| } |
| |
| fn decode_component_import<'a>( |
| &mut self, |
| name: &str, |
| world: WorldId, |
| package: &mut PackageFields<'a>, |
| ) -> Result<()> { |
| log::debug!("decoding component import `{name}`"); |
| let ty = self |
| .types |
| .as_ref() |
| .component_entity_type_of_import(name) |
| .unwrap(); |
| let owner = TypeOwner::World(world); |
| let (name, item) = match ty { |
| ComponentEntityType::Instance(i) => { |
| let ty = &self.types[i]; |
| let (name, id) = if name.contains('/') { |
| let id = self.register_import(name, ty)?; |
| (WorldKey::Interface(id), id) |
| } else { |
| self.register_interface(name, ty, package) |
| .with_context(|| format!("failed to decode WIT from import `{name}`"))? |
| }; |
| ( |
| name, |
| WorldItem::Interface { |
| id, |
| stability: Default::default(), |
| }, |
| ) |
| } |
| ComponentEntityType::Func(i) => { |
| let ty = &self.types[i]; |
| let func = self |
| .convert_function(name, ty, owner) |
| .with_context(|| format!("failed to decode function from import `{name}`"))?; |
| (WorldKey::Name(name.to_string()), WorldItem::Function(func)) |
| } |
| ComponentEntityType::Type { |
| referenced, |
| created, |
| } => { |
| let id = self |
| .register_type_export(name, owner, referenced, created) |
| .with_context(|| format!("failed to decode type from export `{name}`"))?; |
| (WorldKey::Name(name.to_string()), WorldItem::Type(id)) |
| } |
| // All other imports do not form part of the component's world |
| _ => return Ok(()), |
| }; |
| self.resolve.worlds[world].imports.insert(name, item); |
| Ok(()) |
| } |
| |
| fn decode_component_export<'a>( |
| &mut self, |
| export: &DecodingExport, |
| world: WorldId, |
| package: &mut PackageFields<'a>, |
| ) -> Result<()> { |
| let name = &export.name; |
| log::debug!("decoding component export `{name}`"); |
| let types = self.types.as_ref(); |
| let ty = types.component_entity_type_of_export(name).unwrap(); |
| let (name, item) = match ty { |
| ComponentEntityType::Func(i) => { |
| let ty = &types[i]; |
| let func = self |
| .convert_function(name, ty, TypeOwner::World(world)) |
| .with_context(|| format!("failed to decode function from export `{name}`"))?; |
| |
| (WorldKey::Name(name.to_string()), WorldItem::Function(func)) |
| } |
| ComponentEntityType::Instance(i) => { |
| let ty = &types[i]; |
| let (name, id) = if name.contains('/') { |
| let id = self.register_import(name, ty)?; |
| (WorldKey::Interface(id), id) |
| } else { |
| self.register_interface(name, ty, package) |
| .with_context(|| format!("failed to decode WIT from export `{name}`"))? |
| }; |
| ( |
| name, |
| WorldItem::Interface { |
| id, |
| stability: Default::default(), |
| }, |
| ) |
| } |
| _ => { |
| bail!("component export `{name}` was not a function or instance") |
| } |
| }; |
| self.resolve.worlds[world].exports.insert(name, item); |
| Ok(()) |
| } |
| |
| /// Registers that the `name` provided is either imported interface from a |
| /// foreign package or referencing a previously defined interface in this |
| /// package. |
| /// |
| /// This function will internally ensure that `name` is well-structured and |
| /// will fill in any information as necessary. For example with a foreign |
| /// dependency the foreign package structure, types, etc, all need to be |
| /// created. For a local dependency it's instead ensured that all the types |
| /// line up with the previous definitions. |
| fn register_import(&mut self, name: &str, ty: &ComponentInstanceType) -> Result<InterfaceId> { |
| let (is_local, interface) = match self.named_interfaces.get(name) { |
| Some(id) => (true, *id), |
| None => (false, self.extract_dep_interface(name)?), |
| }; |
| let owner = TypeOwner::Interface(interface); |
| for (name, ty) in ty.exports.iter() { |
| log::debug!("decoding import instance export `{name}`"); |
| match *ty { |
| ComponentEntityType::Type { |
| referenced, |
| created, |
| } => { |
| match self.resolve.interfaces[interface] |
| .types |
| .get(name.as_str()) |
| .copied() |
| { |
| // If this name is already defined as a type in the |
| // specified interface then that's ok. For package-local |
| // interfaces that's expected since the interface was |
| // fully defined. For remote interfaces it means we're |
| // using something that was already used elsewhere. In |
| // both cases continue along. |
| // |
| // Notably for the remotely defined case this will also |
| // walk over the structure of the type and register |
| // internal wasmparser ids with wit-parser ids. This is |
| // necessary to ensure that anonymous types like |
| // `list<u8>` defined in original definitions are |
| // unified with anonymous types when duplicated inside |
| // of worlds. Overall this prevents, for example, extra |
| // `list<u8>` types from popping up when decoding. This |
| // is not strictly necessary but assists with |
| // roundtripping assertions during fuzzing. |
| Some(id) => { |
| log::debug!("type already exist"); |
| match referenced { |
| ComponentAnyTypeId::Defined(ty) => { |
| self.register_defined(id, &self.types[ty])?; |
| } |
| ComponentAnyTypeId::Resource(_) => {} |
| _ => unreachable!(), |
| } |
| let prev = self.type_map.insert(created, id); |
| assert!(prev.is_none()); |
| } |
| |
| // If the name is not defined, however, then there's two |
| // possibilities: |
| // |
| // * For package-local interfaces this is an error |
| // because the package-local interface defined |
| // everything already and this is referencing |
| // something that isn't defined. |
| // |
| // * For remote interfaces they're never fully declared |
| // so it's lazily filled in here. This means that the |
| // view of remote interfaces ends up being the minimal |
| // slice needed for this resolve, which is what's |
| // intended. |
| None => { |
| if is_local { |
| bail!("instance type export `{name}` not defined in interface"); |
| } |
| let id = self.register_type_export( |
| name.as_str(), |
| owner, |
| referenced, |
| created, |
| )?; |
| let prev = self.resolve.interfaces[interface] |
| .types |
| .insert(name.to_string(), id); |
| assert!(prev.is_none()); |
| } |
| } |
| } |
| |
| // This has similar logic to types above where we lazily fill in |
| // functions for remote dependencies and otherwise assert |
| // they're already defined for local dependencies. |
| ComponentEntityType::Func(ty) => { |
| let def = &self.types[ty]; |
| if self.resolve.interfaces[interface] |
| .functions |
| .contains_key(name.as_str()) |
| { |
| // TODO: should ideally verify that function signatures |
| // match. |
| continue; |
| } |
| if is_local { |
| bail!("instance function export `{name}` not defined in interface"); |
| } |
| let func = self.convert_function(name.as_str(), def, owner)?; |
| let prev = self.resolve.interfaces[interface] |
| .functions |
| .insert(name.to_string(), func); |
| assert!(prev.is_none()); |
| } |
| |
| _ => bail!("instance type export `{name}` is not a type"), |
| } |
| } |
| |
| Ok(interface) |
| } |
| |
| fn find_alias(&self, id: ComponentAnyTypeId) -> Option<TypeId> { |
| // Consult `type_map` for `referenced` or anything in its |
| // chain of aliases to determine what it maps to. This may |
| // bottom out in `None` in the case that this type is |
| // just now being defined, but this should otherwise follow |
| // chains of aliases to determine what exactly this was a |
| // `use` of if it exists. |
| let mut prev = None; |
| let mut cur = id; |
| while prev.is_none() { |
| prev = self.type_map.get(&cur).copied(); |
| cur = match self.types.as_ref().peel_alias(cur) { |
| Some(next) => next, |
| None => break, |
| }; |
| } |
| prev |
| } |
| |
| /// This will parse the `name_string` as a component model ID string and |
| /// ensure that there's an `InterfaceId` corresponding to its components. |
| fn extract_dep_interface(&mut self, name_string: &str) -> Result<InterfaceId> { |
| let name = ComponentName::new(name_string, 0).unwrap(); |
| let name = match name.kind() { |
| ComponentNameKind::Interface(name) => name, |
| _ => bail!("package name is not a valid id: {name_string}"), |
| }; |
| let package_name = name.to_package_name(); |
| // Lazily create a `Package` as necessary, along with the interface. |
| let package = self |
| .foreign_packages |
| .entry(package_name.to_string()) |
| .or_insert_with(|| Package { |
| name: package_name.clone(), |
| docs: Default::default(), |
| interfaces: Default::default(), |
| worlds: Default::default(), |
| }); |
| let interface = *package |
| .interfaces |
| .entry(name.interface().to_string()) |
| .or_insert_with(|| { |
| self.resolve.interfaces.alloc(Interface { |
| name: Some(name.interface().to_string()), |
| docs: Default::default(), |
| types: IndexMap::default(), |
| functions: IndexMap::new(), |
| package: None, |
| stability: Default::default(), |
| }) |
| }); |
| |
| // Record a mapping of which foreign package this interface belongs to |
| self.iface_to_package_index.insert( |
| interface, |
| self.foreign_packages |
| .get_full(&package_name.to_string()) |
| .unwrap() |
| .0, |
| ); |
| Ok(interface) |
| } |
| |
| /// A general-purpose helper function to translate a component instance |
| /// into a WIT interface. |
| /// |
| /// This is one of the main workhorses of this module. This handles |
| /// interfaces both at the type level, for concrete components, and |
| /// internally within worlds as well. |
| /// |
| /// The `name` provided is the contextual ID or name of the interface. This |
| /// could be a kebab-name in the case of a world import or export or it can |
| /// also be an ID. This is used to guide insertion into various maps. |
| /// |
| /// The `ty` provided is the actual component type being decoded. |
| /// |
| /// The `package` is where to insert the final interface if `name` is an ID |
| /// meaning it's registered as a named standalone item within the package. |
| fn register_interface<'a>( |
| &mut self, |
| name: &str, |
| ty: &ComponentInstanceType, |
| package: &mut PackageFields<'a>, |
| ) -> Result<(WorldKey, InterfaceId)> { |
| // If this interface's name is already known then that means this is an |
| // interface that's both imported and exported. Use `register_import` |
| // to draw connections between types and this interface's types. |
| if self.named_interfaces.contains_key(name) { |
| let id = self.register_import(name, ty)?; |
| return Ok((WorldKey::Interface(id), id)); |
| } |
| |
| // If this is a bare kebab-name for an interface then the interface's |
| // listed name is `None` and the name goes out through the key. |
| // Otherwise this name is extracted from `name` interpreted as an ID. |
| let interface_name = self.extract_interface_name_from_component_name(name)?; |
| |
| let mut interface = Interface { |
| name: interface_name.clone(), |
| docs: Default::default(), |
| types: IndexMap::default(), |
| functions: IndexMap::new(), |
| package: None, |
| stability: Default::default(), |
| }; |
| |
| let owner = TypeOwner::Interface(self.resolve.interfaces.next_id()); |
| for (name, ty) in ty.exports.iter() { |
| match *ty { |
| ComponentEntityType::Type { |
| referenced, |
| created, |
| } => { |
| let ty = self |
| .register_type_export(name.as_str(), owner, referenced, created) |
| .with_context(|| format!("failed to register type export '{name}'"))?; |
| let prev = interface.types.insert(name.to_string(), ty); |
| assert!(prev.is_none()); |
| } |
| |
| ComponentEntityType::Func(ty) => { |
| let ty = &self.types[ty]; |
| let func = self |
| .convert_function(name.as_str(), ty, owner) |
| .with_context(|| format!("failed to convert function '{name}'"))?; |
| let prev = interface.functions.insert(name.to_string(), func); |
| assert!(prev.is_none()); |
| } |
| _ => bail!("instance type export `{name}` is not a type or function"), |
| }; |
| } |
| let id = self.resolve.interfaces.alloc(interface); |
| let key = match interface_name { |
| // If this interface is named then it's part of the package, so |
| // insert it. Additionally register it in `named_interfaces` so |
| // further use comes back to this original definition. |
| Some(interface_name) => { |
| let prev = package.interfaces.insert(interface_name, id); |
| assert!(prev.is_none(), "duplicate interface added for {name:?}"); |
| let prev = self.named_interfaces.insert(name.to_string(), id); |
| assert!(prev.is_none()); |
| WorldKey::Interface(id) |
| } |
| |
| // If this interface isn't named then its key is always a |
| // kebab-name. |
| None => WorldKey::Name(name.to_string()), |
| }; |
| Ok((key, id)) |
| } |
| |
| fn parse_component_name(&self, name: &str) -> Result<ComponentName> { |
| ComponentName::new(name, 0) |
| .with_context(|| format!("cannot extract item name from: {name}")) |
| } |
| |
| fn extract_interface_name_from_component_name(&self, name: &str) -> Result<Option<String>> { |
| let component_name = self.parse_component_name(name)?; |
| match component_name.kind() { |
| ComponentNameKind::Interface(name) => Ok(Some(name.interface().to_string())), |
| ComponentNameKind::Label(_name) => Ok(None), |
| _ => bail!("cannot extract item name from: {name}"), |
| } |
| } |
| |
| fn register_type_export( |
| &mut self, |
| name: &str, |
| owner: TypeOwner, |
| referenced: ComponentAnyTypeId, |
| created: ComponentAnyTypeId, |
| ) -> Result<TypeId> { |
| let kind = match self.find_alias(referenced) { |
| // If this `TypeId` points to a type which has |
| // previously been defined, meaning we're aliasing a |
| // prior definition. |
| Some(prev) => { |
| log::debug!("type export for `{name}` is an alias"); |
| TypeDefKind::Type(Type::Id(prev)) |
| } |
| |
| // ... or this `TypeId`'s source definition has never |
| // been seen before, so declare the full type. |
| None => { |
| log::debug!("type export for `{name}` is a new type"); |
| match referenced { |
| ComponentAnyTypeId::Defined(ty) => self |
| .convert_defined(&self.types[ty]) |
| .context("failed to convert unaliased type")?, |
| ComponentAnyTypeId::Resource(_) => TypeDefKind::Resource, |
| _ => unreachable!(), |
| } |
| } |
| }; |
| let ty = self.resolve.types.alloc(TypeDef { |
| name: Some(name.to_string()), |
| kind, |
| docs: Default::default(), |
| stability: Default::default(), |
| owner, |
| }); |
| |
| // If this is a resource then doubly-register it in `self.resources` so |
| // the ID allocated here can be looked up via name later on during |
| // `convert_function`. |
| if let TypeDefKind::Resource = self.resolve.types[ty].kind { |
| let prev = self |
| .resources |
| .entry(owner) |
| .or_insert(HashMap::new()) |
| .insert(name.to_string(), ty); |
| assert!(prev.is_none()); |
| } |
| |
| let prev = self.type_map.insert(created, ty); |
| assert!(prev.is_none()); |
| Ok(ty) |
| } |
| |
| fn register_world<'a>( |
| &mut self, |
| name: &str, |
| ty: &ComponentType, |
| package: &mut PackageFields<'a>, |
| ) -> Result<WorldId> { |
| let name = self |
| .extract_interface_name_from_component_name(name)? |
| .context("expected world name to have an ID form")?; |
| let mut world = World { |
| name: name.clone(), |
| docs: Default::default(), |
| imports: Default::default(), |
| exports: Default::default(), |
| includes: Default::default(), |
| include_names: Default::default(), |
| package: None, |
| stability: Default::default(), |
| }; |
| |
| let owner = TypeOwner::World(self.resolve.worlds.next_id()); |
| for (name, ty) in ty.imports.iter() { |
| let (name, item) = match ty { |
| ComponentEntityType::Instance(idx) => { |
| let ty = &self.types[*idx]; |
| let (name, id) = if name.contains('/') { |
| // If a name is an interface import then it is either to |
| // a package-local or foreign interface, and both |
| // situations are handled in `register_import`. |
| let id = self.register_import(name, ty)?; |
| (WorldKey::Interface(id), id) |
| } else { |
| // A plain kebab-name indicates an inline interface that |
| // wasn't declared explicitly elsewhere with a name, and |
| // `register_interface` will create a new `Interface` |
| // with no name. |
| self.register_interface(name, ty, package)? |
| }; |
| ( |
| name, |
| WorldItem::Interface { |
| id, |
| stability: Default::default(), |
| }, |
| ) |
| } |
| ComponentEntityType::Type { |
| created, |
| referenced, |
| } => { |
| let ty = |
| self.register_type_export(name.as_str(), owner, *referenced, *created)?; |
| (WorldKey::Name(name.to_string()), WorldItem::Type(ty)) |
| } |
| ComponentEntityType::Func(idx) => { |
| let ty = &self.types[*idx]; |
| let func = self.convert_function(name.as_str(), ty, owner)?; |
| (WorldKey::Name(name.to_string()), WorldItem::Function(func)) |
| } |
| _ => bail!("component import `{name}` is not an instance, func, or type"), |
| }; |
| world.imports.insert(name, item); |
| } |
| |
| for (name, ty) in ty.exports.iter() { |
| let (name, item) = match ty { |
| ComponentEntityType::Instance(idx) => { |
| let ty = &self.types[*idx]; |
| let (name, id) = if name.contains('/') { |
| // Note that despite this being an export this is |
| // calling `register_import`. With a URL this interface |
| // must have been previously defined so this will |
| // trigger the logic of either filling in a remotely |
| // defined interface or connecting items to local |
| // definitions of our own interface. |
| let id = self.register_import(name, ty)?; |
| (WorldKey::Interface(id), id) |
| } else { |
| self.register_interface(name, ty, package)? |
| }; |
| ( |
| name, |
| WorldItem::Interface { |
| id, |
| stability: Default::default(), |
| }, |
| ) |
| } |
| |
| ComponentEntityType::Func(idx) => { |
| let ty = &self.types[*idx]; |
| let func = self.convert_function(name.as_str(), ty, owner)?; |
| (WorldKey::Name(name.to_string()), WorldItem::Function(func)) |
| } |
| |
| _ => bail!("component export `{name}` is not an instance or function"), |
| }; |
| world.exports.insert(name, item); |
| } |
| let id = self.resolve.worlds.alloc(world); |
| let prev = package.worlds.insert(name, id); |
| assert!(prev.is_none()); |
| Ok(id) |
| } |
| |
| fn convert_function( |
| &mut self, |
| name: &str, |
| ty: &ComponentFuncType, |
| owner: TypeOwner, |
| ) -> Result<Function> { |
| let name = ComponentName::new(name, 0).unwrap(); |
| let params = ty |
| .params |
| .iter() |
| .map(|(name, ty)| Ok((name.to_string(), self.convert_valtype(ty)?))) |
| .collect::<Result<Vec<_>>>() |
| .context("failed to convert params")?; |
| let results = if ty.results.len() == 1 && ty.results[0].0.is_none() { |
| Results::Anon( |
| self.convert_valtype(&ty.results[0].1) |
| .context("failed to convert anonymous result type")?, |
| ) |
| } else { |
| Results::Named( |
| ty.results |
| .iter() |
| .map(|(name, ty)| { |
| Ok(( |
| name.as_ref().unwrap().to_string(), |
| self.convert_valtype(ty)?, |
| )) |
| }) |
| .collect::<Result<Vec<_>>>() |
| .context("failed to convert named result types")?, |
| ) |
| }; |
| Ok(Function { |
| docs: Default::default(), |
| stability: Default::default(), |
| kind: match name.kind() { |
| ComponentNameKind::Label(_) => FunctionKind::Freestanding, |
| ComponentNameKind::Constructor(resource) => { |
| FunctionKind::Constructor(self.resources[&owner][resource.as_str()]) |
| } |
| ComponentNameKind::Method(name) => { |
| FunctionKind::Method(self.resources[&owner][name.resource().as_str()]) |
| } |
| ComponentNameKind::Static(name) => { |
| FunctionKind::Static(self.resources[&owner][name.resource().as_str()]) |
| } |
| |
| // Functions shouldn't have ID-based names at this time. |
| ComponentNameKind::Interface(_) |
| | ComponentNameKind::Url(_) |
| | ComponentNameKind::Hash(_) |
| | ComponentNameKind::Dependency(_) => unreachable!(), |
| }, |
| |
| // Note that this name includes "name mangling" such as |
| // `[method]foo.bar` which is intentional. The `FunctionKind` |
| // discriminant calculated above indicates how to interpret this |
| // name. |
| name: name.to_string(), |
| params, |
| results, |
| }) |
| } |
| |
| fn convert_valtype(&mut self, ty: &ComponentValType) -> Result<Type> { |
| let id = match ty { |
| ComponentValType::Primitive(ty) => return Ok(self.convert_primitive(*ty)), |
| ComponentValType::Type(id) => *id, |
| }; |
| |
| // Don't create duplicate types for anything previously created. |
| if let Some(ret) = self.type_map.get(&id.into()) { |
| return Ok(Type::Id(*ret)); |
| } |
| |
| // Otherwise create a new `TypeDef` without a name since this is an |
| // anonymous valtype. Note that this is invalid for some types so return |
| // errors on those types, but eventually the `bail!` here is |
| // more-or-less unreachable due to expected validation to be added to |
| // the component model binary format itself. |
| let def = &self.types[id]; |
| let kind = self.convert_defined(def)?; |
| match &kind { |
| TypeDefKind::Type(_) |
| | TypeDefKind::List(_) |
| | TypeDefKind::Tuple(_) |
| | TypeDefKind::Option(_) |
| | TypeDefKind::Result(_) |
| | TypeDefKind::Handle(_) => {} |
| |
| TypeDefKind::Resource |
| | TypeDefKind::Record(_) |
| | TypeDefKind::Enum(_) |
| | TypeDefKind::Variant(_) |
| | TypeDefKind::Flags(_) |
| | TypeDefKind::Future(_) |
| | TypeDefKind::Stream(_) => { |
| bail!("unexpected unnamed type of kind '{}'", kind.as_str()); |
| } |
| TypeDefKind::Unknown => unreachable!(), |
| } |
| let ty = self.resolve.types.alloc(TypeDef { |
| name: None, |
| docs: Default::default(), |
| stability: Default::default(), |
| owner: TypeOwner::None, |
| kind, |
| }); |
| let prev = self.type_map.insert(id.into(), ty); |
| assert!(prev.is_none()); |
| Ok(Type::Id(ty)) |
| } |
| |
| /// Converts a wasmparser `ComponentDefinedType`, the definition of a type |
| /// in the component model, to a WIT `TypeDefKind` to get inserted into the |
| /// types arena by the caller. |
| fn convert_defined(&mut self, ty: &ComponentDefinedType) -> Result<TypeDefKind> { |
| match ty { |
| ComponentDefinedType::Primitive(t) => Ok(TypeDefKind::Type(self.convert_primitive(*t))), |
| |
| ComponentDefinedType::List(t) => { |
| let t = self.convert_valtype(t)?; |
| Ok(TypeDefKind::List(t)) |
| } |
| |
| ComponentDefinedType::Tuple(t) => { |
| let types = t |
| .types |
| .iter() |
| .map(|t| self.convert_valtype(t)) |
| .collect::<Result<_>>()?; |
| Ok(TypeDefKind::Tuple(Tuple { types })) |
| } |
| |
| ComponentDefinedType::Option(t) => { |
| let t = self.convert_valtype(t)?; |
| Ok(TypeDefKind::Option(t)) |
| } |
| |
| ComponentDefinedType::Result { ok, err } => { |
| let ok = match ok { |
| Some(t) => Some(self.convert_valtype(t)?), |
| None => None, |
| }; |
| let err = match err { |
| Some(t) => Some(self.convert_valtype(t)?), |
| None => None, |
| }; |
| Ok(TypeDefKind::Result(Result_ { ok, err })) |
| } |
| |
| ComponentDefinedType::Record(r) => { |
| let fields = r |
| .fields |
| .iter() |
| .map(|(name, ty)| { |
| Ok(Field { |
| name: name.to_string(), |
| ty: self.convert_valtype(ty).with_context(|| { |
| format!("failed to convert record field '{name}'") |
| })?, |
| docs: Default::default(), |
| }) |
| }) |
| .collect::<Result<_>>()?; |
| Ok(TypeDefKind::Record(Record { fields })) |
| } |
| |
| ComponentDefinedType::Variant(v) => { |
| let cases = v |
| .cases |
| .iter() |
| .map(|(name, case)| { |
| if case.refines.is_some() { |
| bail!("unimplemented support for `refines`"); |
| } |
| Ok(Case { |
| name: name.to_string(), |
| ty: match &case.ty { |
| Some(ty) => Some(self.convert_valtype(ty)?), |
| None => None, |
| }, |
| docs: Default::default(), |
| }) |
| }) |
| .collect::<Result<_>>()?; |
| Ok(TypeDefKind::Variant(Variant { cases })) |
| } |
| |
| ComponentDefinedType::Flags(f) => { |
| let flags = f |
| .iter() |
| .map(|name| Flag { |
| name: name.to_string(), |
| docs: Default::default(), |
| }) |
| .collect(); |
| Ok(TypeDefKind::Flags(Flags { flags })) |
| } |
| |
| ComponentDefinedType::Enum(e) => { |
| let cases = e |
| .iter() |
| .cloned() |
| .map(|name| EnumCase { |
| name: name.into(), |
| docs: Default::default(), |
| }) |
| .collect(); |
| Ok(TypeDefKind::Enum(Enum { cases })) |
| } |
| |
| ComponentDefinedType::Own(id) => { |
| let id = self.type_map[&(*id).into()]; |
| Ok(TypeDefKind::Handle(Handle::Own(id))) |
| } |
| |
| ComponentDefinedType::Borrow(id) => { |
| let id = self.type_map[&(*id).into()]; |
| Ok(TypeDefKind::Handle(Handle::Borrow(id))) |
| } |
| } |
| } |
| |
| fn convert_primitive(&self, ty: PrimitiveValType) -> Type { |
| match ty { |
| PrimitiveValType::U8 => Type::U8, |
| PrimitiveValType::S8 => Type::S8, |
| PrimitiveValType::U16 => Type::U16, |
| PrimitiveValType::S16 => Type::S16, |
| PrimitiveValType::U32 => Type::U32, |
| PrimitiveValType::S32 => Type::S32, |
| PrimitiveValType::U64 => Type::U64, |
| PrimitiveValType::S64 => Type::S64, |
| PrimitiveValType::Bool => Type::Bool, |
| PrimitiveValType::Char => Type::Char, |
| PrimitiveValType::String => Type::String, |
| PrimitiveValType::F32 => Type::F32, |
| PrimitiveValType::F64 => Type::F64, |
| } |
| } |
| |
| fn register_defined(&mut self, id: TypeId, def: &ComponentDefinedType) -> Result<()> { |
| Registrar { |
| types: &self.types, |
| type_map: &mut self.type_map, |
| resolve: &self.resolve, |
| } |
| .defined(id, def) |
| } |
| |
| /// Completes the decoding of this resolve by finalizing all packages into |
| /// their topological ordering within the returned `Resolve`. |
| /// |
| /// Takes the root package as an argument to insert. |
| fn finish(mut self, package: Package) -> (Resolve, PackageId) { |
| // Build a topological ordering is then calculated by visiting all the |
| // transitive dependencies of packages. |
| let mut order = IndexSet::new(); |
| for i in 0..self.foreign_packages.len() { |
| self.visit_package(i, &mut order); |
| } |
| |
| // Using the topological ordering create a temporary map from |
| // index-in-`foreign_packages` to index-in-`order` |
| let mut idx_to_pos = vec![0; self.foreign_packages.len()]; |
| for (pos, idx) in order.iter().enumerate() { |
| idx_to_pos[*idx] = pos; |
| } |
| // .. and then using `idx_to_pos` sort the `foreign_packages` array based |
| // on the position it's at in the topological ordering |
| let mut deps = mem::take(&mut self.foreign_packages) |
| .into_iter() |
| .enumerate() |
| .collect::<Vec<_>>(); |
| deps.sort_by_key(|(idx, _)| idx_to_pos[*idx]); |
| |
| // .. and finally insert the packages, in their final topological |
| // ordering, into the returned array. |
| for (_idx, (_url, pkg)) in deps { |
| self.insert_package(pkg); |
| } |
| |
| let id = self.insert_package(package); |
| assert!(self.resolve.worlds.iter().all(|(_, w)| w.package.is_some())); |
| assert!(self |
| .resolve |
| .interfaces |
| .iter() |
| .all(|(_, i)| i.package.is_some())); |
| (self.resolve, id) |
| } |
| |
| fn insert_package(&mut self, package: Package) -> PackageId { |
| let Package { |
| name, |
| interfaces, |
| worlds, |
| docs, |
| } = package; |
| |
| // Most of the time the `package` being inserted is not already present |
| // in `self.resolve`, but in the case of the top-level `decode_world` |
| // function this isn't the case. This shouldn't in general be a problem |
| // so union-up the packages here while asserting that nothing gets |
| // replaced by accident which would indicate a bug. |
| let pkg = self |
| .resolve |
| .package_names |
| .get(&name) |
| .copied() |
| .unwrap_or_else(|| { |
| let id = self.resolve.packages.alloc(Package { |
| name: name.clone(), |
| interfaces: Default::default(), |
| worlds: Default::default(), |
| docs, |
| }); |
| let prev = self.resolve.package_names.insert(name, id); |
| assert!(prev.is_none()); |
| id |
| }); |
| |
| for (name, id) in interfaces { |
| let prev = self.resolve.packages[pkg].interfaces.insert(name, id); |
| assert!(prev.is_none()); |
| self.resolve.interfaces[id].package = Some(pkg); |
| } |
| |
| for (name, id) in worlds { |
| let prev = self.resolve.packages[pkg].worlds.insert(name, id); |
| assert!(prev.is_none()); |
| let world = &mut self.resolve.worlds[id]; |
| world.package = Some(pkg); |
| for (name, item) in world.imports.iter().chain(world.exports.iter()) { |
| if let WorldKey::Name(_) = name { |
| if let WorldItem::Interface { id, .. } = item { |
| self.resolve.interfaces[*id].package = Some(pkg); |
| } |
| } |
| } |
| } |
| |
| pkg |
| } |
| |
| fn visit_package(&self, idx: usize, order: &mut IndexSet<usize>) { |
| if order.contains(&idx) { |
| return; |
| } |
| |
| let (_name, pkg) = self.foreign_packages.get_index(idx).unwrap(); |
| let interfaces = pkg.interfaces.values().copied().chain( |
| pkg.worlds |
| .values() |
| .flat_map(|w| { |
| let world = &self.resolve.worlds[*w]; |
| world.imports.values().chain(world.exports.values()) |
| }) |
| .filter_map(|item| match item { |
| WorldItem::Interface { id, .. } => Some(*id), |
| WorldItem::Function(_) | WorldItem::Type(_) => None, |
| }), |
| ); |
| for iface in interfaces { |
| for dep in self.resolve.interface_direct_deps(iface) { |
| let dep_idx = self.iface_to_package_index[&dep]; |
| if dep_idx != idx { |
| self.visit_package(dep_idx, order); |
| } |
| } |
| } |
| |
| assert!(order.insert(idx)); |
| } |
| } |
| |
| /// Helper type to register the structure of a wasm-defined type against a |
| /// wit-defined type. |
| struct Registrar<'a> { |
| types: &'a Types, |
| type_map: &'a mut HashMap<ComponentAnyTypeId, TypeId>, |
| resolve: &'a Resolve, |
| } |
| |
| impl Registrar<'_> { |
| /// Verifies that the wasm structure of `def` matches the wit structure of |
| /// `id` and recursively registers types. |
| fn defined(&mut self, id: TypeId, def: &ComponentDefinedType) -> Result<()> { |
| match def { |
| ComponentDefinedType::Primitive(_) => Ok(()), |
| |
| ComponentDefinedType::List(t) => { |
| let ty = match &self.resolve.types[id].kind { |
| TypeDefKind::List(r) => r, |
| // Note that all cases below have this match and the general |
| // idea is that once a type is named or otherwise identified |
| // here there's no need to recurse. The purpose of this |
| // registrar is to build connections for anonymous types |
| // that don't otherwise have a name to ensure that they're |
| // decoded to reuse the same constructs consistently. For |
| // that reason once something is named we can bail out. |
| TypeDefKind::Type(Type::Id(_)) => return Ok(()), |
| _ => bail!("expected a list"), |
| }; |
| self.valtype(t, ty) |
| } |
| |
| ComponentDefinedType::Tuple(t) => { |
| let ty = match &self.resolve.types[id].kind { |
| TypeDefKind::Tuple(r) => r, |
| TypeDefKind::Type(Type::Id(_)) => return Ok(()), |
| _ => bail!("expected a tuple"), |
| }; |
| if ty.types.len() != t.types.len() { |
| bail!("mismatched number of tuple fields"); |
| } |
| for (a, b) in t.types.iter().zip(ty.types.iter()) { |
| self.valtype(a, b)?; |
| } |
| Ok(()) |
| } |
| |
| ComponentDefinedType::Option(t) => { |
| let ty = match &self.resolve.types[id].kind { |
| TypeDefKind::Option(r) => r, |
| TypeDefKind::Type(Type::Id(_)) => return Ok(()), |
| _ => bail!("expected an option"), |
| }; |
| self.valtype(t, ty) |
| } |
| |
| ComponentDefinedType::Result { ok, err } => { |
| let ty = match &self.resolve.types[id].kind { |
| TypeDefKind::Result(r) => r, |
| TypeDefKind::Type(Type::Id(_)) => return Ok(()), |
| _ => bail!("expected a result"), |
| }; |
| match (ok, &ty.ok) { |
| (Some(a), Some(b)) => self.valtype(a, b)?, |
| (None, None) => {} |
| _ => bail!("disagreement on result structure"), |
| } |
| match (err, &ty.err) { |
| (Some(a), Some(b)) => self.valtype(a, b)?, |
| (None, None) => {} |
| _ => bail!("disagreement on result structure"), |
| } |
| Ok(()) |
| } |
| |
| ComponentDefinedType::Record(def) => { |
| let ty = match &self.resolve.types[id].kind { |
| TypeDefKind::Record(r) => r, |
| TypeDefKind::Type(Type::Id(_)) => return Ok(()), |
| _ => bail!("expected a record"), |
| }; |
| if def.fields.len() != ty.fields.len() { |
| bail!("mismatched number of record fields"); |
| } |
| for ((name, ty), field) in def.fields.iter().zip(&ty.fields) { |
| if name.as_str() != field.name { |
| bail!("mismatched field order"); |
| } |
| self.valtype(ty, &field.ty)?; |
| } |
| Ok(()) |
| } |
| |
| ComponentDefinedType::Variant(def) => { |
| let ty = match &self.resolve.types[id].kind { |
| TypeDefKind::Variant(r) => r, |
| TypeDefKind::Type(Type::Id(_)) => return Ok(()), |
| _ => bail!("expected a variant"), |
| }; |
| if def.cases.len() != ty.cases.len() { |
| bail!("mismatched number of variant cases"); |
| } |
| for ((name, ty), case) in def.cases.iter().zip(&ty.cases) { |
| if name.as_str() != case.name { |
| bail!("mismatched case order"); |
| } |
| match (&ty.ty, &case.ty) { |
| (Some(a), Some(b)) => self.valtype(a, b)?, |
| (None, None) => {} |
| _ => bail!("disagreement on case type"), |
| } |
| } |
| Ok(()) |
| } |
| |
| // These have no recursive structure so they can bail out. |
| ComponentDefinedType::Flags(_) |
| | ComponentDefinedType::Enum(_) |
| | ComponentDefinedType::Own(_) |
| | ComponentDefinedType::Borrow(_) => Ok(()), |
| } |
| } |
| |
| fn valtype(&mut self, wasm: &ComponentValType, wit: &Type) -> Result<()> { |
| let wasm = match wasm { |
| ComponentValType::Type(wasm) => *wasm, |
| ComponentValType::Primitive(_wasm) => { |
| assert!(!matches!(wit, Type::Id(_))); |
| return Ok(()); |
| } |
| }; |
| let wit = match wit { |
| Type::Id(id) => *id, |
| _ => bail!("expected id-based type"), |
| }; |
| let prev = match self.type_map.insert(wasm.into(), wit) { |
| Some(prev) => prev, |
| None => { |
| let wasm = &self.types[wasm]; |
| return self.defined(wit, wasm); |
| } |
| }; |
| // If `wit` matches `prev` then we've just rediscovered what we already |
| // knew which is that the `wasm` id maps to the `wit` id. |
| // |
| // If, however, `wit` is not equal to `prev` then that's more |
| // interesting. Consider a component such as: |
| // |
| // ```wasm |
| // (component |
| // (import (interface "a:b/name") (instance |
| // (type $l (list string)) |
| // (type $foo (variant (case "l" $l))) |
| // (export "foo" (type (eq $foo))) |
| // )) |
| // (component $c |
| // (type $l (list string)) |
| // (type $bar (variant (case "n" u16) (case "l" $l))) |
| // (export "bar" (type $bar)) |
| // (type $foo (variant (case "l" $l))) |
| // (export "foo" (type $foo)) |
| // ) |
| // (instance $i (instantiate $c)) |
| // (export (interface "a:b/name") (instance $i)) |
| // ) |
| // ``` |
| // |
| // This roughly corresponds to: |
| // |
| // ```wit |
| // package a:b |
| // |
| // interface name { |
| // variant bar { |
| // n(u16), |
| // l(list<string>), |
| // } |
| // |
| // variant foo { |
| // l(list<string>), |
| // } |
| // } |
| // |
| // world module { |
| // import name |
| // export name |
| // } |
| // ``` |
| // |
| // In this situation first we'll see the `import` which records type |
| // information for the `foo` type in `interface name`. Later on the full |
| // picture of `interface name` becomes apparent with the export of a |
| // component which has full type information. When walking over this |
| // first `bar` is seen and its recursive structure. |
| // |
| // The problem arises when walking over the `foo` type. In this |
| // situation the code path we're currently on will be hit because |
| // there's a preexisting definition of `foo` from the import and it's |
| // now going to be unified with what we've seen in the export. When |
| // visiting the `list<string>` case of the `foo` variant this ends up |
| // being different than the `list<string>` used by the `bar` variant. The |
| // reason for this is that when visiting `bar` the wasm-defined `(list |
| // string)` hasn't been seen before so a new type is allocated. Later |
| // though this same wasm type is unified with the first `(list string)` |
| // type in the `import`. |
| // |
| // All-in-all this ends up meaning that it's possible for `prev` to not |
| // match `wit`. In this situation it means the decoded WIT interface |
| // will have duplicate definitions of `list<string>`. This is, |
| // theoretically, not that big of a problem because the same underlying |
| // definition is still there and the meaning of the type is the same. |
| // This can, however, perhaps be a problem for consumers where it's |
| // assumed that all `list<string>` are equal and there's only one. For |
| // example a bindings generator for C may assume that `list<string>` |
| // will only appear once and generate a single name for it, but with two |
| // different types in play here it may generate two types of the same |
| // name (or something like that). |
| // |
| // For now though this is left for a future refactoring. Fixing this |
| // issue would require tracking anonymous types during type translation |
| // so the decoding process for the `bar` export would reuse the |
| // `list<string>` type created from decoding the `foo` import. That's |
| // somewhat nontrivial at this time, so it's left for a future |
| // refactoring. |
| let _ = prev; |
| Ok(()) |
| } |
| } |
| |
| pub(crate) trait InterfaceNameExt { |
| fn to_package_name(&self) -> PackageName; |
| } |
| |
| impl InterfaceNameExt for wasmparser::names::InterfaceName<'_> { |
| fn to_package_name(&self) -> PackageName { |
| PackageName { |
| namespace: self.namespace().to_string(), |
| name: self.package().to_string(), |
| version: self.version(), |
| } |
| } |
| } |