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 | |