| 1 | //! Support for encoding a core wasm module into a component. |
| 2 | //! |
| 3 | //! This module, at a high level, is tasked with transforming a core wasm |
| 4 | //! module into a component. This will process the imports/exports of the core |
| 5 | //! wasm module and translate between the `wit-parser` AST and the component |
| 6 | //! model binary format, producing a final component which will import |
| 7 | //! `*.wit` defined interfaces and export `*.wit` defined interfaces as well |
| 8 | //! with everything wired up internally according to the canonical ABI and such. |
| 9 | //! |
| 10 | //! This doc block here is not currently 100% complete and doesn't cover the |
| 11 | //! full functionality of this module. |
| 12 | //! |
| 13 | //! # Adapter Modules |
| 14 | //! |
| 15 | //! One feature of this encoding process which is non-obvious is the support for |
| 16 | //! "adapter modules". The general idea here is that historical host API |
| 17 | //! definitions have been around for quite some time, such as |
| 18 | //! `wasi_snapshot_preview1`, but these host API definitions are not compatible |
| 19 | //! with the canonical ABI or component model exactly. These APIs, however, can |
| 20 | //! in most situations be roughly adapted to component-model equivalents. This |
| 21 | //! is where adapter modules come into play, they're converting from some |
| 22 | //! arbitrary API/ABI into a component-model using API. |
| 23 | //! |
| 24 | //! An adapter module is a separately compiled `*.wasm` blob which will export |
| 25 | //! functions matching the desired ABI (e.g. exporting functions matching the |
| 26 | //! `wasi_snapshot_preview1` ABI). The `*.wasm` blob will then import functions |
| 27 | //! in the canonical ABI and internally adapt the exported functions to the |
| 28 | //! imported functions. The encoding support in this module is what wires |
| 29 | //! everything up and makes sure that everything is imported and exported to the |
| 30 | //! right place. Adapter modules currently always use "indirect lowerings" |
| 31 | //! meaning that a shim module is created and provided as the imports to the |
| 32 | //! main core wasm module, and the shim module is "filled in" at a later time |
| 33 | //! during the instantiation process. |
| 34 | //! |
| 35 | //! Adapter modules are not intended to be general purpose and are currently |
| 36 | //! very restrictive, namely: |
| 37 | //! |
| 38 | //! * They must import a linear memory and not define their own linear memory |
| 39 | //! otherwise. In other words they import memory and cannot use multi-memory. |
| 40 | //! * They cannot define any `elem` or `data` segments since otherwise there's |
| 41 | //! no knowledge ahead-of-time of where their data or element segments could |
| 42 | //! go. This means things like no panics, no indirect calls, etc. |
| 43 | //! * If the adapter uses a shadow stack, the global that points to it must be a |
| 44 | //! mutable `i32` named `__stack_pointer`. This stack is automatically |
| 45 | //! allocated with an injected `allocate_stack` function that will either use |
| 46 | //! the main module's `cabi_realloc` export (if present) or `memory.grow`. It |
| 47 | //! allocates only 64KB of stack space, and there is no protection if that |
| 48 | //! overflows. |
| 49 | //! * If the adapter has a global, mutable `i32` named `allocation_state`, it |
| 50 | //! will be used to keep track of stack allocation status and avoid infinite |
| 51 | //! recursion if the main module's `cabi_realloc` function calls back into the |
| 52 | //! adapter. `allocate_stack` will check this global on entry; if it is zero, |
| 53 | //! it will set it to one, then allocate the stack, and finally set it to two. |
| 54 | //! If it is non-zero, `allocate_stack` will do nothing and return immediately |
| 55 | //! (because either the stack has already been allocated or is in the process |
| 56 | //! of being allocated). If the adapter does not have an `allocation_state`, |
| 57 | //! `allocate_stack` will use `memory.grow` to allocate the stack; it will |
| 58 | //! _not_ use the main module's `cabi_realloc` even if it's available. |
| 59 | //! * If the adapter imports a `cabi_realloc` function, and the main module |
| 60 | //! exports one, they'll be linked together via an alias. If the adapter |
| 61 | //! imports such a function but the main module does _not_ export one, we'll |
| 62 | //! synthesize one based on `memory.grow` (which will trap for any size other |
| 63 | //! than 64KB). Note that the main module's `cabi_realloc` function may call |
| 64 | //! back into the adapter before the shadow stack has been allocated. In this |
| 65 | //! case (when `allocation_state` is zero or one), the adapter should return |
| 66 | //! whatever dummy value(s) it can immediately without touching the stack. |
| 67 | //! |
| 68 | //! This means that adapter modules are not meant to be written by everyone. |
| 69 | //! It's assumed that these will be relatively few and far between yet still a |
| 70 | //! crucial part of the transition process from to the component model since |
| 71 | //! otherwise there's no way to run a `wasi_snapshot_preview1` module within the |
| 72 | //! component model. |
| 73 | |
| 74 | use crate::metadata::{self, Bindgen, ModuleMetadata}; |
| 75 | use crate::validation::{Export, ExportMap, Import, ImportInstance, ImportMap, PayloadInfo}; |
| 76 | use crate::StringEncoding; |
| 77 | use anyhow::{anyhow, bail, Context, Result}; |
| 78 | use indexmap::{IndexMap, IndexSet}; |
| 79 | use std::borrow::Cow; |
| 80 | use std::collections::HashMap; |
| 81 | use std::hash::Hash; |
| 82 | use std::mem; |
| 83 | use wasm_encoder::*; |
| 84 | use wasmparser::Validator; |
| 85 | use wit_parser::{ |
| 86 | abi::{AbiVariant, WasmSignature, WasmType}, |
| 87 | Docs, Function, FunctionKind, InterfaceId, LiveTypes, Resolve, Results, Stability, Type, |
| 88 | TypeDefKind, TypeId, TypeOwner, WorldItem, WorldKey, |
| 89 | }; |
| 90 | |
| 91 | const INDIRECT_TABLE_NAME: &str = "$imports" ; |
| 92 | |
| 93 | mod wit; |
| 94 | pub use wit::{encode, encode_world}; |
| 95 | |
| 96 | mod types; |
| 97 | use types::{InstanceTypeEncoder, RootTypeEncoder, ValtypeEncoder}; |
| 98 | mod world; |
| 99 | use world::{ComponentWorld, ImportedInterface, Lowering}; |
| 100 | |
| 101 | fn to_val_type(ty: &WasmType) -> ValType { |
| 102 | match ty { |
| 103 | WasmType::I32 => ValType::I32, |
| 104 | WasmType::I64 => ValType::I64, |
| 105 | WasmType::F32 => ValType::F32, |
| 106 | WasmType::F64 => ValType::F64, |
| 107 | WasmType::Pointer => ValType::I32, |
| 108 | WasmType::PointerOrI64 => ValType::I64, |
| 109 | WasmType::Length => ValType::I32, |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | bitflags::bitflags! { |
| 114 | /// Options in the `canon lower` or `canon lift` required for a particular |
| 115 | /// function. |
| 116 | #[derive (Copy, Clone, Debug)] |
| 117 | pub struct RequiredOptions: u8 { |
| 118 | /// A memory must be specified, typically the "main module"'s memory |
| 119 | /// export. |
| 120 | const MEMORY = 1 << 0; |
| 121 | /// A `realloc` function must be specified, typically named |
| 122 | /// `cabi_realloc`. |
| 123 | const REALLOC = 1 << 1; |
| 124 | /// A string encoding must be specified, which is always utf-8 for now |
| 125 | /// today. |
| 126 | const STRING_ENCODING = 1 << 2; |
| 127 | const ASYNC = 1 << 3; |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | impl RequiredOptions { |
| 132 | fn for_import(resolve: &Resolve, func: &Function, abi: AbiVariant) -> RequiredOptions { |
| 133 | let sig = resolve.wasm_signature(abi, func); |
| 134 | let mut ret = RequiredOptions::empty(); |
| 135 | // Lift the params and lower the results for imports |
| 136 | ret.add_lift(TypeContents::for_types( |
| 137 | resolve, |
| 138 | func.params.iter().map(|(_, t)| t), |
| 139 | )); |
| 140 | ret.add_lower(TypeContents::for_types(resolve, func.results.iter_types())); |
| 141 | |
| 142 | // If anything is indirect then `memory` will be required to read the |
| 143 | // indirect values. |
| 144 | if sig.retptr || sig.indirect_params { |
| 145 | ret |= RequiredOptions::MEMORY; |
| 146 | } |
| 147 | if abi == AbiVariant::GuestImportAsync { |
| 148 | ret |= RequiredOptions::ASYNC; |
| 149 | } |
| 150 | ret |
| 151 | } |
| 152 | |
| 153 | fn for_export(resolve: &Resolve, func: &Function, abi: AbiVariant) -> RequiredOptions { |
| 154 | let sig = resolve.wasm_signature(abi, func); |
| 155 | let mut ret = RequiredOptions::empty(); |
| 156 | // Lower the params and lift the results for exports |
| 157 | ret.add_lower(TypeContents::for_types( |
| 158 | resolve, |
| 159 | func.params.iter().map(|(_, t)| t), |
| 160 | )); |
| 161 | ret.add_lift(TypeContents::for_types(resolve, func.results.iter_types())); |
| 162 | |
| 163 | // If anything is indirect then `memory` will be required to read the |
| 164 | // indirect values, but if the arguments are indirect then `realloc` is |
| 165 | // additionally required to allocate space for the parameters. |
| 166 | if sig.retptr || sig.indirect_params { |
| 167 | ret |= RequiredOptions::MEMORY; |
| 168 | if sig.indirect_params { |
| 169 | ret |= RequiredOptions::REALLOC; |
| 170 | } |
| 171 | } |
| 172 | if let AbiVariant::GuestExportAsync | AbiVariant::GuestExportAsyncStackful = abi { |
| 173 | ret |= RequiredOptions::ASYNC; |
| 174 | } |
| 175 | ret |
| 176 | } |
| 177 | |
| 178 | fn add_lower(&mut self, types: TypeContents) { |
| 179 | // If lists/strings are lowered into wasm then memory is required as |
| 180 | // usual but `realloc` is also required to allow the external caller to |
| 181 | // allocate space in the destination for the list/string. |
| 182 | if types.contains(TypeContents::LIST) { |
| 183 | *self |= RequiredOptions::MEMORY | RequiredOptions::REALLOC; |
| 184 | } |
| 185 | if types.contains(TypeContents::STRING) { |
| 186 | *self |= RequiredOptions::MEMORY |
| 187 | | RequiredOptions::STRING_ENCODING |
| 188 | | RequiredOptions::REALLOC; |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | fn add_lift(&mut self, types: TypeContents) { |
| 193 | // Unlike for `lower` when lifting a string/list all that's needed is |
| 194 | // memory, since the string/list already resides in memory `realloc` |
| 195 | // isn't needed. |
| 196 | if types.contains(TypeContents::LIST) { |
| 197 | *self |= RequiredOptions::MEMORY; |
| 198 | } |
| 199 | if types.contains(TypeContents::STRING) { |
| 200 | *self |= RequiredOptions::MEMORY | RequiredOptions::STRING_ENCODING; |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | fn into_iter( |
| 205 | self, |
| 206 | encoding: StringEncoding, |
| 207 | memory_index: Option<u32>, |
| 208 | realloc_index: Option<u32>, |
| 209 | ) -> Result<impl ExactSizeIterator<Item = CanonicalOption>> { |
| 210 | #[derive (Default)] |
| 211 | struct Iter { |
| 212 | options: [Option<CanonicalOption>; 5], |
| 213 | current: usize, |
| 214 | count: usize, |
| 215 | } |
| 216 | |
| 217 | impl Iter { |
| 218 | fn push(&mut self, option: CanonicalOption) { |
| 219 | assert!(self.count < self.options.len()); |
| 220 | self.options[self.count] = Some(option); |
| 221 | self.count += 1; |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | impl Iterator for Iter { |
| 226 | type Item = CanonicalOption; |
| 227 | |
| 228 | fn next(&mut self) -> Option<Self::Item> { |
| 229 | if self.current == self.count { |
| 230 | return None; |
| 231 | } |
| 232 | let option = self.options[self.current]; |
| 233 | self.current += 1; |
| 234 | option |
| 235 | } |
| 236 | |
| 237 | fn size_hint(&self) -> (usize, Option<usize>) { |
| 238 | (self.count - self.current, Some(self.count - self.current)) |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | impl ExactSizeIterator for Iter {} |
| 243 | |
| 244 | let mut iter = Iter::default(); |
| 245 | |
| 246 | if self.contains(RequiredOptions::MEMORY) { |
| 247 | iter.push(CanonicalOption::Memory(memory_index.ok_or_else(|| { |
| 248 | anyhow!("module does not export a memory named `memory`" ) |
| 249 | })?)); |
| 250 | } |
| 251 | |
| 252 | if self.contains(RequiredOptions::REALLOC) { |
| 253 | iter.push(CanonicalOption::Realloc(realloc_index.ok_or_else( |
| 254 | || anyhow!("module does not export a function named `cabi_realloc`" ), |
| 255 | )?)); |
| 256 | } |
| 257 | |
| 258 | if self.contains(RequiredOptions::STRING_ENCODING) { |
| 259 | iter.push(encoding.into()); |
| 260 | } |
| 261 | |
| 262 | if self.contains(RequiredOptions::ASYNC) { |
| 263 | iter.push(CanonicalOption::Async); |
| 264 | } |
| 265 | |
| 266 | Ok(iter) |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | bitflags::bitflags! { |
| 271 | /// Flags about what kinds of types are present within the recursive |
| 272 | /// structure of a type. |
| 273 | struct TypeContents: u8 { |
| 274 | const STRING = 1 << 0; |
| 275 | const LIST = 1 << 1; |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | impl TypeContents { |
| 280 | fn for_types<'a>(resolve: &Resolve, types: impl Iterator<Item = &'a Type>) -> Self { |
| 281 | let mut cur = TypeContents::empty(); |
| 282 | for ty in types { |
| 283 | cur |= Self::for_type(resolve, ty); |
| 284 | } |
| 285 | cur |
| 286 | } |
| 287 | |
| 288 | fn for_optional_types<'a>( |
| 289 | resolve: &Resolve, |
| 290 | types: impl Iterator<Item = Option<&'a Type>>, |
| 291 | ) -> Self { |
| 292 | Self::for_types(resolve, types.flatten()) |
| 293 | } |
| 294 | |
| 295 | fn for_optional_type(resolve: &Resolve, ty: Option<&Type>) -> Self { |
| 296 | match ty { |
| 297 | Some(ty) => Self::for_type(resolve, ty), |
| 298 | None => Self::empty(), |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | fn for_type(resolve: &Resolve, ty: &Type) -> Self { |
| 303 | match ty { |
| 304 | Type::Id(id) => match &resolve.types[*id].kind { |
| 305 | TypeDefKind::Handle(h) => match h { |
| 306 | wit_parser::Handle::Own(_) => Self::empty(), |
| 307 | wit_parser::Handle::Borrow(_) => Self::empty(), |
| 308 | }, |
| 309 | TypeDefKind::Resource => Self::empty(), |
| 310 | TypeDefKind::Record(r) => Self::for_types(resolve, r.fields.iter().map(|f| &f.ty)), |
| 311 | TypeDefKind::Tuple(t) => Self::for_types(resolve, t.types.iter()), |
| 312 | TypeDefKind::Flags(_) => Self::empty(), |
| 313 | TypeDefKind::Option(t) => Self::for_type(resolve, t), |
| 314 | TypeDefKind::Result(r) => { |
| 315 | Self::for_optional_type(resolve, r.ok.as_ref()) |
| 316 | | Self::for_optional_type(resolve, r.err.as_ref()) |
| 317 | } |
| 318 | TypeDefKind::Variant(v) => { |
| 319 | Self::for_optional_types(resolve, v.cases.iter().map(|c| c.ty.as_ref())) |
| 320 | } |
| 321 | TypeDefKind::Enum(_) => Self::empty(), |
| 322 | TypeDefKind::List(t) => Self::for_type(resolve, t) | Self::LIST, |
| 323 | TypeDefKind::Type(t) => Self::for_type(resolve, t), |
| 324 | TypeDefKind::Future(_) => Self::empty(), |
| 325 | TypeDefKind::Stream(_) => Self::empty(), |
| 326 | TypeDefKind::ErrorContext => Self::empty(), |
| 327 | TypeDefKind::Unknown => unreachable!(), |
| 328 | }, |
| 329 | Type::String => Self::STRING, |
| 330 | _ => Self::empty(), |
| 331 | } |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | /// State relating to encoding a component. |
| 336 | pub struct EncodingState<'a> { |
| 337 | /// The component being encoded. |
| 338 | component: ComponentBuilder, |
| 339 | /// The index into the core module index space for the inner core module. |
| 340 | /// |
| 341 | /// If `None`, the core module has not been encoded. |
| 342 | module_index: Option<u32>, |
| 343 | /// The index into the core instance index space for the inner core module. |
| 344 | /// |
| 345 | /// If `None`, the core module has not been instantiated. |
| 346 | instance_index: Option<u32>, |
| 347 | /// The index in the core memory index space for the exported memory. |
| 348 | /// |
| 349 | /// If `None`, then the memory has not yet been aliased. |
| 350 | memory_index: Option<u32>, |
| 351 | /// The index of the shim instance used for lowering imports into the core instance. |
| 352 | /// |
| 353 | /// If `None`, then the shim instance how not yet been encoded. |
| 354 | shim_instance_index: Option<u32>, |
| 355 | /// The index of the fixups module to instantiate to fill in the lowered imports. |
| 356 | /// |
| 357 | /// If `None`, then a fixup module has not yet been encoded. |
| 358 | fixups_module_index: Option<u32>, |
| 359 | |
| 360 | /// A map of named adapter modules and the index that the module was defined |
| 361 | /// at. |
| 362 | adapter_modules: IndexMap<&'a str, u32>, |
| 363 | /// A map of adapter module instances and the index of their instance. |
| 364 | adapter_instances: IndexMap<&'a str, u32>, |
| 365 | |
| 366 | /// Imported instances and what index they were imported as. |
| 367 | imported_instances: IndexMap<InterfaceId, u32>, |
| 368 | imported_funcs: IndexMap<String, u32>, |
| 369 | exported_instances: IndexMap<InterfaceId, u32>, |
| 370 | |
| 371 | /// Maps used when translating types to the component model binary format. |
| 372 | /// Note that imports and exports are stored in separate maps since they |
| 373 | /// need fresh hierarchies of types in case the same interface is both |
| 374 | /// imported and exported. |
| 375 | import_type_map: HashMap<TypeId, u32>, |
| 376 | import_func_type_map: HashMap<types::FunctionKey<'a>, u32>, |
| 377 | export_type_map: HashMap<TypeId, u32>, |
| 378 | export_func_type_map: HashMap<types::FunctionKey<'a>, u32>, |
| 379 | |
| 380 | /// Cache of items that have been aliased from core instances. |
| 381 | /// |
| 382 | /// This is a helper to reduce the number of aliases created by ensuring |
| 383 | /// that repeated requests for the same item return the same index of an |
| 384 | /// original `core alias` item. |
| 385 | aliased_core_items: HashMap<(u32, String), u32>, |
| 386 | |
| 387 | /// Metadata about the world inferred from the input to `ComponentEncoder`. |
| 388 | info: &'a ComponentWorld<'a>, |
| 389 | } |
| 390 | |
| 391 | impl<'a> EncodingState<'a> { |
| 392 | fn encode_core_modules(&mut self) { |
| 393 | assert!(self.module_index.is_none()); |
| 394 | let idx = self.component.core_module_raw(&self.info.encoder.module); |
| 395 | self.module_index = Some(idx); |
| 396 | |
| 397 | for (name, adapter) in self.info.adapters.iter() { |
| 398 | let add_meta = wasm_metadata::AddMetadata { |
| 399 | name: Some(if adapter.library_info.is_some() { |
| 400 | name.to_string() |
| 401 | } else { |
| 402 | format!("wit-component:adapter: {name}" ) |
| 403 | }), |
| 404 | ..Default::default() |
| 405 | }; |
| 406 | let wasm = add_meta |
| 407 | .to_wasm(&adapter.wasm) |
| 408 | .expect("core wasm can get name added" ); |
| 409 | let idx = self.component.core_module_raw(&wasm); |
| 410 | let prev = self.adapter_modules.insert(name, idx); |
| 411 | assert!(prev.is_none()); |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | fn root_import_type_encoder( |
| 416 | &mut self, |
| 417 | interface: Option<InterfaceId>, |
| 418 | ) -> RootTypeEncoder<'_, 'a> { |
| 419 | RootTypeEncoder { |
| 420 | state: self, |
| 421 | interface, |
| 422 | import_types: true, |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | fn root_export_type_encoder( |
| 427 | &mut self, |
| 428 | interface: Option<InterfaceId>, |
| 429 | ) -> RootTypeEncoder<'_, 'a> { |
| 430 | RootTypeEncoder { |
| 431 | state: self, |
| 432 | interface, |
| 433 | import_types: false, |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | fn instance_type_encoder(&mut self, interface: InterfaceId) -> InstanceTypeEncoder<'_, 'a> { |
| 438 | InstanceTypeEncoder { |
| 439 | state: self, |
| 440 | interface, |
| 441 | type_map: Default::default(), |
| 442 | func_type_map: Default::default(), |
| 443 | ty: Default::default(), |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | fn encode_imports(&mut self, name_map: &HashMap<String, String>) -> Result<()> { |
| 448 | let mut has_funcs = false; |
| 449 | for (name, info) in self.info.import_map.iter() { |
| 450 | match name { |
| 451 | Some(name) => { |
| 452 | self.encode_interface_import(name_map.get(name).unwrap_or(name), info)? |
| 453 | } |
| 454 | None => has_funcs = true, |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | let resolve = &self.info.encoder.metadata.resolve; |
| 459 | let world = &resolve.worlds[self.info.encoder.metadata.world]; |
| 460 | for (_name, item) in world.imports.iter() { |
| 461 | if let WorldItem::Type(ty) = item { |
| 462 | self.root_import_type_encoder(None) |
| 463 | .encode_valtype(resolve, &Type::Id(*ty))?; |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | if has_funcs { |
| 468 | let info = &self.info.import_map[&None]; |
| 469 | self.encode_root_import_funcs(info)?; |
| 470 | } |
| 471 | Ok(()) |
| 472 | } |
| 473 | |
| 474 | fn encode_interface_import(&mut self, name: &str, info: &ImportedInterface) -> Result<()> { |
| 475 | let resolve = &self.info.encoder.metadata.resolve; |
| 476 | let interface_id = info.interface.as_ref().unwrap(); |
| 477 | let interface_id = *interface_id; |
| 478 | let interface = &resolve.interfaces[interface_id]; |
| 479 | log::trace!("encoding imports for ` {name}` as {:?}" , interface_id); |
| 480 | let mut encoder = self.instance_type_encoder(interface_id); |
| 481 | |
| 482 | // First encode all type information |
| 483 | if let Some(live) = encoder.state.info.live_type_imports.get(&interface_id) { |
| 484 | for ty in live { |
| 485 | log::trace!( |
| 486 | "encoding extra type {ty:?} name= {:?}" , |
| 487 | resolve.types[*ty].name |
| 488 | ); |
| 489 | encoder.encode_valtype(resolve, &Type::Id(*ty))?; |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | // Next encode all required functions from this imported interface |
| 494 | // into the instance type. |
| 495 | for (_, func) in interface.functions.iter() { |
| 496 | if !(info |
| 497 | .lowerings |
| 498 | .contains_key(&(func.name.clone(), AbiVariant::GuestImport)) |
| 499 | || info |
| 500 | .lowerings |
| 501 | .contains_key(&(func.name.clone(), AbiVariant::GuestImportAsync))) |
| 502 | { |
| 503 | continue; |
| 504 | } |
| 505 | log::trace!("encoding function type for ` {}`" , func.name); |
| 506 | let idx = encoder.encode_func_type(resolve, func)?; |
| 507 | |
| 508 | encoder.ty.export(&func.name, ComponentTypeRef::Func(idx)); |
| 509 | } |
| 510 | |
| 511 | let ty = encoder.ty; |
| 512 | // Don't encode empty instance types since they're not |
| 513 | // meaningful to the runtime of the component anyway. |
| 514 | if ty.is_empty() { |
| 515 | return Ok(()); |
| 516 | } |
| 517 | let instance_type_idx = self.component.type_instance(&ty); |
| 518 | let instance_idx = self |
| 519 | .component |
| 520 | .import(name, ComponentTypeRef::Instance(instance_type_idx)); |
| 521 | let prev = self.imported_instances.insert(interface_id, instance_idx); |
| 522 | assert!(prev.is_none()); |
| 523 | Ok(()) |
| 524 | } |
| 525 | |
| 526 | fn encode_root_import_funcs(&mut self, info: &ImportedInterface) -> Result<()> { |
| 527 | let resolve = &self.info.encoder.metadata.resolve; |
| 528 | let world = self.info.encoder.metadata.world; |
| 529 | for (name, item) in resolve.worlds[world].imports.iter() { |
| 530 | let func = match item { |
| 531 | WorldItem::Function(f) => f, |
| 532 | WorldItem::Interface { .. } | WorldItem::Type(_) => continue, |
| 533 | }; |
| 534 | let name = resolve.name_world_key(name); |
| 535 | if !(info |
| 536 | .lowerings |
| 537 | .contains_key(&(name.clone(), AbiVariant::GuestImport)) |
| 538 | || info |
| 539 | .lowerings |
| 540 | .contains_key(&(name.clone(), AbiVariant::GuestImportAsync))) |
| 541 | { |
| 542 | continue; |
| 543 | } |
| 544 | log::trace!("encoding function type for ` {}`" , func.name); |
| 545 | let idx = self |
| 546 | .root_import_type_encoder(None) |
| 547 | .encode_func_type(resolve, func)?; |
| 548 | let func_idx = self.component.import(&name, ComponentTypeRef::Func(idx)); |
| 549 | let prev = self.imported_funcs.insert(name, func_idx); |
| 550 | assert!(prev.is_none()); |
| 551 | } |
| 552 | Ok(()) |
| 553 | } |
| 554 | |
| 555 | fn alias_imported_type(&mut self, interface: InterfaceId, id: TypeId) -> u32 { |
| 556 | let ty = &self.info.encoder.metadata.resolve.types[id]; |
| 557 | let name = ty.name.as_ref().expect("type must have a name" ); |
| 558 | let instance = self.imported_instances[&interface]; |
| 559 | self.component |
| 560 | .alias_export(instance, name, ComponentExportKind::Type) |
| 561 | } |
| 562 | |
| 563 | fn alias_exported_type(&mut self, interface: InterfaceId, id: TypeId) -> u32 { |
| 564 | let ty = &self.info.encoder.metadata.resolve.types[id]; |
| 565 | let name = ty.name.as_ref().expect("type must have a name" ); |
| 566 | let instance = self.exported_instances[&interface]; |
| 567 | self.component |
| 568 | .alias_export(instance, name, ComponentExportKind::Type) |
| 569 | } |
| 570 | |
| 571 | fn encode_core_instantiation(&mut self) -> Result<()> { |
| 572 | // Encode a shim instantiation if needed |
| 573 | let shims = self.encode_shim_instantiation()?; |
| 574 | |
| 575 | // Next declare all exported resource types. This populates |
| 576 | // `export_type_map` and will additionally be used for imports to |
| 577 | // modules instantiated below. |
| 578 | self.declare_exported_resources(&shims); |
| 579 | |
| 580 | // Next instantiate the main module. This provides the linear memory to |
| 581 | // use for all future adapters and enables creating indirect lowerings |
| 582 | // at the end. |
| 583 | self.instantiate_main_module(&shims)?; |
| 584 | |
| 585 | // Separate the adapters according which should be instantiated before |
| 586 | // and after indirect lowerings are encoded. |
| 587 | let (before, after) = self |
| 588 | .info |
| 589 | .adapters |
| 590 | .iter() |
| 591 | .partition::<Vec<_>, _>(|(_, adapter)| { |
| 592 | !matches!( |
| 593 | adapter.library_info, |
| 594 | Some(LibraryInfo { |
| 595 | instantiate_after_shims: true, |
| 596 | .. |
| 597 | }) |
| 598 | ) |
| 599 | }); |
| 600 | |
| 601 | for (name, _adapter) in before { |
| 602 | self.instantiate_adapter_module(&shims, name)?; |
| 603 | } |
| 604 | |
| 605 | // With all the relevant core wasm instances in play now the original shim |
| 606 | // module, if present, can be filled in with lowerings/adapters/etc. |
| 607 | self.encode_indirect_lowerings(&shims)?; |
| 608 | |
| 609 | for (name, _adapter) in after { |
| 610 | self.instantiate_adapter_module(&shims, name)?; |
| 611 | } |
| 612 | |
| 613 | self.encode_initialize_with_start()?; |
| 614 | |
| 615 | Ok(()) |
| 616 | } |
| 617 | |
| 618 | fn lookup_resource_index(&mut self, id: TypeId) -> u32 { |
| 619 | let resolve = &self.info.encoder.metadata.resolve; |
| 620 | let ty = &resolve.types[id]; |
| 621 | match ty.owner { |
| 622 | // If this resource is owned by a world then it's a top-level |
| 623 | // resource which means it must have already been translated so |
| 624 | // it's available for lookup in `import_type_map`. |
| 625 | TypeOwner::World(_) => self.import_type_map[&id], |
| 626 | TypeOwner::Interface(i) => { |
| 627 | let instance = self.imported_instances[&i]; |
| 628 | let name = ty.name.as_ref().expect("resources must be named" ); |
| 629 | self.component |
| 630 | .alias_export(instance, name, ComponentExportKind::Type) |
| 631 | } |
| 632 | TypeOwner::None => panic!("resources must have an owner" ), |
| 633 | } |
| 634 | } |
| 635 | |
| 636 | fn encode_exports(&mut self, module: CustomModule) -> Result<()> { |
| 637 | let resolve = &self.info.encoder.metadata.resolve; |
| 638 | let exports = match module { |
| 639 | CustomModule::Main => &self.info.encoder.main_module_exports, |
| 640 | CustomModule::Adapter(name) => &self.info.encoder.adapters[name].required_exports, |
| 641 | }; |
| 642 | |
| 643 | if exports.is_empty() { |
| 644 | return Ok(()); |
| 645 | } |
| 646 | |
| 647 | let mut interface_func_core_names = IndexMap::new(); |
| 648 | let mut world_func_core_names = IndexMap::new(); |
| 649 | for (core_name, export) in self.info.exports_for(module).iter() { |
| 650 | match export { |
| 651 | Export::WorldFunc(_, name, _) => { |
| 652 | let prev = world_func_core_names.insert(name, core_name); |
| 653 | assert!(prev.is_none()); |
| 654 | } |
| 655 | Export::InterfaceFunc(_, id, name, _) => { |
| 656 | let prev = interface_func_core_names |
| 657 | .entry(id) |
| 658 | .or_insert(IndexMap::new()) |
| 659 | .insert(name.as_str(), core_name); |
| 660 | assert!(prev.is_none()); |
| 661 | } |
| 662 | Export::WorldFuncCallback(..) |
| 663 | | Export::InterfaceFuncCallback(..) |
| 664 | | Export::WorldFuncPostReturn(..) |
| 665 | | Export::InterfaceFuncPostReturn(..) |
| 666 | | Export::ResourceDtor(..) |
| 667 | | Export::Memory |
| 668 | | Export::GeneralPurposeRealloc |
| 669 | | Export::GeneralPurposeExportRealloc |
| 670 | | Export::GeneralPurposeImportRealloc |
| 671 | | Export::Initialize |
| 672 | | Export::ReallocForAdapter => continue, |
| 673 | } |
| 674 | } |
| 675 | |
| 676 | let world = &resolve.worlds[self.info.encoder.metadata.world]; |
| 677 | |
| 678 | for export_name in exports { |
| 679 | let export_string = resolve.name_world_key(export_name); |
| 680 | match &world.exports[export_name] { |
| 681 | WorldItem::Function(func) => { |
| 682 | let ty = self |
| 683 | .root_import_type_encoder(None) |
| 684 | .encode_func_type(resolve, func)?; |
| 685 | let core_name = world_func_core_names[&func.name]; |
| 686 | let idx = self.encode_lift(module, &core_name, export_name, func, ty)?; |
| 687 | self.component |
| 688 | .export(&export_string, ComponentExportKind::Func, idx, None); |
| 689 | } |
| 690 | WorldItem::Interface { id, .. } => { |
| 691 | let core_names = interface_func_core_names.get(id); |
| 692 | self.encode_interface_export( |
| 693 | &export_string, |
| 694 | module, |
| 695 | export_name, |
| 696 | *id, |
| 697 | core_names, |
| 698 | )?; |
| 699 | } |
| 700 | WorldItem::Type(_) => unreachable!(), |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | Ok(()) |
| 705 | } |
| 706 | |
| 707 | fn encode_interface_export( |
| 708 | &mut self, |
| 709 | export_name: &str, |
| 710 | module: CustomModule<'_>, |
| 711 | key: &WorldKey, |
| 712 | export: InterfaceId, |
| 713 | interface_func_core_names: Option<&IndexMap<&str, &str>>, |
| 714 | ) -> Result<()> { |
| 715 | log::trace!("encode interface export ` {export_name}`" ); |
| 716 | let resolve = &self.info.encoder.metadata.resolve; |
| 717 | |
| 718 | // First execute a `canon lift` for all the functions in this interface |
| 719 | // from the core wasm export. This requires type information but notably |
| 720 | // not exported type information since we don't want to export this |
| 721 | // interface's types from the root of the component. Each lifted |
| 722 | // function is saved off into an `imports` array to get imported into |
| 723 | // the nested component synthesized below. |
| 724 | let mut imports = Vec::new(); |
| 725 | let mut root = self.root_export_type_encoder(Some(export)); |
| 726 | for (_, func) in &resolve.interfaces[export].functions { |
| 727 | let core_name = interface_func_core_names.unwrap()[func.name.as_str()]; |
| 728 | let ty = root.encode_func_type(resolve, func)?; |
| 729 | let func_index = root.state.encode_lift(module, &core_name, key, func, ty)?; |
| 730 | imports.push(( |
| 731 | import_func_name(func), |
| 732 | ComponentExportKind::Func, |
| 733 | func_index, |
| 734 | )); |
| 735 | } |
| 736 | |
| 737 | // Next a nested component is created which will import the functions |
| 738 | // above and then reexport them. The purpose of them is to "re-type" the |
| 739 | // functions through type ascription on each `func` item. |
| 740 | let mut nested = NestedComponentTypeEncoder { |
| 741 | component: ComponentBuilder::default(), |
| 742 | type_map: Default::default(), |
| 743 | func_type_map: Default::default(), |
| 744 | export_types: false, |
| 745 | interface: export, |
| 746 | state: self, |
| 747 | imports: IndexMap::new(), |
| 748 | }; |
| 749 | |
| 750 | // Import all transitively-referenced types from other interfaces into |
| 751 | // this component. This temporarily switches the `interface` listed to |
| 752 | // the interface of the referred-to-type to generate the import. After |
| 753 | // this loop `interface` is rewritten to `export`. |
| 754 | // |
| 755 | // Each component is a standalone "island" so the necessary type |
| 756 | // information needs to be rebuilt within this component. This ensures |
| 757 | // that we're able to build a valid component and additionally connect |
| 758 | // all the type information to the outer context. |
| 759 | let mut types_to_import = LiveTypes::default(); |
| 760 | types_to_import.add_interface(resolve, export); |
| 761 | let exports_used = &nested.state.info.exports_used[&export]; |
| 762 | for ty in types_to_import.iter() { |
| 763 | if let TypeOwner::Interface(owner) = resolve.types[ty].owner { |
| 764 | if owner == export { |
| 765 | // Here this deals with the current exported interface which |
| 766 | // is handled below. |
| 767 | continue; |
| 768 | } |
| 769 | |
| 770 | // Ensure that `self` has encoded this type before. If so this |
| 771 | // is a noop but otherwise it generates the type here. |
| 772 | let mut encoder = if exports_used.contains(&owner) { |
| 773 | nested.state.root_export_type_encoder(Some(export)) |
| 774 | } else { |
| 775 | nested.state.root_import_type_encoder(Some(export)) |
| 776 | }; |
| 777 | encoder.encode_valtype(resolve, &Type::Id(ty))?; |
| 778 | |
| 779 | // Next generate the same type but this time within the |
| 780 | // component itself. The type generated above (or prior) will be |
| 781 | // used to satisfy this type import. |
| 782 | nested.interface = owner; |
| 783 | nested.encode_valtype(resolve, &Type::Id(ty))?; |
| 784 | } |
| 785 | } |
| 786 | nested.interface = export; |
| 787 | |
| 788 | // Record the map of types imported to their index at where they were |
| 789 | // imported. This is used after imports are encoded as exported types |
| 790 | // will refer to these. |
| 791 | let imported_types = nested.type_map.clone(); |
| 792 | |
| 793 | // Handle resource types for this instance specially, namely importing |
| 794 | // them into the nested component. This models how the resource is |
| 795 | // imported from its definition in the outer component to get reexported |
| 796 | // internally. This chiefly avoids creating a second resource which is |
| 797 | // not desired in this situation. |
| 798 | let mut resources = HashMap::new(); |
| 799 | for (_name, ty) in resolve.interfaces[export].types.iter() { |
| 800 | if !matches!(resolve.types[*ty].kind, TypeDefKind::Resource) { |
| 801 | continue; |
| 802 | } |
| 803 | let idx = match nested.encode_valtype(resolve, &Type::Id(*ty))? { |
| 804 | ComponentValType::Type(idx) => idx, |
| 805 | _ => unreachable!(), |
| 806 | }; |
| 807 | resources.insert(*ty, idx); |
| 808 | } |
| 809 | |
| 810 | // Next import each function of this interface. This will end up |
| 811 | // defining local types as necessary or using the types as imported |
| 812 | // above. |
| 813 | for (_, func) in resolve.interfaces[export].functions.iter() { |
| 814 | let ty = nested.encode_func_type(resolve, func)?; |
| 815 | nested |
| 816 | .component |
| 817 | .import(&import_func_name(func), ComponentTypeRef::Func(ty)); |
| 818 | } |
| 819 | |
| 820 | // Swap the `nested.type_map` which was previously from `TypeId` to |
| 821 | // `u32` to instead being from `u32` to `TypeId`. This reverse map is |
| 822 | // then used in conjunction with `self.type_map` to satisfy all type |
| 823 | // imports of the nested component generated. The type import's index in |
| 824 | // the inner component is translated to a `TypeId` via `reverse_map` |
| 825 | // which is then translated back to our own index space via `type_map`. |
| 826 | let reverse_map = nested |
| 827 | .type_map |
| 828 | .drain() |
| 829 | .map(|p| (p.1, p.0)) |
| 830 | .collect::<HashMap<_, _>>(); |
| 831 | for (name, idx) in nested.imports.drain(..) { |
| 832 | let id = reverse_map[&idx]; |
| 833 | let owner = match resolve.types[id].owner { |
| 834 | TypeOwner::Interface(id) => id, |
| 835 | _ => unreachable!(), |
| 836 | }; |
| 837 | let idx = if owner == export || exports_used.contains(&owner) { |
| 838 | log::trace!("consulting exports for {id:?}" ); |
| 839 | nested.state.export_type_map[&id] |
| 840 | } else { |
| 841 | log::trace!("consulting imports for {id:?}" ); |
| 842 | nested.state.import_type_map[&id] |
| 843 | }; |
| 844 | imports.push((name, ComponentExportKind::Type, idx)) |
| 845 | } |
| 846 | |
| 847 | // Before encoding exports reset the type map to what all was imported |
| 848 | // from foreign interfaces. This will enable any encoded types below to |
| 849 | // refer to imports which, after type substitution, will point to the |
| 850 | // correct type in the outer component context. |
| 851 | nested.type_map = imported_types; |
| 852 | |
| 853 | // Next the component reexports all of its imports, but notably uses the |
| 854 | // type ascription feature to change the type of the function. Note that |
| 855 | // no structural change is happening to the types here but instead types |
| 856 | // are getting proper names and such now that this nested component is a |
| 857 | // new type index space. Hence the `export_types = true` flag here which |
| 858 | // flows through the type encoding and when types are emitted. |
| 859 | nested.export_types = true; |
| 860 | nested.func_type_map.clear(); |
| 861 | |
| 862 | // To start off all type information is encoded. This will be used by |
| 863 | // functions below but notably this also has special handling for |
| 864 | // resources. Resources reexport their imported resource type under |
| 865 | // the final name which achieves the desired goal of threading through |
| 866 | // the original resource without creating a new one. |
| 867 | for (_, id) in resolve.interfaces[export].types.iter() { |
| 868 | let ty = &resolve.types[*id]; |
| 869 | match ty.kind { |
| 870 | TypeDefKind::Resource => { |
| 871 | let idx = nested.component.export( |
| 872 | ty.name.as_ref().expect("resources must be named" ), |
| 873 | ComponentExportKind::Type, |
| 874 | resources[id], |
| 875 | None, |
| 876 | ); |
| 877 | nested.type_map.insert(*id, idx); |
| 878 | } |
| 879 | _ => { |
| 880 | nested.encode_valtype(resolve, &Type::Id(*id))?; |
| 881 | } |
| 882 | } |
| 883 | } |
| 884 | |
| 885 | for (i, (_, func)) in resolve.interfaces[export].functions.iter().enumerate() { |
| 886 | let ty = nested.encode_func_type(resolve, func)?; |
| 887 | nested.component.export( |
| 888 | &func.name, |
| 889 | ComponentExportKind::Func, |
| 890 | i as u32, |
| 891 | Some(ComponentTypeRef::Func(ty)), |
| 892 | ); |
| 893 | } |
| 894 | |
| 895 | // Embed the component within our component and then instantiate it with |
| 896 | // the lifted functions. That final instance is then exported under the |
| 897 | // appropriate name as the final typed export of this component. |
| 898 | let component = nested.component; |
| 899 | let component_index = self.component.component(component); |
| 900 | let instance_index = self.component.instantiate(component_index, imports); |
| 901 | let idx = self.component.export( |
| 902 | export_name, |
| 903 | ComponentExportKind::Instance, |
| 904 | instance_index, |
| 905 | None, |
| 906 | ); |
| 907 | let prev = self.exported_instances.insert(export, idx); |
| 908 | assert!(prev.is_none()); |
| 909 | |
| 910 | // After everything is all said and done remove all the type information |
| 911 | // about type exports of this interface. Any entries in the map |
| 912 | // currently were used to create the instance above but aren't the |
| 913 | // actual copy of the exported type since that comes from the exported |
| 914 | // instance itself. Entries will be re-inserted into this map as |
| 915 | // necessary via aliases from the exported instance which is the new |
| 916 | // source of truth for all these types. |
| 917 | for (_name, id) in resolve.interfaces[export].types.iter() { |
| 918 | self.export_type_map.remove(id); |
| 919 | } |
| 920 | |
| 921 | return Ok(()); |
| 922 | |
| 923 | struct NestedComponentTypeEncoder<'state, 'a> { |
| 924 | component: ComponentBuilder, |
| 925 | type_map: HashMap<TypeId, u32>, |
| 926 | func_type_map: HashMap<types::FunctionKey<'a>, u32>, |
| 927 | export_types: bool, |
| 928 | interface: InterfaceId, |
| 929 | state: &'state mut EncodingState<'a>, |
| 930 | imports: IndexMap<String, u32>, |
| 931 | } |
| 932 | |
| 933 | impl<'a> ValtypeEncoder<'a> for NestedComponentTypeEncoder<'_, 'a> { |
| 934 | fn defined_type(&mut self) -> (u32, ComponentDefinedTypeEncoder<'_>) { |
| 935 | self.component.type_defined() |
| 936 | } |
| 937 | fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>) { |
| 938 | self.component.type_function() |
| 939 | } |
| 940 | fn export_type(&mut self, idx: u32, name: &'a str) -> Option<u32> { |
| 941 | if self.export_types { |
| 942 | Some( |
| 943 | self.component |
| 944 | .export(name, ComponentExportKind::Type, idx, None), |
| 945 | ) |
| 946 | } else { |
| 947 | let name = self.unique_import_name(name); |
| 948 | let ret = self |
| 949 | .component |
| 950 | .import(&name, ComponentTypeRef::Type(TypeBounds::Eq(idx))); |
| 951 | self.imports.insert(name, ret); |
| 952 | Some(ret) |
| 953 | } |
| 954 | } |
| 955 | fn export_resource(&mut self, name: &'a str) -> u32 { |
| 956 | if self.export_types { |
| 957 | panic!("resources should already be exported" ) |
| 958 | } else { |
| 959 | let name = self.unique_import_name(name); |
| 960 | let ret = self |
| 961 | .component |
| 962 | .import(&name, ComponentTypeRef::Type(TypeBounds::SubResource)); |
| 963 | self.imports.insert(name, ret); |
| 964 | ret |
| 965 | } |
| 966 | } |
| 967 | fn import_type(&mut self, _: InterfaceId, _id: TypeId) -> u32 { |
| 968 | unreachable!() |
| 969 | } |
| 970 | fn type_map(&mut self) -> &mut HashMap<TypeId, u32> { |
| 971 | &mut self.type_map |
| 972 | } |
| 973 | fn func_type_map(&mut self) -> &mut HashMap<types::FunctionKey<'a>, u32> { |
| 974 | &mut self.func_type_map |
| 975 | } |
| 976 | fn interface(&self) -> Option<InterfaceId> { |
| 977 | Some(self.interface) |
| 978 | } |
| 979 | } |
| 980 | |
| 981 | impl NestedComponentTypeEncoder<'_, '_> { |
| 982 | fn unique_import_name(&mut self, name: &str) -> String { |
| 983 | let mut name = format!("import-type- {name}" ); |
| 984 | let mut n = 0; |
| 985 | while self.imports.contains_key(&name) { |
| 986 | name = format!(" {name}{n}" ); |
| 987 | n += 1; |
| 988 | } |
| 989 | name |
| 990 | } |
| 991 | } |
| 992 | |
| 993 | fn import_func_name(f: &Function) -> String { |
| 994 | match f.kind { |
| 995 | FunctionKind::Freestanding => { |
| 996 | format!("import-func- {}" , f.name) |
| 997 | } |
| 998 | |
| 999 | // transform `[method]foo.bar` into `import-method-foo-bar` to |
| 1000 | // have it be a valid kebab-name which can't conflict with |
| 1001 | // anything else. |
| 1002 | // |
| 1003 | // There's probably a better and more "formal" way to do this |
| 1004 | // but quick-and-dirty string manipulation should work well |
| 1005 | // enough for now hopefully. |
| 1006 | FunctionKind::Method(_) |
| 1007 | | FunctionKind::Static(_) |
| 1008 | | FunctionKind::Constructor(_) => { |
| 1009 | format!( |
| 1010 | "import- {}" , |
| 1011 | f.name.replace('[' , "" ).replace([']' , '.' ], "-" ) |
| 1012 | ) |
| 1013 | } |
| 1014 | } |
| 1015 | } |
| 1016 | } |
| 1017 | |
| 1018 | fn encode_lift( |
| 1019 | &mut self, |
| 1020 | module: CustomModule<'_>, |
| 1021 | core_name: &str, |
| 1022 | key: &WorldKey, |
| 1023 | func: &Function, |
| 1024 | ty: u32, |
| 1025 | ) -> Result<u32> { |
| 1026 | let resolve = &self.info.encoder.metadata.resolve; |
| 1027 | let metadata = self.info.module_metadata_for(module); |
| 1028 | let instance_index = self.instance_for(module); |
| 1029 | let core_func_index = self.core_alias_export(instance_index, core_name, ExportKind::Func); |
| 1030 | let exports = self.info.exports_for(module); |
| 1031 | |
| 1032 | let options = RequiredOptions::for_export( |
| 1033 | resolve, |
| 1034 | func, |
| 1035 | exports |
| 1036 | .abi(key, func) |
| 1037 | .ok_or_else(|| anyhow!("no ABI found for {}" , func.name))?, |
| 1038 | ); |
| 1039 | |
| 1040 | let encoding = metadata |
| 1041 | .export_encodings |
| 1042 | .get(resolve, key, &func.name) |
| 1043 | .unwrap(); |
| 1044 | let exports = self.info.exports_for(module); |
| 1045 | let realloc_index = exports |
| 1046 | .export_realloc_for(key, func) |
| 1047 | .map(|name| self.core_alias_export(instance_index, name, ExportKind::Func)); |
| 1048 | let mut options = options |
| 1049 | .into_iter(encoding, self.memory_index, realloc_index)? |
| 1050 | .collect::<Vec<_>>(); |
| 1051 | |
| 1052 | if let Some(post_return) = exports.post_return(key, func) { |
| 1053 | let post_return = self.core_alias_export(instance_index, post_return, ExportKind::Func); |
| 1054 | options.push(CanonicalOption::PostReturn(post_return)); |
| 1055 | } |
| 1056 | if let Some(callback) = exports.callback(key, func) { |
| 1057 | let callback = self.core_alias_export(instance_index, callback, ExportKind::Func); |
| 1058 | options.push(CanonicalOption::Callback(callback)); |
| 1059 | } |
| 1060 | let func_index = self.component.lift_func(core_func_index, ty, options); |
| 1061 | Ok(func_index) |
| 1062 | } |
| 1063 | |
| 1064 | fn encode_shim_instantiation(&mut self) -> Result<Shims<'a>> { |
| 1065 | let mut ret = Shims::default(); |
| 1066 | |
| 1067 | ret.append_indirect(self.info, CustomModule::Main) |
| 1068 | .context("failed to register indirect shims for main module" )?; |
| 1069 | |
| 1070 | // For all required adapter modules a shim is created for each required |
| 1071 | // function and additionally a set of shims are created for the |
| 1072 | // interface imported into the shim module itself. |
| 1073 | for (adapter_name, _adapter) in self.info.adapters.iter() { |
| 1074 | ret.append_indirect(self.info, CustomModule::Adapter(adapter_name)) |
| 1075 | .with_context(|| { |
| 1076 | format!("failed to register indirect shims for adapter {adapter_name}" ) |
| 1077 | })?; |
| 1078 | } |
| 1079 | |
| 1080 | if ret.shims.is_empty() { |
| 1081 | return Ok(ret); |
| 1082 | } |
| 1083 | |
| 1084 | assert!(self.shim_instance_index.is_none()); |
| 1085 | assert!(self.fixups_module_index.is_none()); |
| 1086 | |
| 1087 | // This function encodes two modules: |
| 1088 | // - A shim module that defines a table and exports functions |
| 1089 | // that indirectly call through the table. |
| 1090 | // - A fixup module that imports that table and a set of functions |
| 1091 | // and populates the imported table via active element segments. The |
| 1092 | // fixup module is used to populate the shim's table once the |
| 1093 | // imported functions have been lowered. |
| 1094 | |
| 1095 | let mut types = TypeSection::new(); |
| 1096 | let mut tables = TableSection::new(); |
| 1097 | let mut functions = FunctionSection::new(); |
| 1098 | let mut exports = ExportSection::new(); |
| 1099 | let mut code = CodeSection::new(); |
| 1100 | let mut sigs = IndexMap::new(); |
| 1101 | let mut imports_section = ImportSection::new(); |
| 1102 | let mut elements = ElementSection::new(); |
| 1103 | let mut func_indexes = Vec::new(); |
| 1104 | let mut func_names = NameMap::new(); |
| 1105 | |
| 1106 | for (i, shim) in ret.shims.values().enumerate() { |
| 1107 | let i = i as u32; |
| 1108 | let type_index = *sigs.entry(&shim.sig).or_insert_with(|| { |
| 1109 | let index = types.len(); |
| 1110 | types.ty().function( |
| 1111 | shim.sig.params.iter().map(to_val_type), |
| 1112 | shim.sig.results.iter().map(to_val_type), |
| 1113 | ); |
| 1114 | index |
| 1115 | }); |
| 1116 | |
| 1117 | functions.function(type_index); |
| 1118 | Self::encode_shim_function(type_index, i, &mut code, shim.sig.params.len() as u32); |
| 1119 | exports.export(&shim.name, ExportKind::Func, i); |
| 1120 | |
| 1121 | imports_section.import("" , &shim.name, EntityType::Function(type_index)); |
| 1122 | func_indexes.push(i); |
| 1123 | func_names.append(i, &shim.debug_name); |
| 1124 | } |
| 1125 | let mut names = NameSection::new(); |
| 1126 | names.module("wit-component:shim" ); |
| 1127 | names.functions(&func_names); |
| 1128 | |
| 1129 | let table_type = TableType { |
| 1130 | element_type: RefType::FUNCREF, |
| 1131 | minimum: ret.shims.len() as u64, |
| 1132 | maximum: Some(ret.shims.len() as u64), |
| 1133 | table64: false, |
| 1134 | shared: false, |
| 1135 | }; |
| 1136 | |
| 1137 | tables.table(table_type); |
| 1138 | |
| 1139 | exports.export(INDIRECT_TABLE_NAME, ExportKind::Table, 0); |
| 1140 | imports_section.import("" , INDIRECT_TABLE_NAME, table_type); |
| 1141 | |
| 1142 | elements.active( |
| 1143 | None, |
| 1144 | &ConstExpr::i32_const(0), |
| 1145 | Elements::Functions(func_indexes.into()), |
| 1146 | ); |
| 1147 | |
| 1148 | let mut shim = Module::new(); |
| 1149 | shim.section(&types); |
| 1150 | shim.section(&functions); |
| 1151 | shim.section(&tables); |
| 1152 | shim.section(&exports); |
| 1153 | shim.section(&code); |
| 1154 | shim.section(&RawCustomSection( |
| 1155 | &crate::base_producers().raw_custom_section(), |
| 1156 | )); |
| 1157 | shim.section(&names); |
| 1158 | |
| 1159 | let mut fixups = Module::default(); |
| 1160 | fixups.section(&types); |
| 1161 | fixups.section(&imports_section); |
| 1162 | fixups.section(&elements); |
| 1163 | fixups.section(&RawCustomSection( |
| 1164 | &crate::base_producers().raw_custom_section(), |
| 1165 | )); |
| 1166 | |
| 1167 | let mut names = NameSection::new(); |
| 1168 | names.module("wit-component:fixups" ); |
| 1169 | fixups.section(&names); |
| 1170 | |
| 1171 | let shim_module_index = self.component.core_module(&shim); |
| 1172 | self.fixups_module_index = Some(self.component.core_module(&fixups)); |
| 1173 | self.shim_instance_index = Some(self.component.core_instantiate(shim_module_index, [])); |
| 1174 | |
| 1175 | return Ok(ret); |
| 1176 | } |
| 1177 | |
| 1178 | fn encode_shim_function( |
| 1179 | type_index: u32, |
| 1180 | func_index: u32, |
| 1181 | code: &mut CodeSection, |
| 1182 | param_count: u32, |
| 1183 | ) { |
| 1184 | let mut func = wasm_encoder::Function::new(std::iter::empty()); |
| 1185 | for i in 0..param_count { |
| 1186 | func.instruction(&Instruction::LocalGet(i)); |
| 1187 | } |
| 1188 | func.instruction(&Instruction::I32Const(func_index as i32)); |
| 1189 | func.instruction(&Instruction::CallIndirect { |
| 1190 | type_index, |
| 1191 | table_index: 0, |
| 1192 | }); |
| 1193 | func.instruction(&Instruction::End); |
| 1194 | code.function(&func); |
| 1195 | } |
| 1196 | |
| 1197 | fn encode_indirect_lowerings(&mut self, shims: &Shims<'_>) -> Result<()> { |
| 1198 | if shims.shims.is_empty() { |
| 1199 | return Ok(()); |
| 1200 | } |
| 1201 | |
| 1202 | let shim_instance_index = self |
| 1203 | .shim_instance_index |
| 1204 | .expect("must have an instantiated shim" ); |
| 1205 | |
| 1206 | let table_index = |
| 1207 | self.core_alias_export(shim_instance_index, INDIRECT_TABLE_NAME, ExportKind::Table); |
| 1208 | |
| 1209 | let resolve = &self.info.encoder.metadata.resolve; |
| 1210 | |
| 1211 | let mut exports = Vec::new(); |
| 1212 | exports.push((INDIRECT_TABLE_NAME, ExportKind::Table, table_index)); |
| 1213 | |
| 1214 | for shim in shims.shims.values() { |
| 1215 | let core_func_index = match &shim.kind { |
| 1216 | // Indirect lowerings are a `canon lower`'d function with |
| 1217 | // options specified from a previously instantiated instance. |
| 1218 | // This previous instance could either be the main module or an |
| 1219 | // adapter module, which affects the `realloc` option here. |
| 1220 | // Currently only one linear memory is supported so the linear |
| 1221 | // memory always comes from the main module. |
| 1222 | ShimKind::IndirectLowering { |
| 1223 | interface, |
| 1224 | index, |
| 1225 | realloc, |
| 1226 | encoding, |
| 1227 | } => { |
| 1228 | let interface = &self.info.import_map[interface]; |
| 1229 | let ((name, _), _) = interface.lowerings.get_index(*index).unwrap(); |
| 1230 | let func_index = match &interface.interface { |
| 1231 | Some(interface_id) => { |
| 1232 | let instance_index = self.imported_instances[interface_id]; |
| 1233 | self.component.alias_export( |
| 1234 | instance_index, |
| 1235 | name, |
| 1236 | ComponentExportKind::Func, |
| 1237 | ) |
| 1238 | } |
| 1239 | None => self.imported_funcs[name], |
| 1240 | }; |
| 1241 | |
| 1242 | let realloc = self |
| 1243 | .info |
| 1244 | .exports_for(*realloc) |
| 1245 | .import_realloc_for(interface.interface, name) |
| 1246 | .map(|name| { |
| 1247 | let instance = self.instance_for(*realloc); |
| 1248 | self.core_alias_export(instance, name, ExportKind::Func) |
| 1249 | }); |
| 1250 | |
| 1251 | self.component.lower_func( |
| 1252 | func_index, |
| 1253 | shim.options |
| 1254 | .into_iter(*encoding, self.memory_index, realloc)?, |
| 1255 | ) |
| 1256 | } |
| 1257 | |
| 1258 | // Adapter shims are defined by an export from an adapter |
| 1259 | // instance, so use the specified name here and the previously |
| 1260 | // created instances to get the core item that represents the |
| 1261 | // shim. |
| 1262 | ShimKind::Adapter { adapter, func } => { |
| 1263 | self.core_alias_export(self.adapter_instances[adapter], func, ExportKind::Func) |
| 1264 | } |
| 1265 | |
| 1266 | // Resources are required for a module to be instantiated |
| 1267 | // meaning that any destructor for the resource must be called |
| 1268 | // indirectly due to the otherwise circular dependency between |
| 1269 | // the module and the resource itself. |
| 1270 | ShimKind::ResourceDtor { module, export } => { |
| 1271 | self.core_alias_export(self.instance_for(*module), export, ExportKind::Func) |
| 1272 | } |
| 1273 | |
| 1274 | ShimKind::PayloadFunc { |
| 1275 | for_module, |
| 1276 | async_, |
| 1277 | info, |
| 1278 | kind, |
| 1279 | } => { |
| 1280 | let metadata = self.info.module_metadata_for(*for_module); |
| 1281 | let exports = self.info.exports_for(*for_module); |
| 1282 | let instance_index = self.instance_for(*for_module); |
| 1283 | let (encoding, realloc) = if info.imported { |
| 1284 | ( |
| 1285 | metadata |
| 1286 | .import_encodings |
| 1287 | .get(resolve, &info.key, &info.function.name), |
| 1288 | exports.import_realloc_for(info.interface, &info.function.name), |
| 1289 | ) |
| 1290 | } else { |
| 1291 | ( |
| 1292 | metadata |
| 1293 | .export_encodings |
| 1294 | .get(resolve, &info.key, &info.function.name), |
| 1295 | exports.export_realloc_for(&info.key, &info.function), |
| 1296 | ) |
| 1297 | }; |
| 1298 | let encoding = encoding.unwrap_or(StringEncoding::UTF8); |
| 1299 | let realloc_index = realloc |
| 1300 | .map(|name| self.core_alias_export(instance_index, name, ExportKind::Func)); |
| 1301 | let options = |me: &mut Self, params: Vec<Type>, results: Vec<Type>| { |
| 1302 | Ok::<_, anyhow::Error>( |
| 1303 | (RequiredOptions::for_import( |
| 1304 | resolve, |
| 1305 | &Function { |
| 1306 | name: String::new(), |
| 1307 | kind: FunctionKind::Freestanding, |
| 1308 | params: params |
| 1309 | .into_iter() |
| 1310 | .enumerate() |
| 1311 | .map(|(i, v)| (format!("a {i}" ), v)) |
| 1312 | .collect(), |
| 1313 | results: match &results[..] { |
| 1314 | [] => Results::Named(Vec::new()), |
| 1315 | [ty] => Results::Anon(*ty), |
| 1316 | _ => unreachable!(), |
| 1317 | }, |
| 1318 | docs: Default::default(), |
| 1319 | stability: Stability::Unknown, |
| 1320 | }, |
| 1321 | if *async_ { |
| 1322 | AbiVariant::GuestImportAsync |
| 1323 | } else { |
| 1324 | AbiVariant::GuestImport |
| 1325 | }, |
| 1326 | ) | RequiredOptions::MEMORY) |
| 1327 | .into_iter(encoding, me.memory_index, realloc_index)? |
| 1328 | .collect::<Vec<_>>(), |
| 1329 | ) |
| 1330 | }; |
| 1331 | let type_index = self.payload_type_index(info.ty, info.imported)?; |
| 1332 | |
| 1333 | match kind { |
| 1334 | PayloadFuncKind::FutureWrite => { |
| 1335 | let TypeDefKind::Future(payload_type) = &resolve.types[info.ty].kind |
| 1336 | else { |
| 1337 | unreachable!() |
| 1338 | }; |
| 1339 | let options = options( |
| 1340 | self, |
| 1341 | if let Some(payload_type) = payload_type { |
| 1342 | vec![*payload_type] |
| 1343 | } else { |
| 1344 | vec![] |
| 1345 | }, |
| 1346 | vec![], |
| 1347 | )?; |
| 1348 | self.component.future_write(type_index, options) |
| 1349 | } |
| 1350 | PayloadFuncKind::FutureRead => { |
| 1351 | let TypeDefKind::Future(payload_type) = &resolve.types[info.ty].kind |
| 1352 | else { |
| 1353 | unreachable!() |
| 1354 | }; |
| 1355 | let options = options( |
| 1356 | self, |
| 1357 | vec![], |
| 1358 | if let Some(payload_type) = payload_type { |
| 1359 | vec![*payload_type] |
| 1360 | } else { |
| 1361 | vec![] |
| 1362 | }, |
| 1363 | )?; |
| 1364 | self.component.future_read(type_index, options) |
| 1365 | } |
| 1366 | PayloadFuncKind::StreamWrite => { |
| 1367 | let TypeDefKind::Stream(payload_type) = &resolve.types[info.ty].kind |
| 1368 | else { |
| 1369 | unreachable!() |
| 1370 | }; |
| 1371 | let options = options(self, vec![*payload_type], vec![])?; |
| 1372 | self.component.stream_write(type_index, options) |
| 1373 | } |
| 1374 | PayloadFuncKind::StreamRead => { |
| 1375 | let TypeDefKind::Stream(payload_type) = &resolve.types[info.ty].kind |
| 1376 | else { |
| 1377 | unreachable!() |
| 1378 | }; |
| 1379 | let options = options(self, vec![], vec![*payload_type])?; |
| 1380 | self.component.stream_read(type_index, options) |
| 1381 | } |
| 1382 | } |
| 1383 | } |
| 1384 | |
| 1385 | ShimKind::TaskWait { async_ } => self |
| 1386 | .component |
| 1387 | .task_wait(*async_, self.memory_index.unwrap()), |
| 1388 | ShimKind::TaskPoll { async_ } => self |
| 1389 | .component |
| 1390 | .task_poll(*async_, self.memory_index.unwrap()), |
| 1391 | ShimKind::ErrorContextNew { encoding } |
| 1392 | | ShimKind::ErrorContextDebugMessage { encoding, .. } => match &shim.kind { |
| 1393 | ShimKind::ErrorContextNew { .. } => self.component.error_context_new( |
| 1394 | (RequiredOptions::MEMORY | RequiredOptions::STRING_ENCODING) |
| 1395 | .into_iter(*encoding, self.memory_index, None)? |
| 1396 | .collect::<Vec<_>>(), |
| 1397 | ), |
| 1398 | ShimKind::ErrorContextDebugMessage { |
| 1399 | for_module, |
| 1400 | realloc, |
| 1401 | .. |
| 1402 | } => { |
| 1403 | let instance_index = self.instance_for(*for_module); |
| 1404 | let realloc_index = |
| 1405 | Some(self.core_alias_export(instance_index, realloc, ExportKind::Func)); |
| 1406 | |
| 1407 | self.component.error_context_debug_message( |
| 1408 | (RequiredOptions::MEMORY |
| 1409 | | RequiredOptions::STRING_ENCODING |
| 1410 | | RequiredOptions::REALLOC) |
| 1411 | .into_iter(*encoding, self.memory_index, realloc_index)? |
| 1412 | .collect::<Vec<_>>(), |
| 1413 | ) |
| 1414 | } |
| 1415 | _ => unreachable!(), |
| 1416 | }, |
| 1417 | }; |
| 1418 | |
| 1419 | exports.push((shim.name.as_str(), ExportKind::Func, core_func_index)); |
| 1420 | } |
| 1421 | |
| 1422 | let instance_index = self.component.core_instantiate_exports(exports); |
| 1423 | self.component.core_instantiate( |
| 1424 | self.fixups_module_index.expect("must have fixup module" ), |
| 1425 | [("" , ModuleArg::Instance(instance_index))], |
| 1426 | ); |
| 1427 | Ok(()) |
| 1428 | } |
| 1429 | |
| 1430 | /// Encode the specified `stream` or `future` type in the component using |
| 1431 | /// either the `root_import_type_encoder` or the `root_export_type_encoder` |
| 1432 | /// depending on the value of `imported`. |
| 1433 | /// |
| 1434 | /// Note that the payload type `T` of `stream<T>` or `future<T>` may be an |
| 1435 | /// imported or exported type, and that determines the appropriate type |
| 1436 | /// encoder to use. |
| 1437 | fn payload_type_index(&mut self, ty: TypeId, imported: bool) -> Result<u32> { |
| 1438 | // `stream` and `future` types don't have owners, but their payload |
| 1439 | // types (or the payload type of the payload type, etc. in the case of |
| 1440 | // nesting) might have an owner, in which case we need to find that in |
| 1441 | // order to make the types match up e.g. when we're exporting a resource |
| 1442 | // that's used as a payload type. |
| 1443 | fn owner(resolve: &Resolve, ty: TypeId) -> Option<InterfaceId> { |
| 1444 | let def = &resolve.types[ty]; |
| 1445 | match &def.kind { |
| 1446 | TypeDefKind::Future(Some(Type::Id(ty))) => owner(resolve, *ty), |
| 1447 | TypeDefKind::Stream(Type::Id(ty)) => owner(resolve, *ty), |
| 1448 | _ => match &def.owner { |
| 1449 | TypeOwner::World(_) | TypeOwner::None => None, |
| 1450 | TypeOwner::Interface(id) => Some(*id), |
| 1451 | }, |
| 1452 | } |
| 1453 | } |
| 1454 | |
| 1455 | let resolve = &self.info.encoder.metadata.resolve; |
| 1456 | let ComponentValType::Type(type_index) = if imported { |
| 1457 | self.root_import_type_encoder(None) |
| 1458 | } else { |
| 1459 | let owner = owner(resolve, ty); |
| 1460 | self.root_export_type_encoder(owner) |
| 1461 | } |
| 1462 | .encode_valtype(resolve, &Type::Id(ty))? |
| 1463 | else { |
| 1464 | unreachable!() |
| 1465 | }; |
| 1466 | Ok(type_index) |
| 1467 | } |
| 1468 | |
| 1469 | /// This is a helper function that will declare, in the component itself, |
| 1470 | /// all exported resources. |
| 1471 | /// |
| 1472 | /// These resources later on get packaged up into instances and such. The |
| 1473 | /// main thing that this handles is that it registers the right destructor |
| 1474 | /// from `shims`, if needed, for each resource. |
| 1475 | fn declare_exported_resources(&mut self, shims: &Shims<'_>) { |
| 1476 | let resolve = &self.info.encoder.metadata.resolve; |
| 1477 | let world = &resolve.worlds[self.info.encoder.metadata.world]; |
| 1478 | |
| 1479 | // Iterate over the main module's exports and the exports of all |
| 1480 | // adapters. Look for exported interfaces that themselves have |
| 1481 | // resources. |
| 1482 | let main_module_keys = self.info.encoder.main_module_exports.iter(); |
| 1483 | let main_module_keys = main_module_keys.map(|key| (CustomModule::Main, key)); |
| 1484 | let adapter_keys = self.info.encoder.adapters.iter().flat_map(|(name, info)| { |
| 1485 | info.required_exports |
| 1486 | .iter() |
| 1487 | .map(move |key| (CustomModule::Adapter(name), key)) |
| 1488 | }); |
| 1489 | for (for_module, key) in main_module_keys.chain(adapter_keys) { |
| 1490 | let id = match &world.exports[key] { |
| 1491 | WorldItem::Interface { id, .. } => *id, |
| 1492 | WorldItem::Type { .. } => unreachable!(), |
| 1493 | WorldItem::Function(_) => continue, |
| 1494 | }; |
| 1495 | |
| 1496 | for ty in resolve.interfaces[id].types.values() { |
| 1497 | match resolve.types[*ty].kind { |
| 1498 | TypeDefKind::Resource => {} |
| 1499 | _ => continue, |
| 1500 | } |
| 1501 | |
| 1502 | // Load the destructor, previously detected in module |
| 1503 | // validation, if one is present. |
| 1504 | let exports = self.info.exports_for(for_module); |
| 1505 | let dtor = exports.resource_dtor(*ty).map(|name| { |
| 1506 | let name = &shims.shims[&ShimKind::ResourceDtor { |
| 1507 | module: for_module, |
| 1508 | export: name, |
| 1509 | }] |
| 1510 | .name; |
| 1511 | let shim = self.shim_instance_index.unwrap(); |
| 1512 | self.core_alias_export(shim, name, ExportKind::Func) |
| 1513 | }); |
| 1514 | |
| 1515 | // Declare the resource with this destructor and register it in |
| 1516 | // our internal map. This should be the first and only time this |
| 1517 | // type is inserted into this map. |
| 1518 | let resource_idx = self.component.type_resource(ValType::I32, dtor); |
| 1519 | let prev = self.export_type_map.insert(*ty, resource_idx); |
| 1520 | assert!(prev.is_none()); |
| 1521 | } |
| 1522 | } |
| 1523 | } |
| 1524 | |
| 1525 | /// Helper to instantiate the main module and record various results of its |
| 1526 | /// instantiation within `self`. |
| 1527 | fn instantiate_main_module(&mut self, shims: &Shims<'_>) -> Result<()> { |
| 1528 | assert!(self.instance_index.is_none()); |
| 1529 | |
| 1530 | let instance_index = self.instantiate_core_module(shims, CustomModule::Main)?; |
| 1531 | |
| 1532 | if let Some(memory) = self.info.info.exports.memory() { |
| 1533 | self.memory_index = |
| 1534 | Some(self.core_alias_export(instance_index, memory, ExportKind::Memory)); |
| 1535 | } |
| 1536 | |
| 1537 | self.instance_index = Some(instance_index); |
| 1538 | Ok(()) |
| 1539 | } |
| 1540 | |
| 1541 | /// This function will instantiate the specified adapter module, which may |
| 1542 | /// depend on previously-instantiated modules. |
| 1543 | fn instantiate_adapter_module(&mut self, shims: &Shims<'_>, name: &'a str) -> Result<()> { |
| 1544 | let instance = self.instantiate_core_module(shims, CustomModule::Adapter(name))?; |
| 1545 | self.adapter_instances.insert(name, instance); |
| 1546 | Ok(()) |
| 1547 | } |
| 1548 | |
| 1549 | /// Generic helper to instantiate a module. |
| 1550 | /// |
| 1551 | /// The `for_module` provided will have all of its imports satisfied from |
| 1552 | /// either previous instantiations or the `shims` module present. This |
| 1553 | /// iterates over the metadata produced during validation to determine what |
| 1554 | /// hooks up to what import. |
| 1555 | fn instantiate_core_module( |
| 1556 | &mut self, |
| 1557 | shims: &Shims, |
| 1558 | for_module: CustomModule<'_>, |
| 1559 | ) -> Result<u32> { |
| 1560 | let module = self.module_for(for_module); |
| 1561 | |
| 1562 | let mut args = Vec::new(); |
| 1563 | for (core_wasm_name, instance) in self.info.imports_for(for_module).modules() { |
| 1564 | match instance { |
| 1565 | // For import modules that are a "bag of names" iterate over |
| 1566 | // each name and materialize it into this component with the |
| 1567 | // `materialize_import` helper. This is then all bottled up into |
| 1568 | // a bag-of-exports instances which is then used for |
| 1569 | // instantiation. |
| 1570 | ImportInstance::Names(names) => { |
| 1571 | let mut exports = Vec::new(); |
| 1572 | for (name, import) in names { |
| 1573 | let (kind, index) = self |
| 1574 | .materialize_import(&shims, for_module, core_wasm_name, name, import) |
| 1575 | .with_context(|| { |
| 1576 | format!("failed to satisfy import ` {core_wasm_name}:: {name}`" ) |
| 1577 | })?; |
| 1578 | exports.push((name.as_str(), kind, index)); |
| 1579 | } |
| 1580 | let index = self.component.core_instantiate_exports(exports); |
| 1581 | args.push((core_wasm_name.as_str(), ModuleArg::Instance(index))); |
| 1582 | } |
| 1583 | |
| 1584 | // Some imports are entire instances, so use the instance for |
| 1585 | // the module identifier as the import. |
| 1586 | ImportInstance::Whole(which) => { |
| 1587 | let instance = self.instance_for(which.to_custom_module()); |
| 1588 | args.push((core_wasm_name.as_str(), ModuleArg::Instance(instance))); |
| 1589 | } |
| 1590 | } |
| 1591 | } |
| 1592 | |
| 1593 | // And with all arguments prepared now, instantiate the module. |
| 1594 | Ok(self.component.core_instantiate(module, args)) |
| 1595 | } |
| 1596 | |
| 1597 | /// Helper function to materialize an import into a core module within the |
| 1598 | /// component being built. |
| 1599 | /// |
| 1600 | /// This function is called for individual imports and uses the results of |
| 1601 | /// validation, notably the `Import` type, to determine what WIT-level or |
| 1602 | /// component-level construct is being hooked up. |
| 1603 | fn materialize_import( |
| 1604 | &mut self, |
| 1605 | shims: &Shims<'_>, |
| 1606 | for_module: CustomModule<'_>, |
| 1607 | module: &str, |
| 1608 | field: &str, |
| 1609 | import: &'a Import, |
| 1610 | ) -> Result<(ExportKind, u32)> { |
| 1611 | log::trace!("attempting to materialize import of ` {module}:: {field}` for {for_module:?}" ); |
| 1612 | let resolve = &self.info.encoder.metadata.resolve; |
| 1613 | let payload_indirect = |me: &mut Self, async_, info, kind| { |
| 1614 | me.component.core_alias_export( |
| 1615 | me.shim_instance_index.expect("shim should be instantiated" ), |
| 1616 | &shims.shims[&ShimKind::PayloadFunc { |
| 1617 | for_module, |
| 1618 | async_, |
| 1619 | info, |
| 1620 | kind, |
| 1621 | }] |
| 1622 | .name, |
| 1623 | ExportKind::Func, |
| 1624 | ) |
| 1625 | }; |
| 1626 | let name_tmp; |
| 1627 | let (key, name, interface_key, abi) = match import { |
| 1628 | // Main module dependencies on an adapter in use are done with an |
| 1629 | // indirection here, so load the shim function and use that. |
| 1630 | Import::AdapterExport(_) => { |
| 1631 | assert!(self.info.encoder.adapters.contains_key(module)); |
| 1632 | let shim_instance = self |
| 1633 | .shim_instance_index |
| 1634 | .expect("shim should be instantiated" ); |
| 1635 | let index = self.core_alias_export( |
| 1636 | shim_instance, |
| 1637 | &shims.shims[&ShimKind::Adapter { |
| 1638 | adapter: module, |
| 1639 | func: field, |
| 1640 | }] |
| 1641 | .name, |
| 1642 | ExportKind::Func, |
| 1643 | ); |
| 1644 | return Ok((ExportKind::Func, index)); |
| 1645 | } |
| 1646 | |
| 1647 | // Adapters might uset he main module's memory, in which case it |
| 1648 | // should have been previously instantiated. |
| 1649 | Import::MainModuleMemory => { |
| 1650 | let index = self |
| 1651 | .memory_index |
| 1652 | .ok_or_else(|| anyhow!("main module cannot import memory" ))?; |
| 1653 | return Ok((ExportKind::Memory, index)); |
| 1654 | } |
| 1655 | |
| 1656 | // Grab-bag of "this adapter wants this thing from the main module". |
| 1657 | Import::MainModuleExport { name, kind } => { |
| 1658 | let instance = self.instance_index.unwrap(); |
| 1659 | let index = self.core_alias_export(instance, name, *kind); |
| 1660 | return Ok((*kind, index)); |
| 1661 | } |
| 1662 | |
| 1663 | // A similar grab-bag to above but with a slightly different |
| 1664 | // structure. Should probably refactor to make these two the same in |
| 1665 | // the future. |
| 1666 | Import::Item(item) => { |
| 1667 | let instance = self.instance_for(item.which.to_custom_module()); |
| 1668 | let index = self.core_alias_export(instance, &item.name, item.kind); |
| 1669 | return Ok((item.kind, index)); |
| 1670 | } |
| 1671 | |
| 1672 | // Resource intrinsics related to exported resources. Despite being |
| 1673 | // an exported resource the component still provides necessary |
| 1674 | // intrinsics for manipulating resource state. These are all |
| 1675 | // handled here using the resource types created during |
| 1676 | // `declare_exported_resources` above. |
| 1677 | Import::ExportedResourceDrop(_key, id) => { |
| 1678 | let index = self.component.resource_drop(self.export_type_map[id]); |
| 1679 | return Ok((ExportKind::Func, index)); |
| 1680 | } |
| 1681 | Import::ExportedResourceRep(_key, id) => { |
| 1682 | let index = self.component.resource_rep(self.export_type_map[id]); |
| 1683 | return Ok((ExportKind::Func, index)); |
| 1684 | } |
| 1685 | Import::ExportedResourceNew(_key, id) => { |
| 1686 | let index = self.component.resource_new(self.export_type_map[id]); |
| 1687 | return Ok((ExportKind::Func, index)); |
| 1688 | } |
| 1689 | |
| 1690 | // And finally here at the end these cases are going to all fall |
| 1691 | // through to the code below. This is where these are connected to a |
| 1692 | // WIT `ImportedInterface` one way or another with the name that was |
| 1693 | // detected during validation. |
| 1694 | Import::ImportedResourceDrop(key, iface, id) => { |
| 1695 | let ty = &resolve.types[*id]; |
| 1696 | let name = ty.name.as_ref().unwrap(); |
| 1697 | name_tmp = format!(" {name}_drop" ); |
| 1698 | ( |
| 1699 | key, |
| 1700 | &name_tmp, |
| 1701 | iface.map(|_| resolve.name_world_key(key)), |
| 1702 | AbiVariant::GuestImport, |
| 1703 | ) |
| 1704 | } |
| 1705 | Import::ExportedTaskReturn(function) => { |
| 1706 | let signature = resolve.wasm_signature( |
| 1707 | AbiVariant::GuestImport, |
| 1708 | &Function { |
| 1709 | name: String::new(), |
| 1710 | kind: FunctionKind::Freestanding, |
| 1711 | params: match &function.results { |
| 1712 | Results::Named(params) => params.clone(), |
| 1713 | Results::Anon(ty) => vec![("v" .to_string(), *ty)], |
| 1714 | }, |
| 1715 | results: Results::Named(Vec::new()), |
| 1716 | docs: Docs::default(), |
| 1717 | stability: Stability::Unknown, |
| 1718 | }, |
| 1719 | ); |
| 1720 | let (type_index, encoder) = self.component.core_type(); |
| 1721 | encoder.core().function( |
| 1722 | signature.params.into_iter().map(into_val_type), |
| 1723 | signature.results.into_iter().map(into_val_type), |
| 1724 | ); |
| 1725 | |
| 1726 | let index = self.component.task_return(type_index); |
| 1727 | return Ok((ExportKind::Func, index)); |
| 1728 | |
| 1729 | fn into_val_type(ty: WasmType) -> ValType { |
| 1730 | match ty { |
| 1731 | WasmType::I32 | WasmType::Pointer | WasmType::Length => ValType::I32, |
| 1732 | WasmType::I64 | WasmType::PointerOrI64 => ValType::I64, |
| 1733 | WasmType::F32 => ValType::F32, |
| 1734 | WasmType::F64 => ValType::F64, |
| 1735 | } |
| 1736 | } |
| 1737 | } |
| 1738 | Import::TaskBackpressure => { |
| 1739 | let index = self.component.task_backpressure(); |
| 1740 | return Ok((ExportKind::Func, index)); |
| 1741 | } |
| 1742 | Import::TaskWait { async_ } => { |
| 1743 | let index = self.component.core_alias_export( |
| 1744 | self.shim_instance_index |
| 1745 | .expect("shim should be instantiated" ), |
| 1746 | &shims.shims[&ShimKind::TaskWait { async_: *async_ }].name, |
| 1747 | ExportKind::Func, |
| 1748 | ); |
| 1749 | return Ok((ExportKind::Func, index)); |
| 1750 | } |
| 1751 | Import::TaskPoll { async_ } => { |
| 1752 | let index = self.component.core_alias_export( |
| 1753 | self.shim_instance_index |
| 1754 | .expect("shim should be instantiated" ), |
| 1755 | &shims.shims[&ShimKind::TaskPoll { async_: *async_ }].name, |
| 1756 | ExportKind::Func, |
| 1757 | ); |
| 1758 | return Ok((ExportKind::Func, index)); |
| 1759 | } |
| 1760 | Import::TaskYield { async_ } => { |
| 1761 | let index = self.component.task_yield(*async_); |
| 1762 | return Ok((ExportKind::Func, index)); |
| 1763 | } |
| 1764 | Import::SubtaskDrop => { |
| 1765 | let index = self.component.subtask_drop(); |
| 1766 | return Ok((ExportKind::Func, index)); |
| 1767 | } |
| 1768 | Import::StreamNew(info) => { |
| 1769 | let ty = self.payload_type_index(info.ty, info.imported)?; |
| 1770 | let index = self.component.stream_new(ty); |
| 1771 | return Ok((ExportKind::Func, index)); |
| 1772 | } |
| 1773 | Import::StreamRead { async_, info } => { |
| 1774 | return Ok(( |
| 1775 | ExportKind::Func, |
| 1776 | payload_indirect(self, *async_, info, PayloadFuncKind::StreamRead), |
| 1777 | )); |
| 1778 | } |
| 1779 | Import::StreamWrite { async_, info } => { |
| 1780 | return Ok(( |
| 1781 | ExportKind::Func, |
| 1782 | payload_indirect(self, *async_, info, PayloadFuncKind::StreamWrite), |
| 1783 | )); |
| 1784 | } |
| 1785 | Import::StreamCancelRead { |
| 1786 | ty, |
| 1787 | imported, |
| 1788 | async_, |
| 1789 | } => { |
| 1790 | let ty = self.payload_type_index(*ty, *imported)?; |
| 1791 | let index = self.component.stream_cancel_read(ty, *async_); |
| 1792 | return Ok((ExportKind::Func, index)); |
| 1793 | } |
| 1794 | Import::StreamCancelWrite { |
| 1795 | ty, |
| 1796 | imported, |
| 1797 | async_, |
| 1798 | } => { |
| 1799 | let ty = self.payload_type_index(*ty, *imported)?; |
| 1800 | let index = self.component.stream_cancel_write(ty, *async_); |
| 1801 | return Ok((ExportKind::Func, index)); |
| 1802 | } |
| 1803 | Import::StreamCloseReadable { ty, imported } => { |
| 1804 | let type_index = self.payload_type_index(*ty, *imported)?; |
| 1805 | let index = self.component.stream_close_readable(type_index); |
| 1806 | return Ok((ExportKind::Func, index)); |
| 1807 | } |
| 1808 | Import::StreamCloseWritable { ty, imported } => { |
| 1809 | let type_index = self.payload_type_index(*ty, *imported)?; |
| 1810 | let index = self.component.stream_close_writable(type_index); |
| 1811 | return Ok((ExportKind::Func, index)); |
| 1812 | } |
| 1813 | Import::FutureNew(info) => { |
| 1814 | let ty = self.payload_type_index(info.ty, info.imported)?; |
| 1815 | let index = self.component.future_new(ty); |
| 1816 | return Ok((ExportKind::Func, index)); |
| 1817 | } |
| 1818 | Import::FutureRead { async_, info } => { |
| 1819 | return Ok(( |
| 1820 | ExportKind::Func, |
| 1821 | payload_indirect(self, *async_, info, PayloadFuncKind::FutureRead), |
| 1822 | )); |
| 1823 | } |
| 1824 | Import::FutureWrite { async_, info } => { |
| 1825 | return Ok(( |
| 1826 | ExportKind::Func, |
| 1827 | payload_indirect(self, *async_, info, PayloadFuncKind::FutureWrite), |
| 1828 | )); |
| 1829 | } |
| 1830 | Import::FutureCancelRead { |
| 1831 | ty, |
| 1832 | imported, |
| 1833 | async_, |
| 1834 | } => { |
| 1835 | let ty = self.payload_type_index(*ty, *imported)?; |
| 1836 | let index = self.component.future_cancel_read(ty, *async_); |
| 1837 | return Ok((ExportKind::Func, index)); |
| 1838 | } |
| 1839 | Import::FutureCancelWrite { |
| 1840 | ty, |
| 1841 | imported, |
| 1842 | async_, |
| 1843 | } => { |
| 1844 | let ty = self.payload_type_index(*ty, *imported)?; |
| 1845 | let index = self.component.future_cancel_write(ty, *async_); |
| 1846 | return Ok((ExportKind::Func, index)); |
| 1847 | } |
| 1848 | Import::FutureCloseReadable { ty, imported } => { |
| 1849 | let type_index = self.payload_type_index(*ty, *imported)?; |
| 1850 | let index = self.component.future_close_readable(type_index); |
| 1851 | return Ok((ExportKind::Func, index)); |
| 1852 | } |
| 1853 | Import::FutureCloseWritable { ty, imported } => { |
| 1854 | let type_index = self.payload_type_index(*ty, *imported)?; |
| 1855 | let index = self.component.future_close_writable(type_index); |
| 1856 | return Ok((ExportKind::Func, index)); |
| 1857 | } |
| 1858 | Import::ErrorContextNew { encoding } => { |
| 1859 | let index = self.component.core_alias_export( |
| 1860 | self.shim_instance_index |
| 1861 | .expect("shim should be instantiated" ), |
| 1862 | &shims.shims[&ShimKind::ErrorContextNew { |
| 1863 | encoding: *encoding, |
| 1864 | }] |
| 1865 | .name, |
| 1866 | ExportKind::Func, |
| 1867 | ); |
| 1868 | return Ok((ExportKind::Func, index)); |
| 1869 | } |
| 1870 | Import::ErrorContextDebugMessage { encoding, realloc } => { |
| 1871 | let index = self.component.core_alias_export( |
| 1872 | self.shim_instance_index |
| 1873 | .expect("shim should be instantiated" ), |
| 1874 | &shims.shims[&ShimKind::ErrorContextDebugMessage { |
| 1875 | for_module, |
| 1876 | encoding: *encoding, |
| 1877 | realloc, |
| 1878 | }] |
| 1879 | .name, |
| 1880 | ExportKind::Func, |
| 1881 | ); |
| 1882 | return Ok((ExportKind::Func, index)); |
| 1883 | } |
| 1884 | Import::ErrorContextDrop => { |
| 1885 | let index = self.component.error_context_drop(); |
| 1886 | return Ok((ExportKind::Func, index)); |
| 1887 | } |
| 1888 | Import::WorldFunc(key, name, abi) => (key, name, None, *abi), |
| 1889 | Import::InterfaceFunc(key, _, name, abi) => { |
| 1890 | (key, name, Some(resolve.name_world_key(key)), *abi) |
| 1891 | } |
| 1892 | }; |
| 1893 | |
| 1894 | let import = &self.info.import_map[&interface_key]; |
| 1895 | let (index, _, lowering) = import.lowerings.get_full(&(name.clone(), abi)).unwrap(); |
| 1896 | let metadata = self.info.module_metadata_for(for_module); |
| 1897 | |
| 1898 | let index = match lowering { |
| 1899 | // All direct lowerings can be `canon lower`'d here immediately |
| 1900 | // and passed as arguments. |
| 1901 | Lowering::Direct => { |
| 1902 | let func_index = match &import.interface { |
| 1903 | Some(interface) => { |
| 1904 | let instance_index = self.imported_instances[interface]; |
| 1905 | self.component |
| 1906 | .alias_export(instance_index, name, ComponentExportKind::Func) |
| 1907 | } |
| 1908 | None => self.imported_funcs[name], |
| 1909 | }; |
| 1910 | self.component.lower_func( |
| 1911 | func_index, |
| 1912 | if let AbiVariant::GuestImportAsync = abi { |
| 1913 | vec![CanonicalOption::Async] |
| 1914 | } else { |
| 1915 | Vec::new() |
| 1916 | }, |
| 1917 | ) |
| 1918 | } |
| 1919 | |
| 1920 | // Indirect lowerings come from the shim that was previously |
| 1921 | // created, so the specific export is loaded here and used as an |
| 1922 | // import. |
| 1923 | Lowering::Indirect { .. } => { |
| 1924 | let encoding = metadata.import_encodings.get(resolve, key, name).unwrap(); |
| 1925 | self.core_alias_export( |
| 1926 | self.shim_instance_index |
| 1927 | .expect("shim should be instantiated" ), |
| 1928 | &shims.shims[&ShimKind::IndirectLowering { |
| 1929 | interface: interface_key, |
| 1930 | index, |
| 1931 | realloc: for_module, |
| 1932 | encoding, |
| 1933 | }] |
| 1934 | .name, |
| 1935 | ExportKind::Func, |
| 1936 | ) |
| 1937 | } |
| 1938 | |
| 1939 | // A "resource drop" intrinsic only needs to find the index of the |
| 1940 | // resource type itself and then the intrinsic is declared. |
| 1941 | Lowering::ResourceDrop(id) => { |
| 1942 | let resource_idx = self.lookup_resource_index(*id); |
| 1943 | self.component.resource_drop(resource_idx) |
| 1944 | } |
| 1945 | }; |
| 1946 | Ok((ExportKind::Func, index)) |
| 1947 | } |
| 1948 | |
| 1949 | /// Generates component bits that are responsible for executing |
| 1950 | /// `_initialize`, if found, in the original component. |
| 1951 | /// |
| 1952 | /// The `_initialize` function was a part of WASIp1 where it generally is |
| 1953 | /// intended to run after imports and memory and such are all "hooked up" |
| 1954 | /// and performs other various initialization tasks. This is additionally |
| 1955 | /// specified in https://github.com/WebAssembly/component-model/pull/378 |
| 1956 | /// to be part of the component model lowerings as well. |
| 1957 | /// |
| 1958 | /// This implements this functionality by encoding a core module that |
| 1959 | /// imports a function and then registers a `start` section with that |
| 1960 | /// imported function. This is all encoded after the |
| 1961 | /// imports/lowerings/tables/etc are all filled in above meaning that this |
| 1962 | /// is the last piece to run. That means that when this is running |
| 1963 | /// everything should be hooked up for all imported functions to work. |
| 1964 | /// |
| 1965 | /// Note that at this time `_initialize` is only detected in the "main |
| 1966 | /// module", not adapters/libraries. |
| 1967 | fn encode_initialize_with_start(&mut self) -> Result<()> { |
| 1968 | let initialize = match self.info.info.exports.initialize() { |
| 1969 | Some(name) => name, |
| 1970 | // If this core module didn't have `_initialize` or similar, then |
| 1971 | // there's nothing to do here. |
| 1972 | None => return Ok(()), |
| 1973 | }; |
| 1974 | let initialize_index = |
| 1975 | self.core_alias_export(self.instance_index.unwrap(), initialize, ExportKind::Func); |
| 1976 | let mut shim = Module::default(); |
| 1977 | let mut section = TypeSection::new(); |
| 1978 | section.ty().function([], []); |
| 1979 | shim.section(§ion); |
| 1980 | let mut section = ImportSection::new(); |
| 1981 | section.import("" , "" , EntityType::Function(0)); |
| 1982 | shim.section(§ion); |
| 1983 | shim.section(&StartSection { function_index: 0 }); |
| 1984 | |
| 1985 | // Declare the core module within the component, create a dummy core |
| 1986 | // instance with one export of our `_initialize` function, and then use |
| 1987 | // that to instantiate the module we emit to run the `start` function in |
| 1988 | // core wasm to run `_initialize`. |
| 1989 | let shim_module_index = self.component.core_module(&shim); |
| 1990 | let shim_args_instance_index = |
| 1991 | self.component |
| 1992 | .core_instantiate_exports([("" , ExportKind::Func, initialize_index)]); |
| 1993 | self.component.core_instantiate( |
| 1994 | shim_module_index, |
| 1995 | [("" , ModuleArg::Instance(shim_args_instance_index))], |
| 1996 | ); |
| 1997 | Ok(()) |
| 1998 | } |
| 1999 | |
| 2000 | /// Convenience function to go from `CustomModule` to the instance index |
| 2001 | /// corresponding to what that points to. |
| 2002 | fn instance_for(&self, module: CustomModule) -> u32 { |
| 2003 | match module { |
| 2004 | CustomModule::Main => self.instance_index.expect("instantiated by now" ), |
| 2005 | CustomModule::Adapter(name) => self.adapter_instances[name], |
| 2006 | } |
| 2007 | } |
| 2008 | |
| 2009 | /// Convenience function to go from `CustomModule` to the module index |
| 2010 | /// corresponding to what that points to. |
| 2011 | fn module_for(&self, module: CustomModule) -> u32 { |
| 2012 | match module { |
| 2013 | CustomModule::Main => self.module_index.unwrap(), |
| 2014 | CustomModule::Adapter(name) => self.adapter_modules[name], |
| 2015 | } |
| 2016 | } |
| 2017 | |
| 2018 | /// Convenience function which caches aliases created so repeated calls to |
| 2019 | /// this function will all return the same index. |
| 2020 | fn core_alias_export(&mut self, instance: u32, name: &str, kind: ExportKind) -> u32 { |
| 2021 | *self |
| 2022 | .aliased_core_items |
| 2023 | .entry((instance, name.to_string())) |
| 2024 | .or_insert_with(|| self.component.core_alias_export(instance, name, kind)) |
| 2025 | } |
| 2026 | } |
| 2027 | |
| 2028 | /// A list of "shims" which start out during the component instantiation process |
| 2029 | /// as functions which immediately trap due to a `call_indirect`-to-`null` but |
| 2030 | /// will get filled in by the time the component instantiation process |
| 2031 | /// completes. |
| 2032 | /// |
| 2033 | /// Shims currently include: |
| 2034 | /// |
| 2035 | /// * "Indirect functions" lowered from imported instances where the lowering |
| 2036 | /// requires an item exported from the main module. These are indirect due to |
| 2037 | /// the circular dependency between the module needing an import and the |
| 2038 | /// import needing the module. |
| 2039 | /// |
| 2040 | /// * Adapter modules which convert from a historical ABI to the component |
| 2041 | /// model's ABI (e.g. wasi preview1 to preview2) get a shim since the adapters |
| 2042 | /// are currently indicated as always requiring the memory of the main module. |
| 2043 | /// |
| 2044 | /// This structure is created by `encode_shim_instantiation`. |
| 2045 | #[derive (Default)] |
| 2046 | struct Shims<'a> { |
| 2047 | /// The list of all shims that a module will require. |
| 2048 | shims: IndexMap<ShimKind<'a>, Shim<'a>>, |
| 2049 | } |
| 2050 | |
| 2051 | struct Shim<'a> { |
| 2052 | /// Canonical ABI options required by this shim, used during `canon lower` |
| 2053 | /// operations. |
| 2054 | options: RequiredOptions, |
| 2055 | |
| 2056 | /// The name, in the shim instance, of this shim. |
| 2057 | /// |
| 2058 | /// Currently this is `"0"`, `"1"`, ... |
| 2059 | name: String, |
| 2060 | |
| 2061 | /// A human-readable debugging name for this shim, used in a core wasm |
| 2062 | /// `name` section. |
| 2063 | debug_name: String, |
| 2064 | |
| 2065 | /// Precise information about what this shim is a lowering of. |
| 2066 | kind: ShimKind<'a>, |
| 2067 | |
| 2068 | /// Wasm type of this shim. |
| 2069 | sig: WasmSignature, |
| 2070 | } |
| 2071 | |
| 2072 | /// Which variation of `{stream|future}.{read|write}` we're emitting for a |
| 2073 | /// `ShimKind::PayloadFunc`. |
| 2074 | #[derive (Debug, Clone, Hash, Eq, PartialEq)] |
| 2075 | enum PayloadFuncKind { |
| 2076 | FutureWrite, |
| 2077 | FutureRead, |
| 2078 | StreamWrite, |
| 2079 | StreamRead, |
| 2080 | } |
| 2081 | |
| 2082 | #[derive (Debug, Clone, Hash, Eq, PartialEq)] |
| 2083 | enum ShimKind<'a> { |
| 2084 | /// This shim is a late indirect lowering of an imported function in a |
| 2085 | /// component which is only possible after prior core wasm modules are |
| 2086 | /// instantiated so their memories and functions are available. |
| 2087 | IndirectLowering { |
| 2088 | /// The name of the interface that's being lowered. |
| 2089 | interface: Option<String>, |
| 2090 | /// The index within the `lowerings` array of the function being lowered. |
| 2091 | index: usize, |
| 2092 | /// Which instance to pull the `realloc` function from, if necessary. |
| 2093 | realloc: CustomModule<'a>, |
| 2094 | /// The string encoding that this lowering is going to use. |
| 2095 | encoding: StringEncoding, |
| 2096 | }, |
| 2097 | /// This shim is a core wasm function defined in an adapter module but isn't |
| 2098 | /// available until the adapter module is itself instantiated. |
| 2099 | Adapter { |
| 2100 | /// The name of the adapter module this shim comes from. |
| 2101 | adapter: &'a str, |
| 2102 | /// The name of the export in the adapter module this shim points to. |
| 2103 | func: &'a str, |
| 2104 | }, |
| 2105 | /// A shim used as the destructor for a resource which allows defining the |
| 2106 | /// resource before the core module being instantiated. |
| 2107 | ResourceDtor { |
| 2108 | /// Which instance to pull the destructor function from. |
| 2109 | module: CustomModule<'a>, |
| 2110 | /// The exported function name of this destructor in the core module. |
| 2111 | export: &'a str, |
| 2112 | }, |
| 2113 | /// A shim used for a `{stream|future}.{read|write}` built-in function, |
| 2114 | /// which must refer to the core module instance's memory from/to which |
| 2115 | /// payload values must be lifted/lowered. |
| 2116 | PayloadFunc { |
| 2117 | /// Which instance to pull the `realloc` function and string encoding |
| 2118 | /// from, if necessary. |
| 2119 | for_module: CustomModule<'a>, |
| 2120 | /// Whether this read/write call is using the `async` option. |
| 2121 | async_: bool, |
| 2122 | /// Additional information regarding the function where this `stream` or |
| 2123 | /// `future` type appeared, which we use in combination with |
| 2124 | /// `for_module` to determine which `realloc` and string encoding to |
| 2125 | /// use, as well as which type to specify when emitting the built-in. |
| 2126 | info: &'a PayloadInfo, |
| 2127 | /// Which variation of `{stream|future}.{read|write}` we're emitting. |
| 2128 | kind: PayloadFuncKind, |
| 2129 | }, |
| 2130 | /// A shim used for the `task.wait` built-in function, which must refer to |
| 2131 | /// the core module instance's memory to which results will be written. |
| 2132 | TaskWait { async_: bool }, |
| 2133 | /// A shim used for the `task.poll` built-in function, which must refer to |
| 2134 | /// the core module instance's memory to which results will be written. |
| 2135 | TaskPoll { async_: bool }, |
| 2136 | /// A shim used for the `error-context.new` built-in function, which must |
| 2137 | /// refer to the core module instance's memory from which the debug message |
| 2138 | /// will be read. |
| 2139 | ErrorContextNew { |
| 2140 | /// String encoding to use when lifting the debug message. |
| 2141 | encoding: StringEncoding, |
| 2142 | }, |
| 2143 | /// A shim used for the `error-context.debug-message` built-in function, |
| 2144 | /// which must refer to the core module instance's memory to which results |
| 2145 | /// will be written. |
| 2146 | ErrorContextDebugMessage { |
| 2147 | /// Which instance to pull the `realloc` function from, if necessary. |
| 2148 | for_module: CustomModule<'a>, |
| 2149 | /// The string encoding to use when lowering the debug message. |
| 2150 | encoding: StringEncoding, |
| 2151 | /// The realloc function to use when allocating linear memory for the |
| 2152 | /// debug message. |
| 2153 | realloc: &'a str, |
| 2154 | }, |
| 2155 | } |
| 2156 | |
| 2157 | /// Indicator for which module is being used for a lowering or where options |
| 2158 | /// like `realloc` are drawn from. |
| 2159 | /// |
| 2160 | /// This is necessary for situations such as an imported function being lowered |
| 2161 | /// into the main module and additionally into an adapter module. For example an |
| 2162 | /// adapter might adapt from preview1 to preview2 for the standard library of a |
| 2163 | /// programming language but the main module's custom application code may also |
| 2164 | /// explicitly import from preview2. These two different lowerings of a preview2 |
| 2165 | /// function are parameterized by this enumeration. |
| 2166 | #[derive (Debug, Copy, Clone, Hash, Eq, PartialEq)] |
| 2167 | enum CustomModule<'a> { |
| 2168 | /// This points to the "main module" which is generally the "output of LLVM" |
| 2169 | /// or what a user wrote. |
| 2170 | Main, |
| 2171 | /// This is selecting an adapter module, identified by name here, where |
| 2172 | /// something is being lowered into. |
| 2173 | Adapter(&'a str), |
| 2174 | } |
| 2175 | |
| 2176 | impl<'a> Shims<'a> { |
| 2177 | /// Adds all shims necessary for the instantiation of `for_module`. |
| 2178 | /// |
| 2179 | /// This function will iterate over all the imports required by this module |
| 2180 | /// and for those that require a shim they're registered here. |
| 2181 | fn append_indirect( |
| 2182 | &mut self, |
| 2183 | world: &'a ComponentWorld<'a>, |
| 2184 | for_module: CustomModule<'a>, |
| 2185 | ) -> Result<()> { |
| 2186 | let module_imports = world.imports_for(for_module); |
| 2187 | let module_exports = world.exports_for(for_module); |
| 2188 | let metadata = world.module_metadata_for(for_module); |
| 2189 | let resolve = &world.encoder.metadata.resolve; |
| 2190 | |
| 2191 | let payload_push = |
| 2192 | |me: &mut Self, module, async_, info: &'a PayloadInfo, kind, params, results| { |
| 2193 | let debug_name = format!(" {module}- {}" , info.name); |
| 2194 | let name = me.shims.len().to_string(); |
| 2195 | me.push(Shim { |
| 2196 | name, |
| 2197 | debug_name, |
| 2198 | options: RequiredOptions::empty(), |
| 2199 | kind: ShimKind::PayloadFunc { |
| 2200 | for_module, |
| 2201 | async_, |
| 2202 | info, |
| 2203 | kind, |
| 2204 | }, |
| 2205 | sig: WasmSignature { |
| 2206 | params, |
| 2207 | results, |
| 2208 | indirect_params: false, |
| 2209 | retptr: false, |
| 2210 | }, |
| 2211 | }); |
| 2212 | }; |
| 2213 | |
| 2214 | for (module, field, import) in module_imports.imports() { |
| 2215 | let (key, name, interface_key, abi) = match import { |
| 2216 | // These imports don't require shims, they can be satisfied |
| 2217 | // as-needed when required. |
| 2218 | Import::ImportedResourceDrop(..) |
| 2219 | | Import::MainModuleMemory |
| 2220 | | Import::MainModuleExport { .. } |
| 2221 | | Import::Item(_) |
| 2222 | | Import::ExportedResourceDrop(..) |
| 2223 | | Import::ExportedResourceRep(..) |
| 2224 | | Import::ExportedResourceNew(..) |
| 2225 | | Import::ErrorContextDrop |
| 2226 | | Import::TaskBackpressure |
| 2227 | | Import::TaskYield { .. } |
| 2228 | | Import::SubtaskDrop |
| 2229 | | Import::ExportedTaskReturn(..) |
| 2230 | | Import::FutureNew(..) |
| 2231 | | Import::StreamNew(..) |
| 2232 | | Import::FutureCancelRead { .. } |
| 2233 | | Import::FutureCancelWrite { .. } |
| 2234 | | Import::FutureCloseWritable { .. } |
| 2235 | | Import::FutureCloseReadable { .. } |
| 2236 | | Import::StreamCancelRead { .. } |
| 2237 | | Import::StreamCancelWrite { .. } |
| 2238 | | Import::StreamCloseWritable { .. } |
| 2239 | | Import::StreamCloseReadable { .. } => continue, |
| 2240 | |
| 2241 | Import::FutureWrite { async_, info } => { |
| 2242 | payload_push( |
| 2243 | self, |
| 2244 | module, |
| 2245 | *async_, |
| 2246 | info, |
| 2247 | PayloadFuncKind::FutureWrite, |
| 2248 | vec![WasmType::I32; 2], |
| 2249 | vec![WasmType::I32], |
| 2250 | ); |
| 2251 | continue; |
| 2252 | } |
| 2253 | Import::FutureRead { async_, info } => { |
| 2254 | payload_push( |
| 2255 | self, |
| 2256 | module, |
| 2257 | *async_, |
| 2258 | info, |
| 2259 | PayloadFuncKind::FutureRead, |
| 2260 | vec![WasmType::I32; 2], |
| 2261 | vec![WasmType::I32], |
| 2262 | ); |
| 2263 | continue; |
| 2264 | } |
| 2265 | Import::StreamWrite { async_, info } => { |
| 2266 | payload_push( |
| 2267 | self, |
| 2268 | module, |
| 2269 | *async_, |
| 2270 | info, |
| 2271 | PayloadFuncKind::StreamWrite, |
| 2272 | vec![WasmType::I32; 3], |
| 2273 | vec![WasmType::I32], |
| 2274 | ); |
| 2275 | continue; |
| 2276 | } |
| 2277 | Import::StreamRead { async_, info } => { |
| 2278 | payload_push( |
| 2279 | self, |
| 2280 | module, |
| 2281 | *async_, |
| 2282 | info, |
| 2283 | PayloadFuncKind::StreamRead, |
| 2284 | vec![WasmType::I32; 3], |
| 2285 | vec![WasmType::I32], |
| 2286 | ); |
| 2287 | continue; |
| 2288 | } |
| 2289 | |
| 2290 | Import::TaskWait { async_ } => { |
| 2291 | let name = self.shims.len().to_string(); |
| 2292 | self.push(Shim { |
| 2293 | name, |
| 2294 | debug_name: "task-wait" .to_string(), |
| 2295 | options: RequiredOptions::empty(), |
| 2296 | kind: ShimKind::TaskWait { async_: *async_ }, |
| 2297 | sig: WasmSignature { |
| 2298 | params: vec![WasmType::I32], |
| 2299 | results: vec![WasmType::I32], |
| 2300 | indirect_params: false, |
| 2301 | retptr: false, |
| 2302 | }, |
| 2303 | }); |
| 2304 | continue; |
| 2305 | } |
| 2306 | |
| 2307 | Import::TaskPoll { async_ } => { |
| 2308 | let name = self.shims.len().to_string(); |
| 2309 | self.push(Shim { |
| 2310 | name, |
| 2311 | debug_name: "task-poll" .to_string(), |
| 2312 | options: RequiredOptions::empty(), |
| 2313 | kind: ShimKind::TaskPoll { async_: *async_ }, |
| 2314 | sig: WasmSignature { |
| 2315 | params: vec![WasmType::I32], |
| 2316 | results: vec![WasmType::I32], |
| 2317 | indirect_params: false, |
| 2318 | retptr: false, |
| 2319 | }, |
| 2320 | }); |
| 2321 | continue; |
| 2322 | } |
| 2323 | |
| 2324 | Import::ErrorContextNew { encoding } => { |
| 2325 | let name = self.shims.len().to_string(); |
| 2326 | self.push(Shim { |
| 2327 | name, |
| 2328 | debug_name: "error-new" .to_string(), |
| 2329 | options: RequiredOptions::empty(), |
| 2330 | kind: ShimKind::ErrorContextNew { |
| 2331 | encoding: *encoding, |
| 2332 | }, |
| 2333 | sig: WasmSignature { |
| 2334 | params: vec![WasmType::I32; 2], |
| 2335 | results: vec![WasmType::I32], |
| 2336 | indirect_params: false, |
| 2337 | retptr: false, |
| 2338 | }, |
| 2339 | }); |
| 2340 | continue; |
| 2341 | } |
| 2342 | |
| 2343 | Import::ErrorContextDebugMessage { encoding, realloc } => { |
| 2344 | let name = self.shims.len().to_string(); |
| 2345 | self.push(Shim { |
| 2346 | name, |
| 2347 | debug_name: "error-debug-message" .to_string(), |
| 2348 | options: RequiredOptions::empty(), |
| 2349 | kind: ShimKind::ErrorContextDebugMessage { |
| 2350 | for_module, |
| 2351 | encoding: *encoding, |
| 2352 | realloc, |
| 2353 | }, |
| 2354 | sig: WasmSignature { |
| 2355 | params: vec![WasmType::I32; 2], |
| 2356 | results: vec![], |
| 2357 | indirect_params: false, |
| 2358 | retptr: false, |
| 2359 | }, |
| 2360 | }); |
| 2361 | continue; |
| 2362 | } |
| 2363 | |
| 2364 | // Adapter imports into the main module must got through an |
| 2365 | // indirection, so that's registered here. |
| 2366 | Import::AdapterExport(ty) => { |
| 2367 | let name = self.shims.len().to_string(); |
| 2368 | log::debug!("shim {name} is adapter ` {module}:: {field}`" ); |
| 2369 | self.push(Shim { |
| 2370 | name, |
| 2371 | debug_name: format!("adapt- {module}- {field}" ), |
| 2372 | // Pessimistically assume that all adapters require |
| 2373 | // memory in one form or another. While this isn't |
| 2374 | // technically true it's true enough for WASI. |
| 2375 | options: RequiredOptions::MEMORY, |
| 2376 | kind: ShimKind::Adapter { |
| 2377 | adapter: module, |
| 2378 | func: field, |
| 2379 | }, |
| 2380 | sig: WasmSignature { |
| 2381 | params: ty.params().iter().map(to_wasm_type).collect(), |
| 2382 | results: ty.results().iter().map(to_wasm_type).collect(), |
| 2383 | indirect_params: false, |
| 2384 | retptr: false, |
| 2385 | }, |
| 2386 | }); |
| 2387 | continue; |
| 2388 | |
| 2389 | fn to_wasm_type(ty: &wasmparser::ValType) -> WasmType { |
| 2390 | match ty { |
| 2391 | wasmparser::ValType::I32 => WasmType::I32, |
| 2392 | wasmparser::ValType::I64 => WasmType::I64, |
| 2393 | wasmparser::ValType::F32 => WasmType::F32, |
| 2394 | wasmparser::ValType::F64 => WasmType::F64, |
| 2395 | _ => unreachable!(), |
| 2396 | } |
| 2397 | } |
| 2398 | } |
| 2399 | |
| 2400 | // WIT-level functions may require an indirection, so yield some |
| 2401 | // metadata out of this `match` to the loop below to figure that |
| 2402 | // out. |
| 2403 | Import::InterfaceFunc(key, _, name, abi) => { |
| 2404 | (key, name, Some(resolve.name_world_key(key)), *abi) |
| 2405 | } |
| 2406 | Import::WorldFunc(key, name, abi) => (key, name, None, *abi), |
| 2407 | }; |
| 2408 | let interface = &world.import_map[&interface_key]; |
| 2409 | let (index, _, lowering) = interface.lowerings.get_full(&(name.clone(), abi)).unwrap(); |
| 2410 | let shim_name = self.shims.len().to_string(); |
| 2411 | match lowering { |
| 2412 | Lowering::Direct | Lowering::ResourceDrop(_) => {} |
| 2413 | |
| 2414 | Lowering::Indirect { sig, options } => { |
| 2415 | log::debug!( |
| 2416 | "shim {shim_name} is import ` {module}:: {field}` lowering {index} ` {name}`" , |
| 2417 | ); |
| 2418 | let encoding = metadata |
| 2419 | .import_encodings |
| 2420 | .get(resolve, key, name) |
| 2421 | .ok_or_else(|| { |
| 2422 | anyhow::anyhow!( |
| 2423 | "missing component metadata for import of \ |
| 2424 | ` {module}:: {field}`" |
| 2425 | ) |
| 2426 | })?; |
| 2427 | self.push(Shim { |
| 2428 | name: shim_name, |
| 2429 | debug_name: format!("indirect- {module}- {field}" ), |
| 2430 | options: *options, |
| 2431 | kind: ShimKind::IndirectLowering { |
| 2432 | interface: interface_key, |
| 2433 | index, |
| 2434 | realloc: for_module, |
| 2435 | encoding, |
| 2436 | }, |
| 2437 | sig: sig.clone(), |
| 2438 | }); |
| 2439 | } |
| 2440 | } |
| 2441 | } |
| 2442 | |
| 2443 | // In addition to all the shims added for imports above this module also |
| 2444 | // requires shims for resource destructors that it exports. Resource |
| 2445 | // types are declared before the module is instantiated so the actual |
| 2446 | // destructor is registered as a shim (defined here) and it's then |
| 2447 | // filled in with the module's exports later. |
| 2448 | for (export_name, export) in module_exports.iter() { |
| 2449 | let id = match export { |
| 2450 | Export::ResourceDtor(id) => id, |
| 2451 | _ => continue, |
| 2452 | }; |
| 2453 | let resource = resolve.types[*id].name.as_ref().unwrap(); |
| 2454 | let name = self.shims.len().to_string(); |
| 2455 | self.push(Shim { |
| 2456 | name, |
| 2457 | debug_name: format!("dtor- {resource}" ), |
| 2458 | options: RequiredOptions::empty(), |
| 2459 | kind: ShimKind::ResourceDtor { |
| 2460 | module: for_module, |
| 2461 | export: export_name, |
| 2462 | }, |
| 2463 | sig: WasmSignature { |
| 2464 | params: vec![WasmType::I32], |
| 2465 | results: Vec::new(), |
| 2466 | indirect_params: false, |
| 2467 | retptr: false, |
| 2468 | }, |
| 2469 | }); |
| 2470 | } |
| 2471 | |
| 2472 | Ok(()) |
| 2473 | } |
| 2474 | |
| 2475 | fn push(&mut self, shim: Shim<'a>) { |
| 2476 | // Only one shim per `ShimKind` is retained, so if it's already present |
| 2477 | // don't overwrite it. If it's not present though go ahead and insert |
| 2478 | // it. |
| 2479 | if !self.shims.contains_key(&shim.kind) { |
| 2480 | self.shims.insert(shim.kind.clone(), shim); |
| 2481 | } |
| 2482 | } |
| 2483 | } |
| 2484 | |
| 2485 | /// Alias argument to an instantiation |
| 2486 | #[derive (Clone, Debug)] |
| 2487 | pub struct Item { |
| 2488 | pub alias: String, |
| 2489 | pub kind: ExportKind, |
| 2490 | pub which: MainOrAdapter, |
| 2491 | pub name: String, |
| 2492 | } |
| 2493 | |
| 2494 | /// Module argument to an instantiation |
| 2495 | #[derive (Debug, PartialEq, Clone)] |
| 2496 | pub enum MainOrAdapter { |
| 2497 | Main, |
| 2498 | Adapter(String), |
| 2499 | } |
| 2500 | |
| 2501 | impl MainOrAdapter { |
| 2502 | fn to_custom_module(&self) -> CustomModule<'_> { |
| 2503 | match self { |
| 2504 | MainOrAdapter::Main => CustomModule::Main, |
| 2505 | MainOrAdapter::Adapter(s: &String) => CustomModule::Adapter(s), |
| 2506 | } |
| 2507 | } |
| 2508 | } |
| 2509 | |
| 2510 | /// Module instantiation argument |
| 2511 | #[derive (Clone)] |
| 2512 | pub enum Instance { |
| 2513 | /// Module argument |
| 2514 | MainOrAdapter(MainOrAdapter), |
| 2515 | |
| 2516 | /// Alias argument |
| 2517 | Items(Vec<Item>), |
| 2518 | } |
| 2519 | |
| 2520 | /// Provides fine-grained control of how a library module is instantiated |
| 2521 | /// relative to other module instances |
| 2522 | #[derive (Clone)] |
| 2523 | pub struct LibraryInfo { |
| 2524 | /// If true, instantiate any shims prior to this module |
| 2525 | pub instantiate_after_shims: bool, |
| 2526 | |
| 2527 | /// Instantiation arguments |
| 2528 | pub arguments: Vec<(String, Instance)>, |
| 2529 | } |
| 2530 | |
| 2531 | /// Represents an adapter or library to be instantiated as part of the component |
| 2532 | pub(super) struct Adapter { |
| 2533 | /// The wasm of the module itself, with `component-type` sections stripped |
| 2534 | wasm: Vec<u8>, |
| 2535 | |
| 2536 | /// The metadata for the adapter |
| 2537 | metadata: ModuleMetadata, |
| 2538 | |
| 2539 | /// The set of exports from the final world which are defined by this |
| 2540 | /// adapter or library |
| 2541 | required_exports: IndexSet<WorldKey>, |
| 2542 | |
| 2543 | /// If present, treat this module as a library rather than a "minimal" adapter |
| 2544 | /// |
| 2545 | /// TODO: We should refactor how various flavors of module are represented |
| 2546 | /// and differentiated to avoid mistaking one for another. |
| 2547 | library_info: Option<LibraryInfo>, |
| 2548 | } |
| 2549 | |
| 2550 | /// An encoder of components based on `wit` interface definitions. |
| 2551 | #[derive (Default)] |
| 2552 | pub struct ComponentEncoder { |
| 2553 | module: Vec<u8>, |
| 2554 | pub(super) metadata: Bindgen, |
| 2555 | validate: bool, |
| 2556 | pub(super) main_module_exports: IndexSet<WorldKey>, |
| 2557 | pub(super) adapters: IndexMap<String, Adapter>, |
| 2558 | import_name_map: HashMap<String, String>, |
| 2559 | realloc_via_memory_grow: bool, |
| 2560 | merge_imports_based_on_semver: Option<bool>, |
| 2561 | pub(super) reject_legacy_names: bool, |
| 2562 | } |
| 2563 | |
| 2564 | impl ComponentEncoder { |
| 2565 | /// Set the core module to encode as a component. |
| 2566 | /// This method will also parse any component type information stored in custom sections |
| 2567 | /// inside the module, and add them as the interface, imports, and exports. |
| 2568 | /// It will also add any producers information inside the component type information to the |
| 2569 | /// core module. |
| 2570 | pub fn module(mut self, module: &[u8]) -> Result<Self> { |
| 2571 | let (wasm, metadata) = self.decode(module)?; |
| 2572 | let exports = self |
| 2573 | .merge_metadata(metadata) |
| 2574 | .context("failed merge WIT metadata for module with previous metadata" )?; |
| 2575 | self.main_module_exports.extend(exports); |
| 2576 | self.module = if let Some(producers) = &self.metadata.producers { |
| 2577 | producers.add_to_wasm(&wasm)? |
| 2578 | } else { |
| 2579 | wasm.to_vec() |
| 2580 | }; |
| 2581 | Ok(self) |
| 2582 | } |
| 2583 | |
| 2584 | fn decode<'a>(&self, wasm: &'a [u8]) -> Result<(Cow<'a, [u8]>, Bindgen)> { |
| 2585 | let (bytes, metadata) = metadata::decode(wasm)?; |
| 2586 | match bytes { |
| 2587 | Some(wasm) => Ok((Cow::Owned(wasm), metadata)), |
| 2588 | None => Ok((Cow::Borrowed(wasm), metadata)), |
| 2589 | } |
| 2590 | } |
| 2591 | |
| 2592 | fn merge_metadata(&mut self, metadata: Bindgen) -> Result<IndexSet<WorldKey>> { |
| 2593 | self.metadata.merge(metadata) |
| 2594 | } |
| 2595 | |
| 2596 | /// Sets whether or not the encoder will validate its output. |
| 2597 | pub fn validate(mut self, validate: bool) -> Self { |
| 2598 | self.validate = validate; |
| 2599 | self |
| 2600 | } |
| 2601 | |
| 2602 | /// Sets whether to merge imports based on semver to the specified value. |
| 2603 | /// |
| 2604 | /// This affects how when to WIT worlds are merged together, for example |
| 2605 | /// from two different libraries, whether their imports are unified when the |
| 2606 | /// semver version ranges for interface allow it. |
| 2607 | /// |
| 2608 | /// This is enabled by default. |
| 2609 | pub fn merge_imports_based_on_semver(mut self, merge: bool) -> Self { |
| 2610 | self.merge_imports_based_on_semver = Some(merge); |
| 2611 | self |
| 2612 | } |
| 2613 | |
| 2614 | /// Sets whether to reject the historical mangling/name scheme for core wasm |
| 2615 | /// imports/exports as they map to the component model. |
| 2616 | /// |
| 2617 | /// The `wit-component` crate supported a different set of names prior to |
| 2618 | /// WebAssembly/component-model#378 and this can be used to disable this |
| 2619 | /// support. |
| 2620 | /// |
| 2621 | /// This is disabled by default. |
| 2622 | pub fn reject_legacy_names(mut self, reject: bool) -> Self { |
| 2623 | self.reject_legacy_names = reject; |
| 2624 | self |
| 2625 | } |
| 2626 | |
| 2627 | /// Specifies a new adapter which is used to translate from a historical |
| 2628 | /// wasm ABI to the canonical ABI and the `interface` provided. |
| 2629 | /// |
| 2630 | /// This is primarily used to polyfill, for example, |
| 2631 | /// `wasi_snapshot_preview1` with a component-model using interface. The |
| 2632 | /// `name` provided is the module name of the adapter that is being |
| 2633 | /// polyfilled, for example `"wasi_snapshot_preview1"`. |
| 2634 | /// |
| 2635 | /// The `bytes` provided is a core wasm module which implements the `name` |
| 2636 | /// interface in terms of the `interface` interface. This core wasm module |
| 2637 | /// is severely restricted in its shape, for example it cannot have any data |
| 2638 | /// segments or element segments. |
| 2639 | /// |
| 2640 | /// The `interface` provided is the component-model-using-interface that the |
| 2641 | /// wasm module specified by `bytes` imports. The `bytes` will then import |
| 2642 | /// `interface` and export functions to get imported from the module `name` |
| 2643 | /// in the core wasm that's being wrapped. |
| 2644 | pub fn adapter(self, name: &str, bytes: &[u8]) -> Result<Self> { |
| 2645 | self.library_or_adapter(name, bytes, None) |
| 2646 | } |
| 2647 | |
| 2648 | /// Specifies a shared-everything library to link into the component. |
| 2649 | /// |
| 2650 | /// Unlike adapters, libraries _may_ have data and/or element segments, but |
| 2651 | /// they must operate on an imported memory and table, respectively. In |
| 2652 | /// this case, the correct amount of space is presumed to have been |
| 2653 | /// statically allocated in the main module's memory and table at the |
| 2654 | /// offsets which the segments target, e.g. as arranged by |
| 2655 | /// [super::linking::Linker]. |
| 2656 | /// |
| 2657 | /// Libraries are treated similarly to adapters, except that they are not |
| 2658 | /// "minified" the way adapters are, and instantiation is controlled |
| 2659 | /// declaratively via the `library_info` parameter. |
| 2660 | pub fn library(self, name: &str, bytes: &[u8], library_info: LibraryInfo) -> Result<Self> { |
| 2661 | self.library_or_adapter(name, bytes, Some(library_info)) |
| 2662 | } |
| 2663 | |
| 2664 | fn library_or_adapter( |
| 2665 | mut self, |
| 2666 | name: &str, |
| 2667 | bytes: &[u8], |
| 2668 | library_info: Option<LibraryInfo>, |
| 2669 | ) -> Result<Self> { |
| 2670 | let (wasm, mut metadata) = self.decode(bytes)?; |
| 2671 | // Merge the adapter's document into our own document to have one large |
| 2672 | // document, and then afterwards merge worlds as well. |
| 2673 | // |
| 2674 | // Note that the `metadata` tracking import/export encodings is removed |
| 2675 | // since this adapter can get different lowerings and is allowed to |
| 2676 | // differ from the main module. This is then tracked within the |
| 2677 | // `Adapter` structure produced below. |
| 2678 | let adapter_metadata = mem::take(&mut metadata.metadata); |
| 2679 | let exports = self.merge_metadata(metadata).with_context(|| { |
| 2680 | format!("failed to merge WIT packages of adapter ` {name}` into main packages" ) |
| 2681 | })?; |
| 2682 | if let Some(library_info) = &library_info { |
| 2683 | // Validate that all referenced modules can be resolved. |
| 2684 | for (_, instance) in &library_info.arguments { |
| 2685 | let resolve = |which: &_| match which { |
| 2686 | MainOrAdapter::Main => Ok(()), |
| 2687 | MainOrAdapter::Adapter(name) => { |
| 2688 | if self.adapters.contains_key(name.as_str()) { |
| 2689 | Ok(()) |
| 2690 | } else { |
| 2691 | Err(anyhow!("instance refers to unknown adapter ` {name}`" )) |
| 2692 | } |
| 2693 | } |
| 2694 | }; |
| 2695 | |
| 2696 | match instance { |
| 2697 | Instance::MainOrAdapter(which) => resolve(which)?, |
| 2698 | Instance::Items(items) => { |
| 2699 | for item in items { |
| 2700 | resolve(&item.which)?; |
| 2701 | } |
| 2702 | } |
| 2703 | } |
| 2704 | } |
| 2705 | } |
| 2706 | self.adapters.insert( |
| 2707 | name.to_string(), |
| 2708 | Adapter { |
| 2709 | wasm: wasm.to_vec(), |
| 2710 | metadata: adapter_metadata, |
| 2711 | required_exports: exports, |
| 2712 | library_info, |
| 2713 | }, |
| 2714 | ); |
| 2715 | Ok(self) |
| 2716 | } |
| 2717 | |
| 2718 | /// True if the realloc and stack allocation should use memory.grow |
| 2719 | /// The default is to use the main module realloc |
| 2720 | /// Can be useful if cabi_realloc cannot be called before the host |
| 2721 | /// runtime is initialized. |
| 2722 | pub fn realloc_via_memory_grow(mut self, value: bool) -> Self { |
| 2723 | self.realloc_via_memory_grow = value; |
| 2724 | self |
| 2725 | } |
| 2726 | |
| 2727 | /// The instance import name map to use. |
| 2728 | /// |
| 2729 | /// This is used to rename instance imports in the final component. |
| 2730 | /// |
| 2731 | /// For example, if there is an instance import `foo:bar/baz` and it is |
| 2732 | /// desired that the import actually be an `unlocked-dep` name, then |
| 2733 | /// `foo:bar/baz` can be mapped to `unlocked-dep=<a:b/c@{>=x.y.z}>`. |
| 2734 | /// |
| 2735 | /// Note: the replacement names are not validated during encoding unless |
| 2736 | /// the `validate` option is set to true. |
| 2737 | pub fn import_name_map(mut self, map: HashMap<String, String>) -> Self { |
| 2738 | self.import_name_map = map; |
| 2739 | self |
| 2740 | } |
| 2741 | |
| 2742 | /// Encode the component and return the bytes. |
| 2743 | pub fn encode(&mut self) -> Result<Vec<u8>> { |
| 2744 | if self.module.is_empty() { |
| 2745 | bail!("a module is required when encoding a component" ); |
| 2746 | } |
| 2747 | |
| 2748 | if self.merge_imports_based_on_semver.unwrap_or(true) { |
| 2749 | self.metadata |
| 2750 | .resolve |
| 2751 | .merge_world_imports_based_on_semver(self.metadata.world)?; |
| 2752 | } |
| 2753 | |
| 2754 | let world = ComponentWorld::new(self).context("failed to decode world from module" )?; |
| 2755 | let mut state = EncodingState { |
| 2756 | component: ComponentBuilder::default(), |
| 2757 | module_index: None, |
| 2758 | instance_index: None, |
| 2759 | memory_index: None, |
| 2760 | shim_instance_index: None, |
| 2761 | fixups_module_index: None, |
| 2762 | adapter_modules: IndexMap::new(), |
| 2763 | adapter_instances: IndexMap::new(), |
| 2764 | import_type_map: HashMap::new(), |
| 2765 | import_func_type_map: HashMap::new(), |
| 2766 | export_type_map: HashMap::new(), |
| 2767 | export_func_type_map: HashMap::new(), |
| 2768 | imported_instances: Default::default(), |
| 2769 | imported_funcs: Default::default(), |
| 2770 | exported_instances: Default::default(), |
| 2771 | aliased_core_items: Default::default(), |
| 2772 | info: &world, |
| 2773 | }; |
| 2774 | state.encode_imports(&self.import_name_map)?; |
| 2775 | state.encode_core_modules(); |
| 2776 | state.encode_core_instantiation()?; |
| 2777 | state.encode_exports(CustomModule::Main)?; |
| 2778 | for name in self.adapters.keys() { |
| 2779 | state.encode_exports(CustomModule::Adapter(name))?; |
| 2780 | } |
| 2781 | state |
| 2782 | .component |
| 2783 | .raw_custom_section(&crate::base_producers().raw_custom_section()); |
| 2784 | let bytes = state.component.finish(); |
| 2785 | |
| 2786 | if self.validate { |
| 2787 | Validator::new() |
| 2788 | .validate_all(&bytes) |
| 2789 | .context("failed to validate component output" )?; |
| 2790 | } |
| 2791 | |
| 2792 | Ok(bytes) |
| 2793 | } |
| 2794 | } |
| 2795 | |
| 2796 | impl ComponentWorld<'_> { |
| 2797 | /// Convenience function to lookup a module's import map. |
| 2798 | fn imports_for(&self, module: CustomModule) -> &ImportMap { |
| 2799 | match module { |
| 2800 | CustomModule::Main => &self.info.imports, |
| 2801 | CustomModule::Adapter(name) => &self.adapters[name].info.imports, |
| 2802 | } |
| 2803 | } |
| 2804 | |
| 2805 | /// Convenience function to lookup a module's export map. |
| 2806 | fn exports_for(&self, module: CustomModule) -> &ExportMap { |
| 2807 | match module { |
| 2808 | CustomModule::Main => &self.info.exports, |
| 2809 | CustomModule::Adapter(name) => &self.adapters[name].info.exports, |
| 2810 | } |
| 2811 | } |
| 2812 | |
| 2813 | /// Convenience function to lookup a module's metadata. |
| 2814 | fn module_metadata_for(&self, module: CustomModule) -> &ModuleMetadata { |
| 2815 | match module { |
| 2816 | CustomModule::Main => &self.encoder.metadata.metadata, |
| 2817 | CustomModule::Adapter(name) => &self.encoder.adapters[name].metadata, |
| 2818 | } |
| 2819 | } |
| 2820 | } |
| 2821 | |
| 2822 | #[cfg (all(test, feature = "dummy-module" ))] |
| 2823 | mod test { |
| 2824 | use super::*; |
| 2825 | use crate::{dummy_module, embed_component_metadata}; |
| 2826 | use wit_parser::ManglingAndAbi; |
| 2827 | |
| 2828 | #[test ] |
| 2829 | fn it_renames_imports() { |
| 2830 | let mut resolve = Resolve::new(); |
| 2831 | let pkg = resolve |
| 2832 | .push_str( |
| 2833 | "test.wit" , |
| 2834 | r#" |
| 2835 | package test:wit; |
| 2836 | |
| 2837 | interface i { |
| 2838 | f: func(); |
| 2839 | } |
| 2840 | |
| 2841 | world test { |
| 2842 | import i; |
| 2843 | import foo: interface { |
| 2844 | f: func(); |
| 2845 | } |
| 2846 | } |
| 2847 | "# , |
| 2848 | ) |
| 2849 | .unwrap(); |
| 2850 | let world = resolve.select_world(pkg, None).unwrap(); |
| 2851 | |
| 2852 | let mut module = dummy_module(&resolve, world, ManglingAndAbi::Standard32); |
| 2853 | |
| 2854 | embed_component_metadata(&mut module, &resolve, world, StringEncoding::UTF8).unwrap(); |
| 2855 | |
| 2856 | let encoded = ComponentEncoder::default() |
| 2857 | .import_name_map(HashMap::from([ |
| 2858 | ( |
| 2859 | "foo" .to_string(), |
| 2860 | "unlocked-dep=<foo:bar/foo@{>=1.0.0 <1.1.0}>" .to_string(), |
| 2861 | ), |
| 2862 | ( |
| 2863 | "test:wit/i" .to_string(), |
| 2864 | "locked-dep=<foo:bar/i@1.2.3>" .to_string(), |
| 2865 | ), |
| 2866 | ])) |
| 2867 | .module(&module) |
| 2868 | .unwrap() |
| 2869 | .validate(true) |
| 2870 | .encode() |
| 2871 | .unwrap(); |
| 2872 | |
| 2873 | let wat = wasmprinter::print_bytes(encoded).unwrap(); |
| 2874 | assert!(wat.contains("unlocked-dep=<foo:bar/foo@{>=1.0.0 <1.1.0}>" )); |
| 2875 | assert!(wat.contains("locked-dep=<foo:bar/i@1.2.3>" )); |
| 2876 | } |
| 2877 | } |
| 2878 | |