/src/wasm-tools/crates/wit-component/src/encoding.rs
Line | Count | Source |
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::StringEncoding; |
75 | | use crate::encoding::wit::{component_extern_name, interface_extern_name}; |
76 | | use crate::metadata::{self, Bindgen, ModuleMetadata}; |
77 | | use crate::validation::{ |
78 | | Export, ExportMap, Import, ImportInstance, ImportMap, PayloadInfo, PayloadType, |
79 | | }; |
80 | | use anyhow::{Context, Result, anyhow, bail}; |
81 | | use indexmap::{IndexMap, IndexSet}; |
82 | | use std::borrow::Cow; |
83 | | use std::collections::HashMap; |
84 | | use std::hash::Hash; |
85 | | use std::mem; |
86 | | use wasm_encoder::reencode::{Reencode, RoundtripReencoder}; |
87 | | use wasm_encoder::*; |
88 | | use wasmparser::{Validator, WasmFeatures}; |
89 | | use wit_parser::{ |
90 | | Function, FunctionKind, InterfaceId, LiveTypes, Param, Resolve, Stability, Type, TypeDefKind, |
91 | | TypeId, TypeOwner, WorldId, WorldItem, WorldKey, |
92 | | abi::{AbiVariant, WasmSignature, WasmType}, |
93 | | }; |
94 | | |
95 | | const INDIRECT_TABLE_NAME: &str = "$imports"; |
96 | | const SHIM_MEMORY_NAME: &str = "$memory"; |
97 | | const TLS_BASE_GET: &str = "$get-tls-base"; |
98 | | const TLS_BASE_SET: &str = "$set-tls-base"; |
99 | | |
100 | | pub(crate) mod fixup; |
101 | | |
102 | | mod wit; |
103 | | pub use wit::{encode, encode_world}; |
104 | | |
105 | | mod types; |
106 | | use types::{InstanceTypeEncoder, RootTypeEncoder, TypeEncodingMaps, ValtypeEncoder}; |
107 | | mod world; |
108 | | use world::{ComponentWorld, ImportedInterface, Lowering}; |
109 | | |
110 | | mod dedupe; |
111 | | pub(crate) use dedupe::ModuleImportMap; |
112 | | use wasm_metadata::AddMetadataField; |
113 | | |
114 | 25.1k | fn to_val_type(ty: &WasmType) -> ValType { |
115 | 25.1k | match ty { |
116 | 21.9k | WasmType::I32 => ValType::I32, |
117 | 170 | WasmType::I64 => ValType::I64, |
118 | 156 | WasmType::F32 => ValType::F32, |
119 | 74 | WasmType::F64 => ValType::F64, |
120 | 2.19k | WasmType::Pointer => ValType::I32, |
121 | 0 | WasmType::PointerOrI64 => ValType::I64, |
122 | 706 | WasmType::Length => ValType::I32, |
123 | | } |
124 | 25.1k | } |
125 | | |
126 | 13.4k | fn import_func_name(f: &Function) -> String { |
127 | 13.4k | match f.kind { |
128 | | FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => { |
129 | 7.75k | format!("import-func-{}", f.item_name()) |
130 | | } |
131 | | |
132 | | // transform `[method]foo.bar` into `import-method-foo-bar` to |
133 | | // have it be a valid kebab-name which can't conflict with |
134 | | // anything else. |
135 | | // |
136 | | // There's probably a better and more "formal" way to do this |
137 | | // but quick-and-dirty string manipulation should work well |
138 | | // enough for now hopefully. |
139 | | FunctionKind::Getter |
140 | | | FunctionKind::Setter |
141 | | | FunctionKind::Method(_) |
142 | | | FunctionKind::MethodGetter(_) |
143 | | | FunctionKind::MethodSetter(_) |
144 | | | FunctionKind::AsyncMethod(_) |
145 | | | FunctionKind::Static(_) |
146 | | | FunctionKind::StaticGetter(_) |
147 | | | FunctionKind::StaticSetter(_) |
148 | | | FunctionKind::AsyncStatic(_) |
149 | | | FunctionKind::Constructor(_) => { |
150 | 5.67k | format!( |
151 | | "import-{}", |
152 | 5.67k | f.name.replace('[', "").replace([']', '.'], "-") |
153 | | ) |
154 | | } |
155 | | } |
156 | 13.4k | } |
157 | | |
158 | | bitflags::bitflags! { |
159 | | /// Options in the `canon lower` or `canon lift` required for a particular |
160 | | /// function. |
161 | | #[derive(Copy, Clone, Debug)] |
162 | | pub struct RequiredOptions: u8 { |
163 | | /// A memory must be specified, typically the "main module"'s memory |
164 | | /// export. |
165 | | const MEMORY = 1 << 0; |
166 | | /// A `realloc` function must be specified, typically named |
167 | | /// `cabi_realloc`. |
168 | | const REALLOC = 1 << 1; |
169 | | /// A string encoding must be specified, which is always utf-8 for now |
170 | | /// today. |
171 | | const STRING_ENCODING = 1 << 2; |
172 | | const ASYNC = 1 << 3; |
173 | | } |
174 | | } |
175 | | |
176 | | impl RequiredOptions { |
177 | 6.86k | fn for_import(resolve: &Resolve, func: &Function, abi: AbiVariant) -> RequiredOptions { |
178 | 6.86k | let sig = resolve.wasm_signature(abi, func); |
179 | 6.86k | let mut ret = RequiredOptions::empty(); |
180 | | // Lift the params and lower the results for imports |
181 | 6.86k | ret.add_lift(TypeContents::for_types( |
182 | 6.86k | resolve, |
183 | 6.86k | func.params.iter().map(|p| &p.ty), |
184 | | )); |
185 | 6.86k | ret.add_lower(TypeContents::for_types(resolve, &func.result)); |
186 | | |
187 | | // If anything is indirect then `memory` will be required to read the |
188 | | // indirect values. |
189 | 6.86k | if sig.retptr || sig.indirect_params { |
190 | 1.75k | ret |= RequiredOptions::MEMORY; |
191 | 5.11k | } |
192 | 6.86k | if abi == AbiVariant::GuestImportAsync { |
193 | 2.49k | ret |= RequiredOptions::ASYNC; |
194 | 4.37k | } |
195 | 6.86k | ret |
196 | 6.86k | } |
197 | | |
198 | 7.02k | fn for_export(resolve: &Resolve, func: &Function, abi: AbiVariant) -> RequiredOptions { |
199 | 7.02k | let sig = resolve.wasm_signature(abi, func); |
200 | 7.02k | let mut ret = RequiredOptions::empty(); |
201 | | // Lower the params and lift the results for exports |
202 | 7.02k | ret.add_lower(TypeContents::for_types( |
203 | 7.02k | resolve, |
204 | 7.02k | func.params.iter().map(|p| &p.ty), |
205 | | )); |
206 | 7.02k | ret.add_lift(TypeContents::for_types(resolve, &func.result)); |
207 | | |
208 | | // If anything is indirect then `memory` will be required to read the |
209 | | // indirect values, but if the arguments are indirect then `realloc` is |
210 | | // additionally required to allocate space for the parameters. |
211 | 7.02k | if sig.retptr || sig.indirect_params { |
212 | 1.68k | ret |= RequiredOptions::MEMORY; |
213 | 1.68k | if sig.indirect_params { |
214 | 334 | ret |= RequiredOptions::REALLOC; |
215 | 1.34k | } |
216 | 5.34k | } |
217 | 7.02k | if let AbiVariant::GuestExportAsync | AbiVariant::GuestExportAsyncStackful = abi { |
218 | 770 | ret |= RequiredOptions::ASYNC; |
219 | 770 | ret |= task_return_options_and_type(resolve, func).0; |
220 | 6.25k | } |
221 | 7.02k | ret |
222 | 7.02k | } |
223 | | |
224 | 2.19k | fn for_payload( |
225 | 2.19k | resolve: &Resolve, |
226 | 2.19k | info: &PayloadInfo, |
227 | 2.19k | kind: &PayloadFuncKind, |
228 | 2.19k | async_: bool, |
229 | 2.19k | ) -> RequiredOptions { |
230 | 2.19k | let payload = info.payload(resolve); |
231 | 2.19k | let (wit_param, wit_result) = match kind { |
232 | 1.09k | PayloadFuncKind::StreamRead | PayloadFuncKind::FutureRead => (None, payload), |
233 | 1.09k | PayloadFuncKind::StreamWrite | PayloadFuncKind::FutureWrite => (payload, None), |
234 | | }; |
235 | | RequiredOptions::MEMORY |
236 | 2.19k | | RequiredOptions::for_import( |
237 | 2.19k | resolve, |
238 | | &Function { |
239 | 2.19k | name: String::new(), |
240 | 2.19k | kind: FunctionKind::Freestanding, |
241 | 2.19k | params: match wit_param { |
242 | 404 | Some(ty) => vec![Param { |
243 | 404 | name: "a".to_string(), |
244 | 404 | ty, |
245 | 404 | span: Default::default(), |
246 | 404 | }], |
247 | 1.79k | None => Vec::new(), |
248 | | }, |
249 | 2.19k | result: wit_result, |
250 | 2.19k | docs: Default::default(), |
251 | 2.19k | stability: Stability::Unknown, |
252 | 2.19k | span: Default::default(), |
253 | 2.19k | external_id: None, |
254 | | }, |
255 | 2.19k | if async_ { |
256 | 2.19k | AbiVariant::GuestImportAsync |
257 | | } else { |
258 | 0 | AbiVariant::GuestImport |
259 | | }, |
260 | | ) |
261 | 2.19k | } |
262 | | |
263 | 13.8k | fn add_lower(&mut self, types: TypeContents) { |
264 | | // If lists/strings are lowered into wasm then memory is required as |
265 | | // usual but `realloc` is also required to allow the external caller to |
266 | | // allocate space in the destination for the list/string. |
267 | 13.8k | if types.contains(TypeContents::NEEDS_MEMORY) { |
268 | 141 | *self |= RequiredOptions::MEMORY | RequiredOptions::REALLOC; |
269 | 13.7k | } |
270 | 13.8k | if types.contains(TypeContents::STRING) { |
271 | 352 | *self |= RequiredOptions::MEMORY |
272 | 352 | | RequiredOptions::STRING_ENCODING |
273 | 352 | | RequiredOptions::REALLOC; |
274 | 13.5k | } |
275 | 13.8k | } |
276 | | |
277 | 13.8k | fn add_lift(&mut self, types: TypeContents) { |
278 | | // Unlike for `lower` when lifting a string/list all that's needed is |
279 | | // memory, since the string/list already resides in memory `realloc` |
280 | | // isn't needed. |
281 | 13.8k | if types.contains(TypeContents::NEEDS_MEMORY) { |
282 | 156 | *self |= RequiredOptions::MEMORY; |
283 | 13.7k | } |
284 | 13.8k | if types.contains(TypeContents::STRING) { |
285 | 490 | *self |= RequiredOptions::MEMORY | RequiredOptions::STRING_ENCODING; |
286 | 13.4k | } |
287 | 13.8k | } |
288 | | |
289 | 11.1k | fn into_iter( |
290 | 11.1k | self, |
291 | 11.1k | encoding: StringEncoding, |
292 | 11.1k | memory_index: Option<u32>, |
293 | 11.1k | realloc_index: Option<u32>, |
294 | 11.1k | ) -> Result<impl ExactSizeIterator<Item = CanonicalOption>> { |
295 | | #[derive(Default)] |
296 | | struct Iter { |
297 | | options: [Option<CanonicalOption>; 5], |
298 | | current: usize, |
299 | | count: usize, |
300 | | } |
301 | | |
302 | | impl Iter { |
303 | 7.54k | fn push(&mut self, option: CanonicalOption) { |
304 | 7.54k | assert!(self.count < self.options.len()); |
305 | 7.54k | self.options[self.count] = Some(option); |
306 | 7.54k | self.count += 1; |
307 | 7.54k | } |
308 | | } |
309 | | |
310 | | impl Iterator for Iter { |
311 | | type Item = CanonicalOption; |
312 | | |
313 | 18.7k | fn next(&mut self) -> Option<Self::Item> { |
314 | 18.7k | if self.current == self.count { |
315 | 11.1k | return None; |
316 | 7.54k | } |
317 | 7.54k | let option = self.options[self.current]; |
318 | 7.54k | self.current += 1; |
319 | 7.54k | option |
320 | 18.7k | } |
321 | | |
322 | 4.58k | fn size_hint(&self) -> (usize, Option<usize>) { |
323 | 4.58k | (self.count - self.current, Some(self.count - self.current)) |
324 | 4.58k | } |
325 | | } |
326 | | |
327 | | impl ExactSizeIterator for Iter {} |
328 | | |
329 | 11.1k | let mut iter = Iter::default(); |
330 | | |
331 | 11.1k | if self.contains(RequiredOptions::MEMORY) { |
332 | 4.03k | iter.push(CanonicalOption::Memory(memory_index.ok_or_else(|| { |
333 | 0 | anyhow!("module does not export a memory named `memory`") |
334 | 0 | })?)); |
335 | 7.12k | } |
336 | | |
337 | 11.1k | if self.contains(RequiredOptions::REALLOC) { |
338 | 634 | iter.push(CanonicalOption::Realloc(realloc_index.ok_or_else( |
339 | 0 | || anyhow!("module does not export a function named `cabi_realloc`"), |
340 | 0 | )?)); |
341 | 10.5k | } |
342 | | |
343 | 11.1k | if self.contains(RequiredOptions::STRING_ENCODING) { |
344 | 744 | iter.push(encoding.into()); |
345 | 10.4k | } |
346 | | |
347 | 11.1k | if self.contains(RequiredOptions::ASYNC) { |
348 | 2.13k | iter.push(CanonicalOption::Async); |
349 | 9.02k | } |
350 | | |
351 | 11.1k | Ok(iter) |
352 | 11.1k | } |
353 | | } |
354 | | |
355 | | bitflags::bitflags! { |
356 | | /// Flags about what kinds of types are present within the recursive |
357 | | /// structure of a type. |
358 | | struct TypeContents: u8 { |
359 | | const STRING = 1 << 0; |
360 | | const NEEDS_MEMORY = 1 << 1; |
361 | | } |
362 | | } |
363 | | |
364 | | impl TypeContents { |
365 | 43.3k | fn for_types<'a>(resolve: &Resolve, types: impl IntoIterator<Item = &'a Type>) -> Self { |
366 | 43.3k | let mut cur = TypeContents::empty(); |
367 | 124k | for ty in types { |
368 | 124k | cur |= Self::for_type(resolve, ty); |
369 | 124k | } |
370 | 43.3k | cur |
371 | 43.3k | } <wit_component::encoding::TypeContents>::for_types::<core::slice::iter::Iter<wit_parser::Type>> Line | Count | Source | 365 | 15.4k | fn for_types<'a>(resolve: &Resolve, types: impl IntoIterator<Item = &'a Type>) -> Self { | 366 | 15.4k | let mut cur = TypeContents::empty(); | 367 | 69.2k | for ty in types { | 368 | 69.2k | cur |= Self::for_type(resolve, ty); | 369 | 69.2k | } | 370 | 15.4k | cur | 371 | 15.4k | } |
<wit_component::encoding::TypeContents>::for_types::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Field>, <wit_component::encoding::TypeContents>::for_type::{closure#0}>>Line | Count | Source | 365 | 76 | fn for_types<'a>(resolve: &Resolve, types: impl IntoIterator<Item = &'a Type>) -> Self { | 366 | 76 | let mut cur = TypeContents::empty(); | 367 | 167 | for ty in types { | 368 | 167 | cur |= Self::for_type(resolve, ty); | 369 | 167 | } | 370 | 76 | cur | 371 | 76 | } |
<wit_component::encoding::TypeContents>::for_types::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Param>, <wit_component::encoding::RequiredOptions>::for_export::{closure#0}>>Line | Count | Source | 365 | 7.02k | fn for_types<'a>(resolve: &Resolve, types: impl IntoIterator<Item = &'a Type>) -> Self { | 366 | 7.02k | let mut cur = TypeContents::empty(); | 367 | 28.1k | for ty in types { | 368 | 28.1k | cur |= Self::for_type(resolve, ty); | 369 | 28.1k | } | 370 | 7.02k | cur | 371 | 7.02k | } |
<wit_component::encoding::TypeContents>::for_types::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Param>, <wit_component::encoding::RequiredOptions>::for_import::{closure#0}>>Line | Count | Source | 365 | 6.86k | fn for_types<'a>(resolve: &Resolve, types: impl IntoIterator<Item = &'a Type>) -> Self { | 366 | 6.86k | let mut cur = TypeContents::empty(); | 367 | 15.7k | for ty in types { | 368 | 15.7k | cur |= Self::for_type(resolve, ty); | 369 | 15.7k | } | 370 | 6.86k | cur | 371 | 6.86k | } |
<wit_component::encoding::TypeContents>::for_types::<core::iter::adapters::flatten::Flatten<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Case>, <wit_component::encoding::TypeContents>::for_type::{closure#1}>>>Line | Count | Source | 365 | 50 | fn for_types<'a>(resolve: &Resolve, types: impl IntoIterator<Item = &'a Type>) -> Self { | 366 | 50 | let mut cur = TypeContents::empty(); | 367 | 398 | for ty in types { | 368 | 398 | cur |= Self::for_type(resolve, ty); | 369 | 398 | } | 370 | 50 | cur | 371 | 50 | } |
<wit_component::encoding::TypeContents>::for_types::<&core::option::Option<wit_parser::Type>> Line | Count | Source | 365 | 13.8k | fn for_types<'a>(resolve: &Resolve, types: impl IntoIterator<Item = &'a Type>) -> Self { | 366 | 13.8k | let mut cur = TypeContents::empty(); | 367 | 13.8k | for ty in types { | 368 | 11.0k | cur |= Self::for_type(resolve, ty); | 369 | 11.0k | } | 370 | 13.8k | cur | 371 | 13.8k | } |
|
372 | | |
373 | 50 | fn for_optional_types<'a>( |
374 | 50 | resolve: &Resolve, |
375 | 50 | types: impl Iterator<Item = Option<&'a Type>>, |
376 | 50 | ) -> Self { |
377 | 50 | Self::for_types(resolve, types.flatten()) |
378 | 50 | } |
379 | | |
380 | 29.6k | fn for_optional_type(resolve: &Resolve, ty: Option<&Type>) -> Self { |
381 | 29.6k | match ty { |
382 | 27.8k | Some(ty) => Self::for_type(resolve, ty), |
383 | 1.74k | None => Self::empty(), |
384 | | } |
385 | 29.6k | } |
386 | | |
387 | 157k | fn for_type(resolve: &Resolve, ty: &Type) -> Self { |
388 | 157k | match ty { |
389 | 44.6k | Type::Id(id) => match &resolve.types[*id].kind { |
390 | 3.53k | TypeDefKind::Handle(h) => match h { |
391 | 772 | wit_parser::Handle::Own(_) => Self::empty(), |
392 | 2.76k | wit_parser::Handle::Borrow(_) => Self::empty(), |
393 | | }, |
394 | 0 | TypeDefKind::Resource => Self::empty(), |
395 | 76 | TypeDefKind::Record(r) => Self::for_types(resolve, r.fields.iter().map(|f| &f.ty)), |
396 | 15.4k | TypeDefKind::Tuple(t) => Self::for_types(resolve, t.types.iter()), |
397 | 37 | TypeDefKind::Flags(_) => Self::empty(), |
398 | 2.45k | TypeDefKind::Option(t) => Self::for_type(resolve, t), |
399 | 14.8k | TypeDefKind::Result(r) => { |
400 | 14.8k | Self::for_optional_type(resolve, r.ok.as_ref()) |
401 | 14.8k | | Self::for_optional_type(resolve, r.err.as_ref()) |
402 | | } |
403 | 50 | TypeDefKind::Variant(v) => { |
404 | 454 | Self::for_optional_types(resolve, v.cases.iter().map(|c| c.ty.as_ref())) |
405 | | } |
406 | 81 | TypeDefKind::Enum(_) => Self::empty(), |
407 | 1.91k | TypeDefKind::List(t) => Self::for_type(resolve, t) | Self::NEEDS_MEMORY, |
408 | 0 | TypeDefKind::Map(k, v) => { |
409 | 0 | Self::for_type(resolve, k) | Self::for_type(resolve, v) | Self::NEEDS_MEMORY |
410 | | } |
411 | 552 | TypeDefKind::FixedLengthList(t, _elements) => Self::for_type(resolve, t), |
412 | 201 | TypeDefKind::Type(t) => Self::for_type(resolve, t), |
413 | 4.79k | TypeDefKind::Future(_) => Self::empty(), |
414 | 650 | TypeDefKind::Stream(_) => Self::empty(), |
415 | 0 | TypeDefKind::Unknown => unreachable!(), |
416 | | }, |
417 | 1.49k | Type::String => Self::STRING, |
418 | 111k | _ => Self::empty(), |
419 | | } |
420 | 157k | } |
421 | | } |
422 | | |
423 | | /// State relating to encoding a component. |
424 | | pub struct EncodingState<'a> { |
425 | | /// The component being encoded. |
426 | | component: ComponentBuilder, |
427 | | /// The index into the core module index space for the inner core module. |
428 | | /// |
429 | | /// If `None`, the core module has not been encoded. |
430 | | module_index: Option<u32>, |
431 | | /// The index into the core instance index space for the inner core module. |
432 | | /// |
433 | | /// If `None`, the core module has not been instantiated. |
434 | | instance_index: Option<u32>, |
435 | | /// The index in the core memory index space for the exported memory. |
436 | | /// |
437 | | /// If `None`, then the memory has not yet been aliased. |
438 | | memory_index: Option<u32>, |
439 | | /// The index of the shim instance used for lowering imports into the core instance. |
440 | | /// |
441 | | /// If `None`, then the shim instance how not yet been encoded. |
442 | | shim_instance_index: Option<u32>, |
443 | | |
444 | | /// A map of named adapter modules and the index that the module was defined |
445 | | /// at. |
446 | | adapter_modules: IndexMap<&'a str, u32>, |
447 | | /// A map of adapter module instances and the index of their instance. |
448 | | adapter_instances: IndexMap<&'a str, u32>, |
449 | | |
450 | | /// Imported/exported instances and what index they were imported as. |
451 | | instances: IndexMap<InterfaceId, u32>, |
452 | | imported_funcs: IndexMap<String, u32>, |
453 | | |
454 | | /// Maps used when translating types to the component model binary format. |
455 | | /// Note that imports and exports are stored in separate maps since they |
456 | | /// need fresh hierarchies of types in case the same interface is both |
457 | | /// imported and exported. |
458 | | type_encoding_maps: TypeEncodingMaps<'a>, |
459 | | |
460 | | /// Cache of items that have been aliased from core instances. |
461 | | /// |
462 | | /// This is a helper to reduce the number of aliases created by ensuring |
463 | | /// that repeated requests for the same item return the same index of an |
464 | | /// original `core alias` item. |
465 | | aliased_core_items: HashMap<(u32, String), u32>, |
466 | | |
467 | | /// Metadata about the world inferred from the input to `ComponentEncoder`. |
468 | | info: &'a ComponentWorld<'a>, |
469 | | |
470 | | /// Maps from the instance an export came from, plus its original core |
471 | | /// name, to the index of the wrapper function which the fixup module |
472 | | /// defines for it. |
473 | | /// |
474 | | /// Used to invoke the task hook around lifted exports, post-return |
475 | | /// functions, callbacks, and `realloc` functions. |
476 | | export_task_initialization_wrappers: HashMap<(fixup::ImportedInstance, String), u32>, |
477 | | } |
478 | | |
479 | | impl<'a> EncodingState<'a> { |
480 | 3.79k | fn encode_core_modules(&mut self) { |
481 | 3.79k | assert!(self.module_index.is_none()); |
482 | 3.79k | let idx = self |
483 | 3.79k | .component |
484 | 3.79k | .core_module_raw(Some("main"), &self.info.encoder.module); |
485 | 3.79k | self.module_index = Some(idx); |
486 | | |
487 | 3.79k | for (name, adapter) in self.info.adapters.iter() { |
488 | 0 | let debug_name = if adapter.library_info.is_some() { |
489 | 0 | name.to_string() |
490 | | } else { |
491 | 0 | format!("wit-component:adapter:{name}") |
492 | | }; |
493 | 0 | let idx = if self.info.encoder.debug_names { |
494 | 0 | let mut add_meta = wasm_metadata::AddMetadata::default(); |
495 | 0 | add_meta.name = AddMetadataField::Set(debug_name.clone()); |
496 | 0 | let wasm = add_meta |
497 | 0 | .to_wasm(&adapter.wasm) |
498 | 0 | .expect("core wasm can get name added"); |
499 | 0 | self.component.core_module_raw(Some(&debug_name), &wasm) |
500 | | } else { |
501 | 0 | self.component |
502 | 0 | .core_module_raw(Some(&debug_name), &adapter.wasm) |
503 | | }; |
504 | 0 | let prev = self.adapter_modules.insert(name, idx); |
505 | 0 | assert!(prev.is_none()); |
506 | | } |
507 | 3.79k | } |
508 | | |
509 | 5.12k | fn root_import_type_encoder( |
510 | 5.12k | &mut self, |
511 | 5.12k | interface: Option<InterfaceId>, |
512 | 5.12k | ) -> RootTypeEncoder<'_, 'a> { |
513 | 5.12k | RootTypeEncoder { |
514 | 5.12k | state: self, |
515 | 5.12k | interface, |
516 | 5.12k | import_types: true, |
517 | 5.12k | } |
518 | 5.12k | } |
519 | | |
520 | 36.0k | fn root_export_type_encoder( |
521 | 36.0k | &mut self, |
522 | 36.0k | interface: Option<InterfaceId>, |
523 | 36.0k | ) -> RootTypeEncoder<'_, 'a> { |
524 | 36.0k | RootTypeEncoder { |
525 | 36.0k | state: self, |
526 | 36.0k | interface, |
527 | 36.0k | import_types: false, |
528 | 36.0k | } |
529 | 36.0k | } |
530 | | |
531 | 1.16k | fn instance_type_encoder(&mut self, interface: InterfaceId) -> InstanceTypeEncoder<'_, 'a> { |
532 | 1.16k | InstanceTypeEncoder { |
533 | 1.16k | state: self, |
534 | 1.16k | interface, |
535 | 1.16k | type_encoding_maps: Default::default(), |
536 | 1.16k | ty: Default::default(), |
537 | 1.16k | } |
538 | 1.16k | } |
539 | | |
540 | 3.79k | fn encode_imports(&mut self, name_map: &HashMap<String, String>) -> Result<()> { |
541 | 3.79k | let mut has_funcs = false; |
542 | 3.79k | for (name, info) in self.info.import_map.iter() { |
543 | 2.32k | match name { |
544 | 1.16k | Some(name) => { |
545 | 1.16k | self.encode_interface_import(name_map.get(name).unwrap_or(name), info)? |
546 | | } |
547 | 1.15k | None => has_funcs = true, |
548 | | } |
549 | | } |
550 | | |
551 | 3.79k | let resolve = &self.info.encoder.metadata.resolve; |
552 | 3.79k | let world = &resolve.worlds[self.info.encoder.metadata.world]; |
553 | | |
554 | | // FIXME: ideally this would use the liveness analysis from |
555 | | // world-building to only encode live types, not all type in a world. |
556 | 3.79k | for (_name, item) in world.imports.iter() { |
557 | 3.57k | if let WorldItem::Type { id, .. } = item { |
558 | 1.57k | self.root_import_type_encoder(None) |
559 | 1.57k | .encode_valtype(resolve, &Type::Id(*id))?; |
560 | 2.00k | } |
561 | | } |
562 | | |
563 | 3.79k | if has_funcs { |
564 | 1.15k | let info = &self.info.import_map[&None]; |
565 | 1.15k | self.encode_root_import_funcs(info)?; |
566 | 2.63k | } |
567 | 3.79k | Ok(()) |
568 | 3.79k | } |
569 | | |
570 | 1.16k | fn encode_interface_import(&mut self, name: &str, info: &ImportedInterface) -> Result<()> { |
571 | 1.16k | let resolve = &self.info.encoder.metadata.resolve; |
572 | 1.16k | let interface_id = info.interface.as_ref().unwrap(); |
573 | 1.16k | let interface_id = *interface_id; |
574 | 1.16k | let interface = &resolve.interfaces[interface_id]; |
575 | 1.16k | log::trace!("encoding imports for `{name}` as {interface_id:?}"); |
576 | 1.16k | let mut encoder = self.instance_type_encoder(interface_id); |
577 | | |
578 | | // First encode all type information |
579 | 1.16k | if let Some(live) = encoder.state.info.live_type_imports.get(&interface_id) { |
580 | 535 | for ty in live { |
581 | 535 | log::trace!( |
582 | | "encoding extra type {ty:?} name={:?}", |
583 | 0 | resolve.types[*ty].name |
584 | | ); |
585 | 535 | encoder.encode_valtype(resolve, &Type::Id(*ty))?; |
586 | | } |
587 | 707 | } |
588 | | |
589 | | // Next encode all required functions from this imported interface |
590 | | // into the instance type. |
591 | 1.40k | for (_, func) in interface.functions.iter() { |
592 | 1.40k | if !(info |
593 | 1.40k | .lowerings |
594 | 1.40k | .contains_key(&(func.name.clone(), AbiVariant::GuestImport)) |
595 | 231 | || info |
596 | 231 | .lowerings |
597 | 231 | .contains_key(&(func.name.clone(), AbiVariant::GuestImportAsync))) |
598 | | { |
599 | 0 | continue; |
600 | 1.40k | } |
601 | 1.40k | log::trace!("encoding function type for `{}`", func.name); |
602 | 1.40k | let idx = encoder.encode_func_type(resolve, func)?; |
603 | | |
604 | 1.40k | encoder.ty.export( |
605 | 1.40k | crate::encoding::types::extern_name(&func.name, func.external_id.as_deref()), |
606 | 1.40k | ComponentTypeRef::Func(idx), |
607 | | ); |
608 | | } |
609 | | |
610 | 1.16k | let ty = encoder.ty; |
611 | | // Don't encode empty instance types since they're not |
612 | | // meaningful to the runtime of the component anyway. |
613 | 1.16k | if ty.is_empty() { |
614 | 485 | return Ok(()); |
615 | 682 | } |
616 | 682 | let instance_type_idx = self |
617 | 682 | .component |
618 | 682 | .type_instance(Some(&format!("ty-{name}")), &ty); |
619 | | |
620 | | // `name` is the interface's name unless it's a plain name, which is the |
621 | | // case for imports with `implements` and of anonymous interfaces. |
622 | 682 | let is_interface_name = info.implements.is_none() && interface.name.is_some(); |
623 | 682 | let extern_name = interface_extern_name( |
624 | 682 | resolve, |
625 | 682 | name, |
626 | 682 | is_interface_name.then_some(interface_id), |
627 | 682 | info.implements, |
628 | 682 | info.external_id.clone(), |
629 | 682 | self.info.encoder.emit_canonical_names, |
630 | | ); |
631 | 682 | let instance_idx = self |
632 | 682 | .component |
633 | 682 | .import(extern_name, ComponentTypeRef::Instance(instance_type_idx)); |
634 | 682 | let prev = self.instances.insert(interface_id, instance_idx); |
635 | 682 | assert!(prev.is_none()); |
636 | 682 | Ok(()) |
637 | 1.16k | } |
638 | | |
639 | 1.15k | fn encode_root_import_funcs(&mut self, info: &ImportedInterface) -> Result<()> { |
640 | 1.15k | let resolve = &self.info.encoder.metadata.resolve; |
641 | 1.15k | let world = self.info.encoder.metadata.world; |
642 | 2.95k | for (name, item) in resolve.worlds[world].imports.iter() { |
643 | 2.95k | let func = match item { |
644 | 834 | WorldItem::Function(f) => f, |
645 | 2.11k | WorldItem::Interface { .. } | WorldItem::Type { .. } => continue, |
646 | | }; |
647 | 834 | let name = resolve.name_world_key(name); |
648 | 834 | if !(info |
649 | 834 | .lowerings |
650 | 834 | .contains_key(&(name.clone(), AbiVariant::GuestImport)) |
651 | 72 | || info |
652 | 72 | .lowerings |
653 | 72 | .contains_key(&(name.clone(), AbiVariant::GuestImportAsync))) |
654 | | { |
655 | 0 | continue; |
656 | 834 | } |
657 | 834 | log::trace!("encoding function type for `{}`", func.name); |
658 | 834 | let idx = self |
659 | 834 | .root_import_type_encoder(None) |
660 | 834 | .encode_func_type(resolve, func)?; |
661 | 834 | let func_idx = self.component.import( |
662 | 834 | crate::encoding::types::extern_name(name.as_str(), func.external_id.as_deref()), |
663 | 834 | ComponentTypeRef::Func(idx), |
664 | | ); |
665 | 834 | let prev = self.imported_funcs.insert(name, func_idx); |
666 | 834 | assert!(prev.is_none()); |
667 | | } |
668 | 1.15k | Ok(()) |
669 | 1.15k | } |
670 | | |
671 | 401 | fn alias_instance_type_export(&mut self, interface: InterfaceId, id: TypeId) -> u32 { |
672 | 401 | let ty = &self.info.encoder.metadata.resolve.types[id]; |
673 | 401 | let name = ty.name.as_ref().expect("type must have a name"); |
674 | 401 | let instance = self.instances[&interface]; |
675 | 401 | self.component |
676 | 401 | .alias_export(instance, name, ComponentExportKind::Type) |
677 | 401 | } |
678 | | |
679 | 3.79k | fn encode_core_instantiation( |
680 | 3.79k | &mut self, |
681 | 3.79k | fixup_hook: Option<&mut dyn FnMut(&mut fixup::FixupModule) -> Result<()>>, |
682 | 3.79k | ) -> Result<()> { |
683 | | // Encode a shim instantiation if needed |
684 | 3.79k | let (shims, mut fixups) = self.encode_shim_instantiation()?; |
685 | | |
686 | | // Next declare any types needed for imported intrinsics. This |
687 | | // populates `export_type_map` and will additionally be used for |
688 | | // imports to modules instantiated below. |
689 | 3.79k | self.declare_types_for_imported_intrinsics(&shims)?; |
690 | | |
691 | | // Next instantiate the main module. This provides the linear memory to |
692 | | // use for all future adapters and enables creating indirect lowerings |
693 | | // at the end. |
694 | 3.79k | self.instantiate_main_module(&shims)?; |
695 | | |
696 | 3.79k | for (name, _adapter) in self.info.adapters.iter() { |
697 | 0 | self.instantiate_adapter_module(&shims, name)?; |
698 | | } |
699 | | |
700 | 3.79k | if let Some(initialize) = self.info.info.exports.initialize() { |
701 | 3.79k | fixups.add_initialize(initialize)?; |
702 | 0 | } |
703 | | |
704 | 3.79k | if let Some(hook) = fixup_hook { |
705 | 0 | hook(&mut fixups)?; |
706 | 3.79k | } |
707 | | |
708 | | // With all the relevant core wasm instances in play now the original shim |
709 | | // module, if present, can be filled in with lowerings/adapters/etc. |
710 | 3.79k | fixups.instantiate(&shims, self)?; |
711 | | |
712 | 3.79k | Ok(()) |
713 | 3.79k | } |
714 | | |
715 | 246 | fn lookup_resource_index(&mut self, id: TypeId) -> u32 { |
716 | 246 | let resolve = &self.info.encoder.metadata.resolve; |
717 | 246 | let ty = &resolve.types[id]; |
718 | 246 | match ty.owner { |
719 | | // If this resource is owned by a world then it's a top-level |
720 | | // resource which means it must have already been translated so |
721 | | // it's available for lookup in `import_type_map`. |
722 | 89 | TypeOwner::World(_) => self.type_encoding_maps.id_to_index[&id], |
723 | 157 | TypeOwner::Interface(i) => { |
724 | 157 | let instance = self.instances[&i]; |
725 | 157 | let name = ty.name.as_ref().expect("resources must be named"); |
726 | 157 | self.component |
727 | 157 | .alias_export(instance, name, ComponentExportKind::Type) |
728 | | } |
729 | 0 | TypeOwner::None => panic!("resources must have an owner"), |
730 | | } |
731 | 246 | } |
732 | | |
733 | 3.79k | fn encode_exports(&mut self, module: CustomModule) -> Result<()> { |
734 | 3.79k | let resolve = &self.info.encoder.metadata.resolve; |
735 | 3.79k | let exports = match module { |
736 | 3.79k | CustomModule::Main => &self.info.encoder.main_module_exports, |
737 | 0 | CustomModule::Adapter(name) => &self.info.encoder.adapters[name].required_exports, |
738 | | }; |
739 | | |
740 | 3.79k | if exports.is_empty() { |
741 | 2.35k | return Ok(()); |
742 | 1.44k | } |
743 | | |
744 | 1.44k | let mut interface_func_core_names = IndexMap::new(); |
745 | 1.44k | let mut world_func_core_names = IndexMap::new(); |
746 | 18.8k | for (core_name, export) in self.info.exports_for(module).iter() { |
747 | 18.8k | match export { |
748 | 315 | Export::WorldFunc(_, name, _) => { |
749 | 315 | let prev = world_func_core_names.insert(name, core_name); |
750 | 315 | assert!(prev.is_none()); |
751 | | } |
752 | 6.71k | Export::InterfaceFunc(key, _, name, _) => { |
753 | 6.71k | let prev = interface_func_core_names |
754 | 6.71k | .entry(key) |
755 | 6.71k | .or_insert(IndexMap::new()) |
756 | 6.71k | .insert(name.as_str(), core_name); |
757 | 6.71k | assert!(prev.is_none()); |
758 | | } |
759 | | Export::WorldFuncCallback(..) |
760 | | | Export::InterfaceFuncCallback(..) |
761 | | | Export::WorldFuncPostReturn(..) |
762 | | | Export::InterfaceFuncPostReturn(..) |
763 | | | Export::ResourceDtor(..) |
764 | | | Export::Memory(_) |
765 | | | Export::GeneralPurposeRealloc |
766 | | | Export::GeneralPurposeExportRealloc |
767 | | | Export::GeneralPurposeImportRealloc |
768 | | | Export::Initialize |
769 | | | Export::ReallocForAdapter |
770 | | | Export::IndirectFunctionTable |
771 | 11.8k | | Export::WasmTaskHook => continue, |
772 | | } |
773 | | } |
774 | | |
775 | 1.44k | let world = &resolve.worlds[self.info.encoder.metadata.world]; |
776 | | |
777 | 6.43k | for export_name in exports { |
778 | 6.43k | let export_string = if self.info.encoder.emit_canonical_names { |
779 | 0 | resolve.name_canonicalized_world_key(export_name) |
780 | | } else { |
781 | 6.43k | resolve.name_world_key(export_name) |
782 | | }; |
783 | 6.43k | match &world.exports[export_name] { |
784 | 315 | WorldItem::Function(func) => { |
785 | 315 | let ty = self |
786 | 315 | .root_import_type_encoder(None) |
787 | 315 | .encode_func_type(resolve, func)?; |
788 | 315 | let core_name = world_func_core_names[&func.name]; |
789 | 315 | let idx = self.encode_lift(module, &core_name, export_name, func, ty)?; |
790 | 315 | self.component.export( |
791 | 315 | crate::encoding::types::extern_name( |
792 | 315 | &export_string, |
793 | 315 | func.external_id.as_deref(), |
794 | | ), |
795 | 315 | ComponentExportKind::Func, |
796 | 315 | idx, |
797 | 315 | None, |
798 | | ); |
799 | | } |
800 | 6.11k | item @ WorldItem::Interface { id, .. } => { |
801 | 6.11k | let core_names = interface_func_core_names.get(export_name); |
802 | 6.11k | self.encode_interface_export( |
803 | 6.11k | &export_string, |
804 | 6.11k | module, |
805 | 6.11k | export_name, |
806 | 6.11k | item, |
807 | 6.11k | *id, |
808 | 6.11k | core_names, |
809 | 0 | )?; |
810 | | } |
811 | 0 | WorldItem::Type { .. } => unreachable!(), |
812 | | } |
813 | | } |
814 | | |
815 | 1.44k | Ok(()) |
816 | 3.79k | } |
817 | | |
818 | 6.11k | fn encode_interface_export( |
819 | 6.11k | &mut self, |
820 | 6.11k | export_name: &str, |
821 | 6.11k | module: CustomModule<'_>, |
822 | 6.11k | key: &WorldKey, |
823 | 6.11k | item: &WorldItem, |
824 | 6.11k | export: InterfaceId, |
825 | 6.11k | interface_func_core_names: Option<&IndexMap<&str, &str>>, |
826 | 6.11k | ) -> Result<()> { |
827 | 6.11k | log::trace!("encode interface export `{export_name}`"); |
828 | 6.11k | let resolve = &self.info.encoder.metadata.resolve; |
829 | | |
830 | | // First execute a `canon lift` for all the functions in this interface |
831 | | // from the core wasm export. This requires type information but notably |
832 | | // not exported type information since we don't want to export this |
833 | | // interface's types from the root of the component. Each lifted |
834 | | // function is saved off into an `imports` array to get imported into |
835 | | // the nested component synthesized below. |
836 | 6.11k | let mut imports = Vec::new(); |
837 | 6.11k | let mut root = self.root_export_type_encoder(Some(export)); |
838 | 6.71k | for (_, func) in &resolve.interfaces[export].functions { |
839 | 6.71k | let core_name = interface_func_core_names.unwrap()[func.name.as_str()]; |
840 | 6.71k | let ty = root.encode_func_type(resolve, func)?; |
841 | 6.71k | let func_index = root.state.encode_lift(module, &core_name, key, func, ty)?; |
842 | 6.71k | imports.push(( |
843 | 6.71k | import_func_name(func), |
844 | 6.71k | ComponentExportKind::Func, |
845 | 6.71k | func_index, |
846 | 6.71k | )); |
847 | | } |
848 | | |
849 | | // Next a nested component is created which will import the functions |
850 | | // above and then reexport them. The purpose of them is to "re-type" the |
851 | | // functions through type ascription on each `func` item. |
852 | 6.11k | let mut nested = NestedComponentTypeEncoder { |
853 | 6.11k | component: ComponentBuilder::default(), |
854 | 6.11k | type_encoding_maps: Default::default(), |
855 | 6.11k | export_types: false, |
856 | 6.11k | interface: export, |
857 | 6.11k | state: self, |
858 | 6.11k | imports: IndexMap::new(), |
859 | 6.11k | }; |
860 | | |
861 | | // Import all transitively-referenced types from other interfaces into |
862 | | // this component. This temporarily switches the `interface` listed to |
863 | | // the interface of the referred-to-type to generate the import. After |
864 | | // this loop `interface` is rewritten to `export`. |
865 | | // |
866 | | // Each component is a standalone "island" so the necessary type |
867 | | // information needs to be rebuilt within this component. This ensures |
868 | | // that we're able to build a valid component and additionally connect |
869 | | // all the type information to the outer context. |
870 | 6.11k | let mut types_to_import = LiveTypes::default(); |
871 | 6.11k | types_to_import.add_interface(resolve, export); |
872 | 6.11k | let exports_used = &nested.state.info.exports_used[&export]; |
873 | 40.3k | for ty in types_to_import.iter() { |
874 | 40.3k | if let TypeOwner::Interface(owner) = resolve.types[ty].owner { |
875 | 24.5k | if owner == export { |
876 | | // Here this deals with the current exported interface which |
877 | | // is handled below. |
878 | 23.9k | continue; |
879 | 515 | } |
880 | | |
881 | | // Ensure that `self` has encoded this type before. If so this |
882 | | // is a noop but otherwise it generates the type here. |
883 | 515 | let mut encoder = if exports_used.contains(&owner) { |
884 | 9 | nested.state.root_export_type_encoder(Some(export)) |
885 | | } else { |
886 | 506 | nested.state.root_import_type_encoder(Some(export)) |
887 | | }; |
888 | 515 | encoder.encode_valtype(resolve, &Type::Id(ty))?; |
889 | | |
890 | | // Next generate the same type but this time within the |
891 | | // component itself. The type generated above (or prior) will be |
892 | | // used to satisfy this type import. |
893 | 515 | nested.interface = owner; |
894 | 515 | nested.encode_valtype(resolve, &Type::Id(ty))?; |
895 | 15.8k | } |
896 | | } |
897 | 6.11k | nested.interface = export; |
898 | | |
899 | | // Record the map of types imported to their index at where they were |
900 | | // imported. This is used after imports are encoded as exported types |
901 | | // will refer to these. |
902 | 6.11k | let imported_type_maps = nested.type_encoding_maps.clone(); |
903 | | |
904 | | // Handle resource types for this instance specially, namely importing |
905 | | // them into the nested component. This models how the resource is |
906 | | // imported from its definition in the outer component to get reexported |
907 | | // internally. This chiefly avoids creating a second resource which is |
908 | | // not desired in this situation. |
909 | 6.11k | let mut resources = HashMap::new(); |
910 | 23.9k | for (_name, ty) in resolve.interfaces[export].types.iter() { |
911 | 23.9k | if !matches!(resolve.types[*ty].kind, TypeDefKind::Resource) { |
912 | 23.3k | continue; |
913 | 645 | } |
914 | 645 | let idx = match nested.encode_valtype(resolve, &Type::Id(*ty))? { |
915 | 645 | ComponentValType::Type(idx) => idx, |
916 | 0 | _ => unreachable!(), |
917 | | }; |
918 | 645 | resources.insert(*ty, idx); |
919 | | } |
920 | | |
921 | | // Next import each function of this interface. This will end up |
922 | | // defining local types as necessary or using the types as imported |
923 | | // above. |
924 | 6.71k | for (_, func) in resolve.interfaces[export].functions.iter() { |
925 | 6.71k | let ty = nested.encode_func_type(resolve, func)?; |
926 | 6.71k | nested |
927 | 6.71k | .component |
928 | 6.71k | .import(&import_func_name(func), ComponentTypeRef::Func(ty)); |
929 | | } |
930 | | |
931 | | // Swap the `nested.type_map` which was previously from `TypeId` to |
932 | | // `u32` to instead being from `u32` to `TypeId`. This reverse map is |
933 | | // then used in conjunction with `self.type_map` to satisfy all type |
934 | | // imports of the nested component generated. The type import's index in |
935 | | // the inner component is translated to a `TypeId` via `reverse_map` |
936 | | // which is then translated back to our own index space via `type_map`. |
937 | 6.11k | let reverse_map = nested |
938 | 6.11k | .type_encoding_maps |
939 | 6.11k | .id_to_index |
940 | 6.11k | .drain() |
941 | 15.5k | .map(|p| (p.1, p.0)) |
942 | 6.11k | .collect::<HashMap<_, _>>(); |
943 | 6.11k | nested.type_encoding_maps.def_to_index.clear(); |
944 | 6.11k | for (name, idx) in nested.imports.drain(..) { |
945 | 1.22k | let id = reverse_map[&idx]; |
946 | 1.22k | let idx = nested.state.type_encoding_maps.id_to_index[&id]; |
947 | 1.22k | imports.push((name, ComponentExportKind::Type, idx)) |
948 | | } |
949 | | |
950 | | // Before encoding exports reset the type map to what all was imported |
951 | | // from foreign interfaces. This will enable any encoded types below to |
952 | | // refer to imports which, after type substitution, will point to the |
953 | | // correct type in the outer component context. |
954 | 6.11k | nested.type_encoding_maps = imported_type_maps; |
955 | | |
956 | | // Next the component reexports all of its imports, but notably uses the |
957 | | // type ascription feature to change the type of the function. Note that |
958 | | // no structural change is happening to the types here but instead types |
959 | | // are getting proper names and such now that this nested component is a |
960 | | // new type index space. Hence the `export_types = true` flag here which |
961 | | // flows through the type encoding and when types are emitted. |
962 | 6.11k | nested.export_types = true; |
963 | 6.11k | nested.type_encoding_maps.func_type_map.clear(); |
964 | | |
965 | | // To start off all type information is encoded. This will be used by |
966 | | // functions below but notably this also has special handling for |
967 | | // resources. Resources reexport their imported resource type under |
968 | | // the final name which achieves the desired goal of threading through |
969 | | // the original resource without creating a new one. |
970 | 23.9k | for (_, id) in resolve.interfaces[export].types.iter() { |
971 | 23.9k | let ty = &resolve.types[*id]; |
972 | 23.9k | match ty.kind { |
973 | 645 | TypeDefKind::Resource => { |
974 | 645 | let idx = nested.component.export( |
975 | 645 | crate::encoding::types::extern_name( |
976 | 645 | ty.name.as_ref().expect("resources must be named"), |
977 | 645 | ty.external_id.as_deref(), |
978 | 645 | ), |
979 | 645 | ComponentExportKind::Type, |
980 | 645 | resources[id], |
981 | 645 | None, |
982 | 645 | ); |
983 | 645 | nested.type_encoding_maps.id_to_index.insert(*id, idx); |
984 | 645 | } |
985 | | _ => { |
986 | 23.3k | nested.encode_valtype(resolve, &Type::Id(*id))?; |
987 | | } |
988 | | } |
989 | | } |
990 | | |
991 | 6.71k | for (i, (_, func)) in resolve.interfaces[export].functions.iter().enumerate() { |
992 | 6.71k | let ty = nested.encode_func_type(resolve, func)?; |
993 | 6.71k | nested.component.export( |
994 | 6.71k | crate::encoding::types::extern_name(&func.name, func.external_id.as_deref()), |
995 | 6.71k | ComponentExportKind::Func, |
996 | 6.71k | i as u32, |
997 | 6.71k | Some(ComponentTypeRef::Func(ty)), |
998 | | ); |
999 | | } |
1000 | | |
1001 | | // Embed the component within our component and then instantiate it with |
1002 | | // the lifted functions. That final instance is then exported under the |
1003 | | // appropriate name as the final typed export of this component. |
1004 | 6.11k | let component = nested.component; |
1005 | 6.11k | let component_index = self |
1006 | 6.11k | .component |
1007 | 6.11k | .component(Some(&format!("{export_name}-shim-component")), component); |
1008 | 6.11k | let instance_index = self.component.instantiate( |
1009 | 6.11k | Some(&format!("{export_name}-shim-instance")), |
1010 | 6.11k | component_index, |
1011 | 6.11k | imports, |
1012 | | ); |
1013 | 6.11k | let extern_name = |
1014 | 6.11k | component_extern_name(resolve, key, item, self.info.encoder.emit_canonical_names); |
1015 | 6.11k | let idx = self.component.export( |
1016 | 6.11k | extern_name, |
1017 | 6.11k | ComponentExportKind::Instance, |
1018 | 6.11k | instance_index, |
1019 | 6.11k | None, |
1020 | | ); |
1021 | 6.11k | let prev = self.instances.insert(export, idx); |
1022 | 6.11k | assert!(prev.is_none()); |
1023 | | |
1024 | | // After everything is all said and done remove all the type information |
1025 | | // about type exports of this interface. Any entries in the map |
1026 | | // currently were used to create the instance above but aren't the |
1027 | | // actual copy of the exported type since that comes from the exported |
1028 | | // instance itself. Entries will be re-inserted into this map as |
1029 | | // necessary via aliases from the exported instance which is the new |
1030 | | // source of truth for all these types. |
1031 | 23.9k | for (_name, id) in resolve.interfaces[export].types.iter() { |
1032 | 23.9k | self.type_encoding_maps.id_to_index.remove(id); |
1033 | 23.9k | self.type_encoding_maps |
1034 | 23.9k | .def_to_index |
1035 | 23.9k | .remove(&resolve.types[*id].kind); |
1036 | 23.9k | } |
1037 | | |
1038 | 6.11k | return Ok(()); |
1039 | | |
1040 | | struct NestedComponentTypeEncoder<'state, 'a> { |
1041 | | component: ComponentBuilder, |
1042 | | type_encoding_maps: TypeEncodingMaps<'a>, |
1043 | | export_types: bool, |
1044 | | interface: InterfaceId, |
1045 | | state: &'state mut EncodingState<'a>, |
1046 | | imports: IndexMap<String, u32>, |
1047 | | } |
1048 | | |
1049 | | impl<'a> ValtypeEncoder<'a> for NestedComponentTypeEncoder<'_, 'a> { |
1050 | 53.3k | fn defined_type(&mut self) -> (u32, ComponentDefinedTypeEncoder<'_>) { |
1051 | 53.3k | self.component.type_defined(None) |
1052 | 53.3k | } |
1053 | 9.97k | fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>) { |
1054 | 9.97k | self.component.type_function(None) |
1055 | 9.97k | } |
1056 | 23.9k | fn export_type( |
1057 | 23.9k | &mut self, |
1058 | 23.9k | idx: u32, |
1059 | 23.9k | name: wasm_encoder::ComponentExternName<'a>, |
1060 | 23.9k | ) -> Option<u32> { |
1061 | 23.9k | if self.export_types { |
1062 | 23.3k | Some( |
1063 | 23.3k | self.component |
1064 | 23.3k | .export(name, ComponentExportKind::Type, idx, None), |
1065 | 23.3k | ) |
1066 | | } else { |
1067 | 577 | let name = self.unique_import_name(&name.name); |
1068 | 577 | let ret = self |
1069 | 577 | .component |
1070 | 577 | .import(&name, ComponentTypeRef::Type(TypeBounds::Eq(idx))); |
1071 | 577 | self.imports.insert(name, ret); |
1072 | 577 | Some(ret) |
1073 | | } |
1074 | 23.9k | } |
1075 | 651 | fn export_resource(&mut self, name: wasm_encoder::ComponentExternName<'a>) -> u32 { |
1076 | 651 | if self.export_types { |
1077 | 0 | panic!("resources should already be exported") |
1078 | | } else { |
1079 | 651 | let name = self.unique_import_name(&name.name); |
1080 | 651 | let ret = self |
1081 | 651 | .component |
1082 | 651 | .import(&name, ComponentTypeRef::Type(TypeBounds::SubResource)); |
1083 | 651 | self.imports.insert(name, ret); |
1084 | 651 | ret |
1085 | | } |
1086 | 651 | } |
1087 | 0 | fn import_type(&mut self, _: InterfaceId, _id: TypeId) -> Result<u32> { |
1088 | 0 | unreachable!() |
1089 | | } |
1090 | 137k | fn type_encoding_maps(&mut self) -> &mut TypeEncodingMaps<'a> { |
1091 | 137k | &mut self.type_encoding_maps |
1092 | 137k | } |
1093 | 24.5k | fn interface(&self) -> Option<InterfaceId> { |
1094 | 24.5k | Some(self.interface) |
1095 | 24.5k | } |
1096 | | } |
1097 | | |
1098 | | impl NestedComponentTypeEncoder<'_, '_> { |
1099 | 1.22k | fn unique_import_name(&mut self, name: &str) -> String { |
1100 | 1.22k | let mut name = format!("import-type-{name}"); |
1101 | 1.22k | let mut n = 0; |
1102 | 1.26k | while self.imports.contains_key(&name) { |
1103 | 33 | name = format!("{name}{n}"); |
1104 | 33 | n += 1; |
1105 | 33 | } |
1106 | 1.22k | name |
1107 | 1.22k | } |
1108 | | } |
1109 | 6.11k | } |
1110 | | |
1111 | 7.02k | fn encode_lift( |
1112 | 7.02k | &mut self, |
1113 | 7.02k | module: CustomModule<'_>, |
1114 | 7.02k | core_name: &str, |
1115 | 7.02k | key: &WorldKey, |
1116 | 7.02k | func: &Function, |
1117 | 7.02k | ty: u32, |
1118 | 7.02k | ) -> Result<u32> { |
1119 | 7.02k | let resolve = &self.info.encoder.metadata.resolve; |
1120 | 7.02k | let metadata = self.info.module_metadata_for(module); |
1121 | | |
1122 | 7.02k | let core_alias_export = |
1123 | 13.8k | |me: &mut Self, core_name: &str| me.hooked_core_alias_export(module, core_name); |
1124 | | |
1125 | 7.02k | let core_func_index = core_alias_export(self, core_name); |
1126 | 7.02k | let exports = self.info.exports_for(module); |
1127 | | |
1128 | 7.02k | let options = RequiredOptions::for_export( |
1129 | 7.02k | resolve, |
1130 | 7.02k | func, |
1131 | 7.02k | exports |
1132 | 7.02k | .abi(key, func) |
1133 | 7.02k | .ok_or_else(|| anyhow!("no ABI found for {}", func.name))?, |
1134 | | ); |
1135 | | |
1136 | 7.02k | let encoding = metadata |
1137 | 7.02k | .export_encodings |
1138 | 7.02k | .get(resolve, key, &func.name) |
1139 | 7.02k | .unwrap(); |
1140 | 7.02k | let exports = self.info.exports_for(module); |
1141 | 7.02k | let realloc_index = if options.contains(RequiredOptions::REALLOC) { |
1142 | 519 | let realloc = exports.export_realloc_for(key, &func.name); |
1143 | 519 | resolve_realloc(self.info, module, realloc) |
1144 | 519 | .map(|(module, name)| self.hooked_core_alias_export(module, name)) |
1145 | | } else { |
1146 | 6.51k | None |
1147 | | }; |
1148 | 7.02k | let mut options = options |
1149 | 7.02k | .into_iter(encoding, self.memory_index, realloc_index)? |
1150 | 7.02k | .collect::<Vec<_>>(); |
1151 | | |
1152 | 7.02k | if let Some(post_return) = exports.post_return(key, func) { |
1153 | 6.25k | let post_return = core_alias_export(self, post_return); |
1154 | 6.25k | options.push(CanonicalOption::PostReturn(post_return)); |
1155 | 6.25k | } |
1156 | 7.02k | if let Some(callback) = exports.callback(key, func) { |
1157 | 610 | let callback = core_alias_export(self, callback); |
1158 | 610 | options.push(CanonicalOption::Callback(callback)); |
1159 | 6.41k | } |
1160 | 7.02k | let func_index = self |
1161 | 7.02k | .component |
1162 | 7.02k | .lift_func(Some(&func.name), core_func_index, ty, options); |
1163 | 7.02k | Ok(func_index) |
1164 | 7.02k | } |
1165 | | |
1166 | 3.79k | fn encode_shim_instantiation(&mut self) -> Result<(Shims<'a>, fixup::FixupModule)> { |
1167 | 3.79k | let mut ret = Shims::default(); |
1168 | 3.79k | let mut fixups = fixup::FixupModule::default(); |
1169 | | |
1170 | 3.79k | ret.append_indirect(self.info, CustomModule::Main) |
1171 | 3.79k | .context("failed to register indirect shims for main module")?; |
1172 | | |
1173 | | // For all required adapter modules a shim is created for each required |
1174 | | // function and additionally a set of shims are created for the |
1175 | | // interface imported into the shim module itself. |
1176 | 3.79k | for (adapter_name, _adapter) in self.info.adapters.iter() { |
1177 | 0 | ret.append_indirect(self.info, CustomModule::Adapter(adapter_name)) |
1178 | 0 | .with_context(|| { |
1179 | 0 | format!("failed to register indirect shims for adapter {adapter_name}") |
1180 | 0 | })?; |
1181 | | } |
1182 | | |
1183 | | // Determine if a TLS global needs to be synthesized here by seeing if |
1184 | | // it was imported into any module. |
1185 | 3.79k | let tls_base_global_type = if self.info.can_use_context_slot_1() { |
1186 | 0 | None |
1187 | | } else { |
1188 | 3.79k | let infos = [&self.info.info] |
1189 | 3.79k | .into_iter() |
1190 | 3.79k | .chain(self.info.adapters.values().map(|a| &a.info)); |
1191 | 3.79k | let mut ret = None; |
1192 | 3.79k | for info in infos { |
1193 | 22.9k | for (_, _, import) in info.imports.imports() { |
1194 | 22.9k | let ty = match import { |
1195 | 0 | Import::TlsBaseGet { ty } | Import::TlsBaseSet { ty } => *ty, |
1196 | 22.9k | _ => continue, |
1197 | | }; |
1198 | 0 | let prev = *ret.get_or_insert(ty); |
1199 | 0 | if prev != ty { |
1200 | 0 | bail!("conflicting TLS base pointer types `{prev}` and `{ty}`"); |
1201 | 0 | } |
1202 | | } |
1203 | | } |
1204 | 3.79k | match ret { |
1205 | 0 | Some(ret) => Some(ret.try_into()?), |
1206 | 3.79k | None => None, |
1207 | | } |
1208 | | }; |
1209 | | |
1210 | 3.79k | if ret.shims.is_empty() |
1211 | 2.69k | && self.info.info.imports.imported_memory().is_none() |
1212 | 2.69k | && tls_base_global_type.is_none() |
1213 | | { |
1214 | 2.69k | return Ok((ret, fixups)); |
1215 | 1.09k | } |
1216 | | |
1217 | 1.09k | assert!(self.shim_instance_index.is_none()); |
1218 | | |
1219 | | // This function encodes a single module, the shim module, which defines |
1220 | | // functions to indirectly call through tables or globals. This shim |
1221 | | // additionally may contain the memory for the main module if it needs |
1222 | | // it as well as the TLS base pointer for the program. This shim is |
1223 | | // instantiated before the main module. |
1224 | | |
1225 | 1.09k | let mut types = TypeSection::new(); |
1226 | 1.09k | let mut shim_globals = GlobalSection::new(); |
1227 | 1.09k | let mut shim_tables = TableSection::new(); |
1228 | 1.09k | let mut shim_memories = MemorySection::new(); |
1229 | 1.09k | let mut shim_functions = FunctionSection::new(); |
1230 | 1.09k | let mut shim_exports = ExportSection::new(); |
1231 | 1.09k | let mut shim_code = CodeSection::new(); |
1232 | 1.09k | let mut sigs = IndexMap::new(); |
1233 | 1.09k | let mut trapping_stubs = IndexMap::new(); |
1234 | 1.09k | let mut func_indexes = Vec::new(); |
1235 | 1.09k | let mut func_names = NameMap::new(); |
1236 | | |
1237 | 1.09k | let mut intern_type = |
1238 | 4.13k | |types: &mut TypeSection, params: Vec<ValType>, results: Vec<ValType>| { |
1239 | 4.13k | *sigs |
1240 | 4.13k | .entry((params, results)) |
1241 | 4.13k | .or_insert_with_key(|(params, results)| { |
1242 | 1.45k | let index = types.len(); |
1243 | 1.45k | types |
1244 | 1.45k | .ty() |
1245 | 1.45k | .function(params.iter().copied(), results.iter().copied()); |
1246 | 1.45k | index |
1247 | 1.45k | }) |
1248 | 4.13k | }; |
1249 | | |
1250 | 4.13k | for shim in ret.shims.values() { |
1251 | 4.13k | let type_index = intern_type( |
1252 | 4.13k | &mut types, |
1253 | 4.13k | shim.sig.params.iter().map(to_val_type).collect(), |
1254 | 4.13k | shim.sig.results.iter().map(to_val_type).collect(), |
1255 | 4.13k | ); |
1256 | | |
1257 | 4.13k | let trapping_stub = if self.info.encoder.shim_return_call_ref { |
1258 | | // Generate a stub which is `unreachable` for the initializer of |
1259 | | // this global. This can be deduplicated by type signature. |
1260 | 2.36k | Some(*trapping_stubs.entry(type_index).or_insert_with(|| { |
1261 | 574 | let mut stub = wasm_encoder::Function::new(std::iter::empty()); |
1262 | 574 | stub.instructions().unreachable().end(); |
1263 | 574 | let index = shim_code.len(); |
1264 | 574 | shim_functions.function(type_index); |
1265 | 574 | shim_code.function(&stub); |
1266 | 574 | func_names.append(index, "trap stub before initialization"); |
1267 | 574 | index |
1268 | 574 | })) |
1269 | | } else { |
1270 | 1.77k | None |
1271 | | }; |
1272 | | |
1273 | 4.13k | let func_index = shim_functions.len(); |
1274 | 4.13k | shim_functions.function(type_index); |
1275 | 4.13k | let mut func = wasm_encoder::Function::new(std::iter::empty()); |
1276 | 9.68k | for i in 0..shim.sig.params.len() { |
1277 | 9.68k | func.instructions().local_get(i as u32); |
1278 | 9.68k | } |
1279 | 4.13k | fixups.add_shim(&self.info.encoder, shim)?; |
1280 | 4.13k | if self.info.encoder.shim_return_call_ref { |
1281 | 2.36k | // Generate a global-per-function which gets exported from the |
1282 | 2.36k | // module and then filled in later. |
1283 | 2.36k | let global = shim_globals.len(); |
1284 | 2.36k | let ty = GlobalType { |
1285 | 2.36k | val_type: RefType { |
1286 | 2.36k | nullable: false, |
1287 | 2.36k | heap_type: HeapType::Concrete(type_index), |
1288 | 2.36k | } |
1289 | 2.36k | .into(), |
1290 | 2.36k | mutable: true, |
1291 | 2.36k | shared: false, |
1292 | 2.36k | }; |
1293 | 2.36k | let global_name = format!("g{}", shim.name); |
1294 | 2.36k | shim_globals.global(ty, &ConstExpr::ref_func(trapping_stub.unwrap())); |
1295 | 2.36k | shim_exports.export(&global_name, ExportKind::Global, global); |
1296 | 2.36k | |
1297 | 2.36k | func.instructions().global_get(global); |
1298 | 2.36k | func.instructions().return_call_ref(type_index); |
1299 | 2.36k | } else { |
1300 | 1.77k | func.instructions().i32_const(func_index as i32); |
1301 | 1.77k | func.instructions().call_indirect(0, type_index); |
1302 | 1.77k | } |
1303 | 4.13k | func.instructions().end(); |
1304 | 4.13k | shim_code.function(&func); |
1305 | 4.13k | shim_exports.export(&shim.name, ExportKind::Func, func_index); |
1306 | | |
1307 | 4.13k | func_indexes.push(func_index); |
1308 | 4.13k | func_names.append(func_index, &shim.debug_name); |
1309 | | } |
1310 | | |
1311 | 1.09k | if !ret.shims.is_empty() && !self.info.encoder.shim_return_call_ref { |
1312 | 670 | let table_type = TableType { |
1313 | 670 | element_type: RefType::FUNCREF, |
1314 | 670 | minimum: ret.shims.len() as u64, |
1315 | 670 | maximum: Some(ret.shims.len() as u64), |
1316 | 670 | table64: false, |
1317 | 670 | shared: false, |
1318 | 670 | }; |
1319 | 670 | |
1320 | 670 | shim_tables.table(table_type); |
1321 | 670 | shim_exports.export(INDIRECT_TABLE_NAME, ExportKind::Table, 0); |
1322 | 670 | } |
1323 | | |
1324 | | // If the main module imports its memory then define it here and export |
1325 | | // it for everyone else to use. |
1326 | 1.09k | if let Some(ty) = self.info.info.imports.imported_memory() { |
1327 | 0 | shim_memories.memory(RoundtripReencoder.memory_type(ty)?); |
1328 | 0 | shim_exports.export(SHIM_MEMORY_NAME, ExportKind::Memory, 0); |
1329 | 1.09k | } |
1330 | | |
1331 | | // If the TLS base pointer is stored in a `global`, as opposed to a |
1332 | | // `context` slot, then define that global here along with accessors |
1333 | | // for it which everyone else uses. |
1334 | 1.09k | if let Some(ty) = tls_base_global_type { |
1335 | 0 | let get_ty = intern_type(&mut types, Vec::new(), vec![ty]); |
1336 | 0 | let set_ty = intern_type(&mut types, vec![ty], Vec::new()); |
1337 | | |
1338 | 0 | let global = shim_globals.len(); |
1339 | 0 | shim_globals.global( |
1340 | 0 | GlobalType { |
1341 | 0 | val_type: ty, |
1342 | 0 | mutable: true, |
1343 | 0 | shared: false, |
1344 | 0 | }, |
1345 | 0 | &match ty { |
1346 | 0 | ValType::I64 => ConstExpr::i64_const(0), |
1347 | 0 | ValType::I32 => ConstExpr::i32_const(0), |
1348 | 0 | _ => unreachable!(), |
1349 | | }, |
1350 | | ); |
1351 | | |
1352 | 0 | let get = shim_functions.len(); |
1353 | 0 | shim_functions.function(get_ty); |
1354 | 0 | let mut func = wasm_encoder::Function::new(std::iter::empty()); |
1355 | 0 | func.instructions().global_get(global).end(); |
1356 | 0 | shim_code.function(&func); |
1357 | 0 | shim_exports.export(TLS_BASE_GET, ExportKind::Func, get); |
1358 | 0 | func_names.append(get, TLS_BASE_GET); |
1359 | | |
1360 | 0 | let set = shim_functions.len(); |
1361 | 0 | shim_functions.function(set_ty); |
1362 | 0 | let mut func = wasm_encoder::Function::new(std::iter::empty()); |
1363 | 0 | func.instructions().local_get(0).global_set(global).end(); |
1364 | 0 | shim_code.function(&func); |
1365 | 0 | shim_exports.export(TLS_BASE_SET, ExportKind::Func, set); |
1366 | 0 | func_names.append(set, TLS_BASE_SET); |
1367 | 1.09k | } |
1368 | | |
1369 | 1.09k | let mut shim_names = NameSection::new(); |
1370 | 1.09k | shim_names.module("wit-component:shim"); |
1371 | 1.09k | shim_names.functions(&func_names); |
1372 | | |
1373 | 1.09k | let mut shim = Module::new(); |
1374 | 1.09k | shim.section(&types); |
1375 | 1.09k | if !shim_functions.is_empty() { |
1376 | 1.09k | shim.section(&shim_functions); |
1377 | 1.09k | } |
1378 | 1.09k | if !shim_tables.is_empty() { |
1379 | 670 | shim.section(&shim_tables); |
1380 | 670 | } |
1381 | 1.09k | if !shim_memories.is_empty() { |
1382 | 0 | shim.section(&shim_memories); |
1383 | 1.09k | } |
1384 | 1.09k | if !shim_globals.is_empty() { |
1385 | 424 | shim.section(&shim_globals); |
1386 | 670 | } |
1387 | 1.09k | if !shim_exports.is_empty() { |
1388 | 1.09k | shim.section(&shim_exports); |
1389 | 1.09k | } |
1390 | 1.09k | if !shim_code.is_empty() { |
1391 | 1.09k | shim.section(&shim_code); |
1392 | 1.09k | } |
1393 | 1.09k | shim.section(&RawCustomSection( |
1394 | 1.09k | &crate::base_producers().raw_custom_section(), |
1395 | 1.09k | )); |
1396 | 1.09k | if self.info.encoder.debug_names { |
1397 | 406 | shim.section(&shim_names); |
1398 | 688 | } |
1399 | | |
1400 | 1.09k | let shim_module_index = self |
1401 | 1.09k | .component |
1402 | 1.09k | .core_module(Some("wit-component-shim-module"), &shim); |
1403 | | |
1404 | 1.09k | let shim_instance = self.component.core_instantiate( |
1405 | 1.09k | Some("wit-component-shim-instance"), |
1406 | 1.09k | shim_module_index, |
1407 | 1.09k | [], |
1408 | | ); |
1409 | 1.09k | self.shim_instance_index = Some(shim_instance); |
1410 | | |
1411 | | // With the shim instantiated the memory it defines, if any, is now |
1412 | | // available for use as a canonical option. |
1413 | 1.09k | if self.info.info.imports.imported_memory().is_some() { |
1414 | 0 | assert!(self.memory_index.is_none()); |
1415 | 0 | self.memory_index = Some(self.core_alias_export( |
1416 | 0 | Some("memory"), |
1417 | 0 | shim_instance, |
1418 | 0 | SHIM_MEMORY_NAME, |
1419 | 0 | ExportKind::Memory, |
1420 | 0 | )); |
1421 | 1.09k | } |
1422 | | |
1423 | 1.09k | Ok((ret, fixups)) |
1424 | 3.79k | } |
1425 | | |
1426 | 4.13k | fn encode_shim(&mut self, shims: &Shims<'_>, shim: &Shim<'_>) -> Result<u32> { |
1427 | 4.13k | let resolve = &self.info.encoder.metadata.resolve; |
1428 | 4.13k | Ok(match &shim.kind { |
1429 | | // Indirect lowerings are a `canon lower`'d function with |
1430 | | // options specified from a previously instantiated instance. |
1431 | | // This previous instance could either be the main module or an |
1432 | | // adapter module, which affects the `realloc` option here. |
1433 | | // Currently only one linear memory is supported so the linear |
1434 | | // memory always comes from the main module. |
1435 | | ShimKind::IndirectLowering { |
1436 | 813 | interface, |
1437 | 813 | index, |
1438 | 813 | realloc, |
1439 | 813 | encoding, |
1440 | | } => { |
1441 | 813 | let interface = &self.info.import_map[interface]; |
1442 | 813 | let ((name, _), _) = interface.lowerings.get_index(*index).unwrap(); |
1443 | 813 | let func_index = match &interface.interface { |
1444 | 552 | Some(interface_id) => { |
1445 | 552 | let instance_index = self.instances[interface_id]; |
1446 | 552 | self.component |
1447 | 552 | .alias_export(instance_index, name, ComponentExportKind::Func) |
1448 | | } |
1449 | 261 | None => self.imported_funcs[name], |
1450 | | }; |
1451 | | |
1452 | 813 | let realloc_export = self |
1453 | 813 | .info |
1454 | 813 | .exports_for(*realloc) |
1455 | 813 | .import_realloc_for(interface.interface, name); |
1456 | 813 | let realloc = self.realloc_index( |
1457 | 813 | shims, |
1458 | 813 | *realloc, |
1459 | 813 | ReallocSite::AfterInstantiation, |
1460 | 813 | shim.options, |
1461 | 813 | realloc_export, |
1462 | | ); |
1463 | | |
1464 | 813 | self.component.lower_func( |
1465 | 813 | Some(&shim.debug_name), |
1466 | 813 | func_index, |
1467 | 813 | shim.options |
1468 | 813 | .into_iter(*encoding, self.memory_index, realloc)?, |
1469 | | ) |
1470 | | } |
1471 | | |
1472 | | // Adapter shims are defined by an export from an adapter |
1473 | | // instance, so use the specified name here and the previously |
1474 | | // created instances to get the core item that represents the |
1475 | | // shim. |
1476 | 0 | ShimKind::Adapter { adapter, func } => self.core_alias_export( |
1477 | 0 | Some(func), |
1478 | 0 | self.adapter_instances[adapter], |
1479 | 0 | func, |
1480 | 0 | ExportKind::Func, |
1481 | | ), |
1482 | | |
1483 | | // Resources are required for a module to be instantiated |
1484 | | // meaning that any destructor for the resource must be called |
1485 | | // indirectly due to the otherwise circular dependency between |
1486 | | // the module and the resource itself. |
1487 | 645 | ShimKind::ResourceDtor { module, export } => self.core_alias_export( |
1488 | 645 | Some(export), |
1489 | 645 | self.instance_for(*module), |
1490 | 645 | export, |
1491 | 645 | ExportKind::Func, |
1492 | | ), |
1493 | | |
1494 | | ShimKind::PayloadFunc { |
1495 | 1.06k | for_module, |
1496 | 1.06k | info, |
1497 | 1.06k | kind, |
1498 | 1.06k | } => self.encode_payload_func( |
1499 | 1.06k | shims, |
1500 | 1.06k | for_module, |
1501 | 1.06k | info, |
1502 | 1.06k | kind, |
1503 | 1.06k | shim.options, |
1504 | 1.06k | ReallocSite::AfterInstantiation, |
1505 | 0 | )?, |
1506 | | |
1507 | | ShimKind::WaitableSetWait => { |
1508 | 689 | self.component.waitable_set_wait(self.memory_index.unwrap()) |
1509 | | } |
1510 | | ShimKind::WaitableSetPoll => { |
1511 | 689 | self.component.waitable_set_poll(self.memory_index.unwrap()) |
1512 | | } |
1513 | 0 | ShimKind::ErrorContextNew { encoding } => self |
1514 | 0 | .component |
1515 | 0 | .error_context_new(shim.options.into_iter(*encoding, self.memory_index, None)?), |
1516 | | ShimKind::ErrorContextDebugMessage { |
1517 | 0 | for_module, |
1518 | 0 | encoding, |
1519 | | } => { |
1520 | 0 | let realloc = self.info.exports_for(*for_module).import_realloc_fallback(); |
1521 | 0 | let realloc_index = self.realloc_index( |
1522 | 0 | shims, |
1523 | 0 | *for_module, |
1524 | 0 | ReallocSite::AfterInstantiation, |
1525 | 0 | shim.options, |
1526 | 0 | realloc, |
1527 | | ); |
1528 | | |
1529 | 0 | self.component |
1530 | 0 | .error_context_debug_message(shim.options.into_iter( |
1531 | 0 | *encoding, |
1532 | 0 | self.memory_index, |
1533 | 0 | realloc_index, |
1534 | 0 | )?) |
1535 | | } |
1536 | | ShimKind::TaskReturn { |
1537 | 241 | interface, |
1538 | 241 | func, |
1539 | 241 | result, |
1540 | 241 | encoding, |
1541 | 241 | for_module, |
1542 | | } => { |
1543 | | // See `Import::ExportedTaskReturn` handling for why this |
1544 | | // encoder is treated specially. |
1545 | 241 | let mut encoder = if interface.is_none() { |
1546 | 15 | self.root_import_type_encoder(*interface) |
1547 | | } else { |
1548 | 226 | self.root_export_type_encoder(*interface) |
1549 | | }; |
1550 | 241 | let result = match result { |
1551 | 237 | Some(ty) => Some(encoder.encode_valtype(resolve, ty)?), |
1552 | 4 | None => None, |
1553 | | }; |
1554 | | |
1555 | 241 | let exports = self.info.exports_for(*for_module); |
1556 | 241 | let realloc = exports.import_realloc_for(*interface, func); |
1557 | | |
1558 | 241 | let instance_index = self.instance_for(*for_module); |
1559 | 241 | let realloc_index = realloc.map(|r| { |
1560 | 241 | self.core_alias_export(Some("realloc"), instance_index, r, ExportKind::Func) |
1561 | 241 | }); |
1562 | 241 | let options = |
1563 | 241 | shim.options |
1564 | 241 | .into_iter(*encoding, self.memory_index, realloc_index)?; |
1565 | 241 | self.component.task_return(result, options) |
1566 | | } |
1567 | 0 | ShimKind::ThreadNewIndirect { func_ty } => { |
1568 | | // Encode the function type for the thread start function so we can reference it in the `canon` call. |
1569 | 0 | let (func_ty_idx, f) = self.component.core_type(Some("thread-start")); |
1570 | 0 | f.core().func_type(func_ty); |
1571 | | |
1572 | | // In order for the funcref table referenced by `thread.new-indirect` to be used, |
1573 | | // it must have been exported by the main module. |
1574 | 0 | let exports = self.info.exports_for(CustomModule::Main); |
1575 | 0 | let instance_index = self.instance_for(CustomModule::Main); |
1576 | 0 | let table_idx = exports.indirect_function_table().map(|table| { |
1577 | 0 | self.core_alias_export( |
1578 | 0 | Some("indirect-function-table"), |
1579 | 0 | instance_index, |
1580 | 0 | table, |
1581 | 0 | ExportKind::Table, |
1582 | | ) |
1583 | 0 | }).ok_or_else(|| { |
1584 | 0 | anyhow!( |
1585 | | "table __indirect_function_table must be an exported funcref table for thread.new-indirect" |
1586 | | ) |
1587 | 0 | })?; |
1588 | | |
1589 | 0 | self.component.thread_new_indirect(func_ty_idx, table_idx) |
1590 | | } |
1591 | | |
1592 | 0 | ShimKind::Realloc { module, export } => { |
1593 | 0 | let instance = self.instance_for(*module); |
1594 | 0 | self.core_alias_export(Some(export), instance, export, ExportKind::Func) |
1595 | | } |
1596 | | }) |
1597 | 4.13k | } |
1598 | | |
1599 | 1.06k | fn encode_payload_func( |
1600 | 1.06k | &mut self, |
1601 | 1.06k | shims: &Shims<'_>, |
1602 | 1.06k | for_module: &CustomModule, |
1603 | 1.06k | info: &PayloadInfo, |
1604 | 1.06k | kind: &PayloadFuncKind, |
1605 | 1.06k | options: RequiredOptions, |
1606 | 1.06k | site: ReallocSite, |
1607 | 1.06k | ) -> Result<u32> { |
1608 | 1.06k | let resolve = &self.info.encoder.metadata.resolve; |
1609 | 1.06k | let metadata = self.info.module_metadata_for(*for_module); |
1610 | 1.06k | let encoding = match &info.ty { |
1611 | 1.06k | PayloadType::Type { function, .. } => { |
1612 | 1.06k | if info.imported { |
1613 | 312 | metadata.import_encodings.get(resolve, &info.key, function) |
1614 | | } else { |
1615 | 748 | metadata.export_encodings.get(resolve, &info.key, function) |
1616 | | } |
1617 | | } |
1618 | 0 | PayloadType::UnitFuture | PayloadType::UnitStream => None, |
1619 | | }; |
1620 | 1.06k | let realloc = payload_realloc_name(self.info, *for_module, info); |
1621 | 1.06k | let encoding = encoding.unwrap_or(StringEncoding::UTF8); |
1622 | 1.06k | let realloc_index = self.realloc_index(shims, *for_module, site, options, realloc); |
1623 | 1.06k | let type_index = self.payload_type_index(info)?; |
1624 | 1.06k | let options = options.into_iter(encoding, self.memory_index, realloc_index)?; |
1625 | | |
1626 | 1.06k | Ok(match kind { |
1627 | 393 | PayloadFuncKind::FutureWrite => self.component.future_write(type_index, options), |
1628 | 393 | PayloadFuncKind::FutureRead => self.component.future_read(type_index, options), |
1629 | 137 | PayloadFuncKind::StreamWrite => self.component.stream_write(type_index, options), |
1630 | 137 | PayloadFuncKind::StreamRead => self.component.stream_read(type_index, options), |
1631 | | }) |
1632 | 1.06k | } |
1633 | | |
1634 | | /// Encode the specified `stream` or `future` type in the component using |
1635 | | /// either the `root_import_type_encoder` or the `root_export_type_encoder` |
1636 | | /// depending on the value of `imported`. |
1637 | | /// |
1638 | | /// Note that the payload type `T` of `stream<T>` or `future<T>` may be an |
1639 | | /// imported or exported type, and that determines the appropriate type |
1640 | | /// encoder to use. |
1641 | 7.64k | fn payload_type_index(&mut self, info: &PayloadInfo) -> Result<u32> { |
1642 | 7.64k | let resolve = &self.info.encoder.metadata.resolve; |
1643 | | // What exactly is selected here as the encoder is a bit unusual here. |
1644 | | // If the interface is imported, an import encoder is used. An import |
1645 | | // encoder is also used though if `info` is exported and |
1646 | | // `info.interface` is `None`, meaning that this is for a function that |
1647 | | // is in the top-level of a world. At the top level of a world all |
1648 | | // types are imported. |
1649 | | // |
1650 | | // Additionally for the import encoder the interface passed in is |
1651 | | // `None`, not `info.interface`. Notably this means that references to |
1652 | | // named types will be aliased from their imported versions, which is |
1653 | | // what we want here. |
1654 | | // |
1655 | | // Finally though exports do use `info.interface`. Honestly I'm not |
1656 | | // really entirely sure why. Fuzzing is happy though, and truly |
1657 | | // everything must be ok if the fuzzers are happy, right? |
1658 | 7.64k | let mut encoder = if info.imported || info.interface.is_none() { |
1659 | 1.83k | self.root_import_type_encoder(None) |
1660 | | } else { |
1661 | 5.81k | self.root_export_type_encoder(info.interface) |
1662 | | }; |
1663 | 7.64k | match info.ty { |
1664 | 7.64k | PayloadType::Type { id, .. } => match encoder.encode_valtype(resolve, &Type::Id(id))? { |
1665 | 7.64k | ComponentValType::Type(index) => Ok(index), |
1666 | 0 | ComponentValType::Primitive(_) => unreachable!(), |
1667 | | }, |
1668 | 0 | PayloadType::UnitFuture => Ok(encoder.encode_unit_future()), |
1669 | 0 | PayloadType::UnitStream => Ok(encoder.encode_unit_stream()), |
1670 | | } |
1671 | 7.64k | } |
1672 | | |
1673 | | /// This is a helper function that will declare any types necessary for |
1674 | | /// declaring intrinsics that are imported into the module or adapter. |
1675 | | /// |
1676 | | /// For example resources must be declared to generate |
1677 | | /// destructors/constructors/etc. Additionally types must also be declared |
1678 | | /// for `task.return` with the component model async feature. |
1679 | 3.79k | fn declare_types_for_imported_intrinsics(&mut self, shims: &Shims<'_>) -> Result<()> { |
1680 | 3.79k | let resolve = &self.info.encoder.metadata.resolve; |
1681 | 3.79k | let world = &resolve.worlds[self.info.encoder.metadata.world]; |
1682 | | |
1683 | | // Iterate over the main module's exports and the exports of all |
1684 | | // adapters. Look for exported interfaces. |
1685 | 3.79k | let main_module_keys = self.info.encoder.main_module_exports.iter(); |
1686 | 6.43k | let main_module_keys = main_module_keys.map(|key| (CustomModule::Main, key)); |
1687 | 3.79k | let adapter_keys = self.info.encoder.adapters.iter().flat_map(|(name, info)| { |
1688 | 0 | info.required_exports |
1689 | 0 | .iter() |
1690 | 0 | .map(move |key| (CustomModule::Adapter(name), key)) |
1691 | 0 | }); |
1692 | 6.43k | for (for_module, key) in main_module_keys.chain(adapter_keys) { |
1693 | 6.43k | let id = match &world.exports[key] { |
1694 | 6.11k | WorldItem::Interface { id, .. } => *id, |
1695 | 0 | WorldItem::Type { .. } => unreachable!(), |
1696 | 315 | WorldItem::Function(_) => continue, |
1697 | | }; |
1698 | | |
1699 | 23.9k | for ty in resolve.interfaces[id].types.values() { |
1700 | 23.9k | let def = &resolve.types[*ty]; |
1701 | 23.9k | match &def.kind { |
1702 | | // Declare exported resources specially as they generally |
1703 | | // need special treatment for later handling exports and |
1704 | | // such. |
1705 | | TypeDefKind::Resource => { |
1706 | | // Load the destructor, previously detected in module |
1707 | | // validation, if one is present. |
1708 | 645 | let exports = self.info.exports_for(for_module); |
1709 | 645 | let dtor = exports.resource_dtor(*ty).map(|name| { |
1710 | 645 | let shim = &shims.shims[&ShimKind::ResourceDtor { |
1711 | 645 | module: for_module, |
1712 | 645 | export: name, |
1713 | 645 | }]; |
1714 | 645 | let index = self.shim_instance_index.unwrap(); |
1715 | 645 | self.core_alias_export( |
1716 | 645 | Some(&shim.debug_name), |
1717 | 645 | index, |
1718 | 645 | &shim.name, |
1719 | 645 | ExportKind::Func, |
1720 | | ) |
1721 | 645 | }); |
1722 | | |
1723 | | // Declare the resource with this destructor and register it in |
1724 | | // our internal map. This should be the first and only time this |
1725 | | // type is inserted into this map. |
1726 | 645 | let resource_idx = self.component.type_resource( |
1727 | 645 | Some(def.name.as_ref().unwrap()), |
1728 | 645 | ValType::I32, |
1729 | 645 | dtor, |
1730 | | ); |
1731 | 645 | let prev = self |
1732 | 645 | .type_encoding_maps |
1733 | 645 | .id_to_index |
1734 | 645 | .insert(*ty, resource_idx); |
1735 | 645 | assert!(prev.is_none()); |
1736 | | } |
1737 | 23.3k | _other => { |
1738 | 23.3k | self.root_export_type_encoder(Some(id)) |
1739 | 23.3k | .encode_valtype(resolve, &Type::Id(*ty))?; |
1740 | | } |
1741 | | } |
1742 | | } |
1743 | | } |
1744 | 3.79k | Ok(()) |
1745 | 3.79k | } |
1746 | | |
1747 | | /// Helper to instantiate the main module and record various results of its |
1748 | | /// instantiation within `self`. |
1749 | 3.79k | fn instantiate_main_module(&mut self, shims: &Shims<'_>) -> Result<()> { |
1750 | 3.79k | assert!(self.instance_index.is_none()); |
1751 | | |
1752 | 3.79k | let instance_index = self.instantiate_core_module(shims, CustomModule::Main)?; |
1753 | | |
1754 | | // If memory hasn't been defined yet then it's provided by the main |
1755 | | // module here. |
1756 | 3.79k | if self.memory_index.is_none() { |
1757 | 3.79k | if let Some((memory, _)) = self.info.info.exports.memory() { |
1758 | 3.79k | self.memory_index = Some(self.core_alias_export( |
1759 | 3.79k | Some("memory"), |
1760 | 3.79k | instance_index, |
1761 | 3.79k | memory, |
1762 | 3.79k | ExportKind::Memory, |
1763 | 3.79k | )); |
1764 | 3.79k | } |
1765 | 0 | } |
1766 | | |
1767 | 3.79k | self.instance_index = Some(instance_index); |
1768 | 3.79k | Ok(()) |
1769 | 3.79k | } |
1770 | | |
1771 | | /// This function will instantiate the specified adapter module, which may |
1772 | | /// depend on previously-instantiated modules. |
1773 | 0 | fn instantiate_adapter_module(&mut self, shims: &Shims<'_>, name: &'a str) -> Result<()> { |
1774 | 0 | let instance = self.instantiate_core_module(shims, CustomModule::Adapter(name))?; |
1775 | 0 | self.adapter_instances.insert(name, instance); |
1776 | 0 | Ok(()) |
1777 | 0 | } |
1778 | | |
1779 | | /// Generic helper to instantiate a module. |
1780 | | /// |
1781 | | /// The `for_module` provided will have all of its imports satisfied from |
1782 | | /// either previous instantiations or the `shims` module present. This |
1783 | | /// iterates over the metadata produced during validation to determine what |
1784 | | /// hooks up to what import. |
1785 | 3.79k | fn instantiate_core_module( |
1786 | 3.79k | &mut self, |
1787 | 3.79k | shims: &Shims, |
1788 | 3.79k | for_module: CustomModule<'_>, |
1789 | 3.79k | ) -> Result<u32> { |
1790 | 3.79k | let module = self.module_for(for_module); |
1791 | | |
1792 | 3.79k | let mut args = Vec::new(); |
1793 | 3.79k | for (core_wasm_name, instance) in self.info.imports_for(for_module).modules() { |
1794 | 3.08k | match instance { |
1795 | | // For import modules that are a "bag of names" iterate over |
1796 | | // each name and materialize it into this component with the |
1797 | | // `materialize_import` helper. This is then all bottled up into |
1798 | | // a bag-of-exports instance which is then used for |
1799 | | // instantiation. |
1800 | 3.08k | ImportInstance::Names(names) => { |
1801 | 3.08k | let mut exports = Vec::new(); |
1802 | 22.9k | for (name, import) in names { |
1803 | 22.9k | log::trace!( |
1804 | | "attempting to materialize import of `{core_wasm_name}::{name}` for {for_module:?}" |
1805 | | ); |
1806 | 22.9k | let (kind, index) = self |
1807 | 22.9k | .materialize_import(&shims, for_module, import) |
1808 | 22.9k | .with_context(|| { |
1809 | 0 | format!("failed to satisfy import `{core_wasm_name}::{name}`") |
1810 | 0 | })?; |
1811 | 22.9k | exports.push((name.as_str(), kind, index)); |
1812 | | } |
1813 | 3.08k | let index = self |
1814 | 3.08k | .component |
1815 | 3.08k | .core_instantiate_exports(Some(core_wasm_name), exports); |
1816 | 3.08k | args.push((core_wasm_name.as_str(), ModuleArg::Instance(index))); |
1817 | | } |
1818 | | |
1819 | | // Some imports are entire instances, so use the instance for |
1820 | | // the module identifier as the import. |
1821 | 0 | ImportInstance::Whole(which) => { |
1822 | 0 | let instance = self.instance_for(which.to_custom_module()); |
1823 | 0 | args.push((core_wasm_name.as_str(), ModuleArg::Instance(instance))); |
1824 | 0 | } |
1825 | | } |
1826 | | } |
1827 | | |
1828 | | // And with all arguments prepared now, instantiate the module. |
1829 | 3.79k | Ok(self |
1830 | 3.79k | .component |
1831 | 3.79k | .core_instantiate(Some(for_module.debug_name()), module, args)) |
1832 | 3.79k | } |
1833 | | |
1834 | | /// Helper function to materialize an import into a core module within the |
1835 | | /// component being built. |
1836 | | /// |
1837 | | /// This function is called for individual imports and uses the results of |
1838 | | /// validation, notably the `Import` type, to determine what WIT-level or |
1839 | | /// component-level construct is being hooked up. |
1840 | 22.9k | fn materialize_import( |
1841 | 22.9k | &mut self, |
1842 | 22.9k | shims: &Shims<'_>, |
1843 | 22.9k | for_module: CustomModule<'_>, |
1844 | 22.9k | import: &'a Import, |
1845 | 22.9k | ) -> Result<(ExportKind, u32)> { |
1846 | 22.9k | let resolve = &self.info.encoder.metadata.resolve; |
1847 | 22.9k | match import { |
1848 | | // Main module dependencies on an adapter in use are done with an |
1849 | | // indirection here, so load the shim function and use that. |
1850 | | Import::AdapterExport { |
1851 | 0 | adapter, |
1852 | 0 | func, |
1853 | | ty: _, |
1854 | | } => { |
1855 | 0 | assert!(self.info.encoder.adapters.contains_key(adapter)); |
1856 | 0 | Ok(self.materialize_shim_import(shims, &ShimKind::Adapter { adapter, func })) |
1857 | | } |
1858 | | |
1859 | | // Adapters might use the main module's memory, in which case it |
1860 | | // should have been previously instantiated. |
1861 | | Import::MainModuleMemory(_) => { |
1862 | 0 | let index = self |
1863 | 0 | .memory_index |
1864 | 0 | .ok_or_else(|| anyhow!("main module cannot import memory"))?; |
1865 | 0 | Ok((ExportKind::Memory, index)) |
1866 | | } |
1867 | | |
1868 | | // Grab-bag of "this adapter wants this thing from the main module". |
1869 | 0 | Import::MainModuleExport { name, kind } => { |
1870 | 0 | let instance = self.instance_index.unwrap(); |
1871 | 0 | let index = self.core_alias_export(Some(name), instance, name, *kind); |
1872 | 0 | Ok((*kind, index)) |
1873 | | } |
1874 | | |
1875 | | // A similar grab-bag to above but with a slightly different |
1876 | | // structure. Should probably refactor to make these two the same in |
1877 | | // the future. |
1878 | 0 | Import::Item(item) => { |
1879 | 0 | let instance = self.instance_for(item.which.to_custom_module()); |
1880 | 0 | let index = |
1881 | 0 | self.core_alias_export(Some(&item.name), instance, &item.name, item.kind); |
1882 | 0 | Ok((item.kind, index)) |
1883 | | } |
1884 | | |
1885 | | // Resource intrinsics related to exported resources. Despite being |
1886 | | // an exported resource the component still provides necessary |
1887 | | // intrinsics for manipulating resource state. These are all |
1888 | | // handled here using the resource types created during |
1889 | | // `declare_types_for_imported_intrinsics` above. |
1890 | 645 | Import::ExportedResourceDrop(_key, id) => { |
1891 | 645 | let index = self |
1892 | 645 | .component |
1893 | 645 | .resource_drop(self.type_encoding_maps.id_to_index[id]); |
1894 | 645 | Ok((ExportKind::Func, index)) |
1895 | | } |
1896 | 645 | Import::ExportedResourceRep(_key, id) => { |
1897 | 645 | let index = self |
1898 | 645 | .component |
1899 | 645 | .resource_rep(self.type_encoding_maps.id_to_index[id]); |
1900 | 645 | Ok((ExportKind::Func, index)) |
1901 | | } |
1902 | 645 | Import::ExportedResourceNew(_key, id) => { |
1903 | 645 | let index = self |
1904 | 645 | .component |
1905 | 645 | .resource_new(self.type_encoding_maps.id_to_index[id]); |
1906 | 645 | Ok((ExportKind::Func, index)) |
1907 | | } |
1908 | | |
1909 | | // And finally here at the end these cases are going to all fall |
1910 | | // through to the code below. This is where these are connected to a |
1911 | | // WIT `ImportedInterface` one way or another with the name that was |
1912 | | // detected during validation. |
1913 | 246 | Import::ImportedResourceDrop(key, iface, id) => { |
1914 | 246 | let ty = &resolve.types[*id]; |
1915 | 246 | let name = ty.name.as_ref().unwrap(); |
1916 | 246 | self.materialize_wit_import( |
1917 | 246 | shims, |
1918 | 246 | for_module, |
1919 | 246 | iface.map(|_| resolve.name_world_key(key)), |
1920 | 246 | &format!("{name}_drop"), |
1921 | 246 | key, |
1922 | 246 | AbiVariant::GuestImport, |
1923 | | ) |
1924 | | } |
1925 | 834 | Import::ExportedTaskReturn(key, interface, func) => { |
1926 | 834 | let (options, _sig) = task_return_options_and_type(resolve, func); |
1927 | 834 | let result_ty = func.result; |
1928 | 834 | if options.is_empty() || self.memory_index.is_some() { |
1929 | | // Note that an "import type encoder" is used here despite |
1930 | | // this being for an exported function if the `interface` |
1931 | | // is none, meaning that this is for a top-level world |
1932 | | // function. In that situation all types that can be |
1933 | | // referred to are imported, not exported. |
1934 | 593 | let mut encoder = if interface.is_none() { |
1935 | 52 | self.root_import_type_encoder(*interface) |
1936 | | } else { |
1937 | 541 | self.root_export_type_encoder(*interface) |
1938 | | }; |
1939 | | |
1940 | 593 | let result = match result_ty.as_ref() { |
1941 | 540 | Some(ty) => Some(encoder.encode_valtype(resolve, ty)?), |
1942 | 53 | None => None, |
1943 | | }; |
1944 | 593 | let metadata = &self.info.module_metadata_for(for_module); |
1945 | 593 | let encoding = metadata |
1946 | 593 | .export_encodings |
1947 | 593 | .get(resolve, key, &func.name) |
1948 | 593 | .ok_or_else(|| { |
1949 | 0 | anyhow!("missing component metadata for export of `{}`", func.name) |
1950 | 0 | })?; |
1951 | 593 | let options = options |
1952 | 593 | .into_iter(encoding, self.memory_index, None)? |
1953 | 593 | .collect::<Vec<_>>(); |
1954 | 593 | let index = self.component.task_return(result, options); |
1955 | 593 | Ok((ExportKind::Func, index)) |
1956 | | } else { |
1957 | 241 | let metadata = &self.info.module_metadata_for(for_module); |
1958 | 241 | let encoding = metadata |
1959 | 241 | .export_encodings |
1960 | 241 | .get(resolve, key, &func.name) |
1961 | 241 | .unwrap(); |
1962 | 241 | Ok(self.materialize_shim_import( |
1963 | 241 | shims, |
1964 | 241 | &ShimKind::TaskReturn { |
1965 | 241 | for_module, |
1966 | 241 | interface: *interface, |
1967 | 241 | func: &func.name, |
1968 | 241 | result: result_ty, |
1969 | 241 | encoding, |
1970 | 241 | }, |
1971 | 241 | )) |
1972 | | } |
1973 | | } |
1974 | | Import::BackpressureInc => { |
1975 | 689 | let index = self.component.backpressure_inc(); |
1976 | 689 | Ok((ExportKind::Func, index)) |
1977 | | } |
1978 | | Import::BackpressureDec => { |
1979 | 689 | let index = self.component.backpressure_dec(); |
1980 | 689 | Ok((ExportKind::Func, index)) |
1981 | | } |
1982 | | Import::WaitableSetWait => { |
1983 | 689 | if let Some(memory) = self.memory_index { |
1984 | 0 | let index = self.component.waitable_set_wait(memory); |
1985 | 0 | return Ok((ExportKind::Func, index)); |
1986 | 689 | } |
1987 | 689 | Ok(self.materialize_shim_import(shims, &ShimKind::WaitableSetWait)) |
1988 | | } |
1989 | | Import::WaitableSetPoll => { |
1990 | 689 | if let Some(memory) = self.memory_index { |
1991 | 0 | let index = self.component.waitable_set_poll(memory); |
1992 | 0 | return Ok((ExportKind::Func, index)); |
1993 | 689 | } |
1994 | 689 | Ok(self.materialize_shim_import(shims, &ShimKind::WaitableSetPoll)) |
1995 | | } |
1996 | | Import::SubtaskDrop => { |
1997 | 689 | let index = self.component.subtask_drop(); |
1998 | 689 | Ok((ExportKind::Func, index)) |
1999 | | } |
2000 | 689 | Import::SubtaskCancel { async_ } => { |
2001 | 689 | let index = self.component.subtask_cancel(*async_); |
2002 | 689 | Ok((ExportKind::Func, index)) |
2003 | | } |
2004 | 137 | Import::StreamNew(info) => { |
2005 | 137 | let ty = self.payload_type_index(info)?; |
2006 | 137 | let index = self.component.stream_new(ty); |
2007 | 137 | Ok((ExportKind::Func, index)) |
2008 | | } |
2009 | 137 | Import::StreamRead { info, async_ } => self.materialize_payload_import( |
2010 | 137 | shims, |
2011 | 137 | for_module, |
2012 | 137 | info, |
2013 | 137 | PayloadFuncKind::StreamRead, |
2014 | 137 | *async_, |
2015 | | ), |
2016 | 137 | Import::StreamWrite { info, async_ } => self.materialize_payload_import( |
2017 | 137 | shims, |
2018 | 137 | for_module, |
2019 | 137 | info, |
2020 | 137 | PayloadFuncKind::StreamWrite, |
2021 | 137 | *async_, |
2022 | | ), |
2023 | 137 | Import::StreamForward(info) => { |
2024 | 137 | let ty = self.payload_type_index(info)?; |
2025 | 137 | let index = self.component.stream_forward(ty); |
2026 | 137 | Ok((ExportKind::Func, index)) |
2027 | | } |
2028 | 137 | Import::StreamCancelRead { info, async_ } => { |
2029 | 137 | let ty = self.payload_type_index(info)?; |
2030 | 137 | let index = self.component.stream_cancel_read(ty, *async_); |
2031 | 137 | Ok((ExportKind::Func, index)) |
2032 | | } |
2033 | 137 | Import::StreamCancelWrite { info, async_ } => { |
2034 | 137 | let ty = self.payload_type_index(info)?; |
2035 | 137 | let index = self.component.stream_cancel_write(ty, *async_); |
2036 | 137 | Ok((ExportKind::Func, index)) |
2037 | | } |
2038 | 137 | Import::StreamDropReadable(info) => { |
2039 | 137 | let type_index = self.payload_type_index(info)?; |
2040 | 137 | let index = self.component.stream_drop_readable(type_index); |
2041 | 137 | Ok((ExportKind::Func, index)) |
2042 | | } |
2043 | 137 | Import::StreamDropWritable(info) => { |
2044 | 137 | let type_index = self.payload_type_index(info)?; |
2045 | 137 | let index = self.component.stream_drop_writable(type_index); |
2046 | 137 | Ok((ExportKind::Func, index)) |
2047 | | } |
2048 | 960 | Import::FutureNew(info) => { |
2049 | 960 | let ty = self.payload_type_index(info)?; |
2050 | 960 | let index = self.component.future_new(ty); |
2051 | 960 | Ok((ExportKind::Func, index)) |
2052 | | } |
2053 | 960 | Import::FutureRead { info, async_ } => self.materialize_payload_import( |
2054 | 960 | shims, |
2055 | 960 | for_module, |
2056 | 960 | info, |
2057 | 960 | PayloadFuncKind::FutureRead, |
2058 | 960 | *async_, |
2059 | | ), |
2060 | 960 | Import::FutureWrite { info, async_ } => self.materialize_payload_import( |
2061 | 960 | shims, |
2062 | 960 | for_module, |
2063 | 960 | info, |
2064 | 960 | PayloadFuncKind::FutureWrite, |
2065 | 960 | *async_, |
2066 | | ), |
2067 | 960 | Import::FutureForward(info) => { |
2068 | 960 | let ty = self.payload_type_index(info)?; |
2069 | 960 | let index = self.component.future_forward(ty); |
2070 | 960 | Ok((ExportKind::Func, index)) |
2071 | | } |
2072 | 960 | Import::FutureCancelRead { info, async_ } => { |
2073 | 960 | let ty = self.payload_type_index(info)?; |
2074 | 960 | let index = self.component.future_cancel_read(ty, *async_); |
2075 | 960 | Ok((ExportKind::Func, index)) |
2076 | | } |
2077 | 960 | Import::FutureCancelWrite { info, async_ } => { |
2078 | 960 | let ty = self.payload_type_index(info)?; |
2079 | 960 | let index = self.component.future_cancel_write(ty, *async_); |
2080 | 960 | Ok((ExportKind::Func, index)) |
2081 | | } |
2082 | 960 | Import::FutureDropReadable(info) => { |
2083 | 960 | let type_index = self.payload_type_index(info)?; |
2084 | 960 | let index = self.component.future_drop_readable(type_index); |
2085 | 960 | Ok((ExportKind::Func, index)) |
2086 | | } |
2087 | 960 | Import::FutureDropWritable(info) => { |
2088 | 960 | let type_index = self.payload_type_index(info)?; |
2089 | 960 | let index = self.component.future_drop_writable(type_index); |
2090 | 960 | Ok((ExportKind::Func, index)) |
2091 | | } |
2092 | 0 | Import::ErrorContextNew { encoding } => { |
2093 | 0 | if self.memory_index.is_some() { |
2094 | 0 | let options = (RequiredOptions::MEMORY | RequiredOptions::STRING_ENCODING) |
2095 | 0 | .into_iter(*encoding, self.memory_index, None)?; |
2096 | 0 | let index = self.component.error_context_new(options); |
2097 | 0 | return Ok((ExportKind::Func, index)); |
2098 | 0 | } |
2099 | 0 | Ok(self.materialize_shim_import( |
2100 | 0 | shims, |
2101 | 0 | &ShimKind::ErrorContextNew { |
2102 | 0 | encoding: *encoding, |
2103 | 0 | }, |
2104 | 0 | )) |
2105 | | } |
2106 | 0 | Import::ErrorContextDebugMessage { encoding } => Ok(self.materialize_shim_import( |
2107 | 0 | shims, |
2108 | 0 | &ShimKind::ErrorContextDebugMessage { |
2109 | 0 | for_module, |
2110 | 0 | encoding: *encoding, |
2111 | 0 | }, |
2112 | 0 | )), |
2113 | | Import::ErrorContextDrop => { |
2114 | 0 | let index = self.component.error_context_drop(); |
2115 | 0 | Ok((ExportKind::Func, index)) |
2116 | | } |
2117 | 834 | Import::WorldFunc(key, name, abi) => { |
2118 | 834 | self.materialize_wit_import(shims, for_module, None, name, key, *abi) |
2119 | | } |
2120 | 1.40k | Import::InterfaceFunc(key, _, name, abi) => self.materialize_wit_import( |
2121 | 1.40k | shims, |
2122 | 1.40k | for_module, |
2123 | 1.40k | Some(resolve.name_world_key(key)), |
2124 | 1.40k | name, |
2125 | 1.40k | key, |
2126 | 1.40k | *abi, |
2127 | | ), |
2128 | | |
2129 | | Import::WaitableSetNew => { |
2130 | 689 | let index = self.component.waitable_set_new(); |
2131 | 689 | Ok((ExportKind::Func, index)) |
2132 | | } |
2133 | | Import::WaitableSetDrop => { |
2134 | 689 | let index = self.component.waitable_set_drop(); |
2135 | 689 | Ok((ExportKind::Func, index)) |
2136 | | } |
2137 | | Import::WaitableJoin => { |
2138 | 689 | let index = self.component.waitable_join(); |
2139 | 689 | Ok((ExportKind::Func, index)) |
2140 | | } |
2141 | 689 | Import::ContextGet { ty, slot } => { |
2142 | 689 | let index = self.component.context_get((*ty).try_into()?, *slot); |
2143 | 689 | Ok((ExportKind::Func, index)) |
2144 | | } |
2145 | 689 | Import::ContextSet { ty, slot } => { |
2146 | 689 | let index = self.component.context_set((*ty).try_into()?, *slot); |
2147 | 689 | Ok((ExportKind::Func, index)) |
2148 | | } |
2149 | 0 | Import::TlsBaseGet { ty } => Ok(( |
2150 | 0 | ExportKind::Func, |
2151 | 0 | self.materialize_tls_base_import(false, (*ty).try_into()?), |
2152 | | )), |
2153 | 0 | Import::TlsBaseSet { ty } => Ok(( |
2154 | 0 | ExportKind::Func, |
2155 | 0 | self.materialize_tls_base_import(true, (*ty).try_into()?), |
2156 | | )), |
2157 | | Import::ExportedTaskCancel => { |
2158 | 689 | let index = self.component.task_cancel(); |
2159 | 689 | Ok((ExportKind::Func, index)) |
2160 | | } |
2161 | | Import::ThreadIndex => { |
2162 | 0 | let index = self.component.thread_index(); |
2163 | 0 | Ok((ExportKind::Func, index)) |
2164 | | } |
2165 | 0 | Import::ThreadNewIndirect => Ok(self.materialize_shim_import( |
2166 | 0 | shims, |
2167 | 0 | &ShimKind::ThreadNewIndirect { |
2168 | 0 | // This is fixed for now |
2169 | 0 | func_ty: FuncType::new([ValType::I32], []), |
2170 | 0 | }, |
2171 | 0 | )), |
2172 | | Import::ThreadResumeLater => { |
2173 | 0 | let index = self.component.thread_resume_later(); |
2174 | 0 | Ok((ExportKind::Func, index)) |
2175 | | } |
2176 | | Import::ThreadSuspend => { |
2177 | 0 | let index = self.component.thread_suspend(); |
2178 | 0 | Ok((ExportKind::Func, index)) |
2179 | | } |
2180 | | Import::ThreadYield => { |
2181 | 689 | let index = self.component.thread_yield(); |
2182 | 689 | Ok((ExportKind::Func, index)) |
2183 | | } |
2184 | | Import::ThreadSuspendThenResume => { |
2185 | 0 | let index = self.component.thread_suspend_then_resume(); |
2186 | 0 | Ok((ExportKind::Func, index)) |
2187 | | } |
2188 | | Import::ThreadYieldThenResume => { |
2189 | 0 | let index = self.component.thread_yield_then_resume(); |
2190 | 0 | Ok((ExportKind::Func, index)) |
2191 | | } |
2192 | | Import::ThreadSuspendThenPromote => { |
2193 | 0 | let index = self.component.thread_suspend_then_promote(); |
2194 | 0 | Ok((ExportKind::Func, index)) |
2195 | | } |
2196 | | Import::ThreadYieldThenPromote => { |
2197 | 0 | let index = self.component.thread_yield_then_promote(); |
2198 | 0 | Ok((ExportKind::Func, index)) |
2199 | | } |
2200 | | } |
2201 | 22.9k | } |
2202 | | |
2203 | | /// Helper to satisfy `__wasm_{get,set}_tls_base` imports. |
2204 | | /// |
2205 | | /// For more information on this see WebAssembly/wasi-libc#857 |
2206 | 0 | fn materialize_tls_base_import(&mut self, set: bool, ty: ValType) -> u32 { |
2207 | 0 | if self.info.can_use_context_slot_1() { |
2208 | 0 | if set { |
2209 | 0 | self.component.context_set(ty, 1) |
2210 | | } else { |
2211 | 0 | self.component.context_get(ty, 1) |
2212 | | } |
2213 | | } else { |
2214 | 0 | let name = if set { TLS_BASE_SET } else { TLS_BASE_GET }; |
2215 | 0 | self.core_alias_export( |
2216 | 0 | Some(name), |
2217 | 0 | self.shim_instance_index.unwrap(), |
2218 | 0 | name, |
2219 | 0 | ExportKind::Func, |
2220 | | ) |
2221 | | } |
2222 | 0 | } |
2223 | | |
2224 | | /// Helper for `materialize_import` above for materializing functions that |
2225 | | /// are part of the "shim module" generated. |
2226 | 4.62k | fn materialize_shim_import(&mut self, shims: &Shims<'_>, kind: &ShimKind) -> (ExportKind, u32) { |
2227 | 4.62k | let index = self.core_alias_export( |
2228 | 4.62k | Some(&shims.shims[kind].debug_name), |
2229 | 4.62k | self.shim_instance_index |
2230 | 4.62k | .expect("shim should be instantiated"), |
2231 | 4.62k | &shims.shims[kind].name, |
2232 | 4.62k | ExportKind::Func, |
2233 | | ); |
2234 | 4.62k | (ExportKind::Func, index) |
2235 | 4.62k | } |
2236 | | |
2237 | | /// Helper for `materialize_import` above for generating imports for |
2238 | | /// future/stream read/write intrinsics. |
2239 | 2.19k | fn materialize_payload_import( |
2240 | 2.19k | &mut self, |
2241 | 2.19k | shims: &Shims<'_>, |
2242 | 2.19k | for_module: CustomModule<'_>, |
2243 | 2.19k | info: &PayloadInfo, |
2244 | 2.19k | kind: PayloadFuncKind, |
2245 | 2.19k | async_: bool, |
2246 | 2.19k | ) -> Result<(ExportKind, u32)> { |
2247 | 2.19k | let shim_kind = ShimKind::PayloadFunc { |
2248 | 2.19k | for_module, |
2249 | 2.19k | info, |
2250 | 2.19k | kind, |
2251 | 2.19k | }; |
2252 | 2.19k | if shims.shims.get(&shim_kind).is_some() { |
2253 | 2.19k | return Ok(self.materialize_shim_import(shims, &shim_kind)); |
2254 | 0 | } |
2255 | 0 | let resolve = &self.info.encoder.metadata.resolve; |
2256 | 0 | let options = RequiredOptions::for_payload(resolve, info, &kind, async_); |
2257 | 0 | let func = self.encode_payload_func( |
2258 | 0 | shims, |
2259 | 0 | &for_module, |
2260 | 0 | info, |
2261 | 0 | &kind, |
2262 | 0 | options, |
2263 | 0 | ReallocSite::BeforeInstantiation, |
2264 | 0 | )?; |
2265 | 0 | Ok((ExportKind::Func, func)) |
2266 | 2.19k | } |
2267 | | |
2268 | | /// Helper for `materialize_import` above which specifically operates on |
2269 | | /// WIT-level functions identified by `interface_key`, `name`, and `abi`. |
2270 | 2.48k | fn materialize_wit_import( |
2271 | 2.48k | &mut self, |
2272 | 2.48k | shims: &Shims<'_>, |
2273 | 2.48k | for_module: CustomModule<'_>, |
2274 | 2.48k | interface_key: Option<String>, |
2275 | 2.48k | name: &String, |
2276 | 2.48k | key: &WorldKey, |
2277 | 2.48k | abi: AbiVariant, |
2278 | 2.48k | ) -> Result<(ExportKind, u32)> { |
2279 | 2.48k | let resolve = &self.info.encoder.metadata.resolve; |
2280 | 2.48k | let import = &self.info.import_map[&interface_key]; |
2281 | 2.48k | let (index, _, lowering) = import.lowerings.get_full(&(name.clone(), abi)).unwrap(); |
2282 | 2.48k | let metadata = self.info.module_metadata_for(for_module); |
2283 | | |
2284 | 2.48k | let index = match lowering { |
2285 | | // All direct lowerings can be `canon lower`'d here immediately |
2286 | | // and passed as arguments. |
2287 | 1.42k | Lowering::Direct { options } => { |
2288 | 1.42k | let func_index = match &import.interface { |
2289 | 851 | Some(interface) => { |
2290 | 851 | let instance_index = self.instances[interface]; |
2291 | 851 | self.component |
2292 | 851 | .alias_export(instance_index, name, ComponentExportKind::Func) |
2293 | | } |
2294 | 573 | None => self.imported_funcs[name], |
2295 | | }; |
2296 | 1.42k | let encoding = metadata |
2297 | 1.42k | .import_encodings |
2298 | 1.42k | .get(resolve, key, name) |
2299 | 1.42k | .ok_or_else(|| anyhow!("missing component metadata for import of `{name}`"))?; |
2300 | 1.42k | let realloc = self |
2301 | 1.42k | .info |
2302 | 1.42k | .exports_for(for_module) |
2303 | 1.42k | .import_realloc_for(import.interface, name); |
2304 | 1.42k | let realloc = self.realloc_index( |
2305 | 1.42k | shims, |
2306 | 1.42k | for_module, |
2307 | 1.42k | ReallocSite::BeforeInstantiation, |
2308 | 1.42k | *options, |
2309 | 1.42k | realloc, |
2310 | | ); |
2311 | 1.42k | let options = options |
2312 | 1.42k | .into_iter(encoding, self.memory_index, realloc)? |
2313 | 1.42k | .collect::<Vec<_>>(); |
2314 | 1.42k | self.component.lower_func(Some(name), func_index, options) |
2315 | | } |
2316 | | |
2317 | | // Indirect lowerings come from the shim that was previously |
2318 | | // created, so the specific export is loaded here and used as an |
2319 | | // import. |
2320 | | Lowering::Indirect { .. } => { |
2321 | 813 | let encoding = metadata.import_encodings.get(resolve, key, name).unwrap(); |
2322 | 813 | return Ok(self.materialize_shim_import( |
2323 | 813 | shims, |
2324 | 813 | &ShimKind::IndirectLowering { |
2325 | 813 | interface: interface_key, |
2326 | 813 | index, |
2327 | 813 | realloc: for_module, |
2328 | 813 | encoding, |
2329 | 813 | }, |
2330 | 813 | )); |
2331 | | } |
2332 | | |
2333 | | // A "resource drop" intrinsic only needs to find the index of the |
2334 | | // resource type itself and then the intrinsic is declared. |
2335 | 246 | Lowering::ResourceDrop(id) => { |
2336 | 246 | let resource_idx = self.lookup_resource_index(*id); |
2337 | 246 | self.component.resource_drop(resource_idx) |
2338 | | } |
2339 | | }; |
2340 | 1.67k | Ok((ExportKind::Func, index)) |
2341 | 2.48k | } |
2342 | | |
2343 | | /// Returns the core function index to use for the `realloc` canonical |
2344 | | /// option where the function is named `export` within `module`. |
2345 | 3.29k | fn realloc_index( |
2346 | 3.29k | &mut self, |
2347 | 3.29k | shims: &Shims<'_>, |
2348 | 3.29k | module: CustomModule<'_>, |
2349 | 3.29k | site: ReallocSite, |
2350 | 3.29k | options: RequiredOptions, |
2351 | 3.29k | export: Option<&str>, |
2352 | 3.29k | ) -> Option<u32> { |
2353 | 3.29k | if !options.contains(RequiredOptions::REALLOC) { |
2354 | 3.18k | return None; |
2355 | 115 | } |
2356 | 115 | let (module, export) = resolve_realloc(self.info, module, export)?; |
2357 | | |
2358 | 115 | if !realloc_needs_shim(self.info, site) { |
2359 | 115 | let instance = self.instance_for(module); |
2360 | 115 | return Some(self.core_alias_export(Some(export), instance, export, ExportKind::Func)); |
2361 | 0 | } |
2362 | | |
2363 | 0 | let kind = ShimKind::Realloc { module, export }; |
2364 | 0 | let shim = shims.shims.get(&kind).unwrap_or_else(|| { |
2365 | 0 | panic!( |
2366 | | "no shim was registered for the `realloc` \ |
2367 | | function `{export}` in `{}`", |
2368 | 0 | module.debug_name() |
2369 | | ) |
2370 | | }); |
2371 | 0 | let instance = self |
2372 | 0 | .shim_instance_index |
2373 | 0 | .expect("shim should be instantiated"); |
2374 | 0 | Some(self.core_alias_export( |
2375 | 0 | Some(&shim.debug_name), |
2376 | 0 | instance, |
2377 | 0 | &shim.name, |
2378 | 0 | ExportKind::Func, |
2379 | 0 | )) |
2380 | 3.29k | } |
2381 | | |
2382 | 14.4k | fn hooked_core_alias_export(&mut self, module: CustomModule<'_>, core_name: &str) -> u32 { |
2383 | 14.4k | let key = (module.to_imported_instance(), core_name.to_string()); |
2384 | 14.4k | if let Some(&wrapper_idx) = self.export_task_initialization_wrappers.get(&key) { |
2385 | 0 | wrapper_idx |
2386 | | } else { |
2387 | 14.4k | let instance = self.instance_for(module); |
2388 | 14.4k | self.core_alias_export(Some(core_name), instance, core_name, ExportKind::Func) |
2389 | | } |
2390 | 14.4k | } |
2391 | | |
2392 | | /// Convenience function to go from `CustomModule` to the instance index |
2393 | | /// corresponding to what that points to. |
2394 | 15.4k | fn instance_for(&self, module: CustomModule) -> u32 { |
2395 | 15.4k | match module { |
2396 | 15.4k | CustomModule::Main => self.instance_index.expect("instantiated by now"), |
2397 | 0 | CustomModule::Adapter(name) => self.adapter_instances[name], |
2398 | | } |
2399 | 15.4k | } |
2400 | | |
2401 | | /// Convenience function to go from `CustomModule` to the module index |
2402 | | /// corresponding to what that points to. |
2403 | 3.79k | fn module_for(&self, module: CustomModule) -> u32 { |
2404 | 3.79k | match module { |
2405 | 3.79k | CustomModule::Main => self.module_index.unwrap(), |
2406 | 0 | CustomModule::Adapter(name) => self.adapter_modules[name], |
2407 | | } |
2408 | 3.79k | } |
2409 | | |
2410 | | /// Convenience function which caches aliases created so repeated calls to |
2411 | | /// this function will all return the same index. |
2412 | 24.4k | fn core_alias_export( |
2413 | 24.4k | &mut self, |
2414 | 24.4k | debug_name: Option<&str>, |
2415 | 24.4k | instance: u32, |
2416 | 24.4k | name: &str, |
2417 | 24.4k | kind: ExportKind, |
2418 | 24.4k | ) -> u32 { |
2419 | 24.4k | *self |
2420 | 24.4k | .aliased_core_items |
2421 | 24.4k | .entry((instance, name.to_string())) |
2422 | 24.4k | .or_insert_with(|| { |
2423 | 22.7k | self.component |
2424 | 22.7k | .core_alias_export(debug_name, instance, name, kind) |
2425 | 22.7k | }) |
2426 | 24.4k | } |
2427 | | } |
2428 | | |
2429 | | /// A list of "shims" which start out during the component instantiation process |
2430 | | /// as functions which immediately trap due to a `call_indirect`-to-`null` but |
2431 | | /// will get filled in by the time the component instantiation process |
2432 | | /// completes. |
2433 | | /// |
2434 | | /// Shims currently include: |
2435 | | /// |
2436 | | /// * "Indirect functions" lowered from imported instances where the lowering |
2437 | | /// requires an item exported from the main module. These are indirect due to |
2438 | | /// the circular dependency between the module needing an import and the |
2439 | | /// import needing the module. |
2440 | | /// |
2441 | | /// * Adapter modules which convert from a historical ABI to the component |
2442 | | /// model's ABI (e.g. wasi preview1 to preview2) get a shim since the adapters |
2443 | | /// are currently indicated as always requiring the memory of the main module. |
2444 | | /// |
2445 | | /// This structure is created by `encode_shim_instantiation`. |
2446 | | #[derive(Default)] |
2447 | | struct Shims<'a> { |
2448 | | /// The list of all shims that a module will require. |
2449 | | shims: IndexMap<ShimKind<'a>, Shim<'a>>, |
2450 | | } |
2451 | | |
2452 | | struct Shim<'a> { |
2453 | | /// Canonical ABI options required by this shim, used during `canon lower` |
2454 | | /// operations. |
2455 | | options: RequiredOptions, |
2456 | | |
2457 | | /// The name, in the shim instance, of this shim. |
2458 | | /// |
2459 | | /// Currently this is `"0"`, `"1"`, ... |
2460 | | name: String, |
2461 | | |
2462 | | /// A human-readable debugging name for this shim, used in a core wasm |
2463 | | /// `name` section. |
2464 | | debug_name: String, |
2465 | | |
2466 | | /// Precise information about what this shim is a lowering of. |
2467 | | kind: ShimKind<'a>, |
2468 | | |
2469 | | /// Wasm type of this shim. |
2470 | | sig: WasmSignature, |
2471 | | } |
2472 | | |
2473 | | /// Which variation of `{stream|future}.{read|write}` we're emitting for a |
2474 | | /// `ShimKind::PayloadFunc`. |
2475 | | #[derive(Debug, Clone, Hash, Eq, PartialEq, Copy)] |
2476 | | enum PayloadFuncKind { |
2477 | | FutureWrite, |
2478 | | FutureRead, |
2479 | | StreamWrite, |
2480 | | StreamRead, |
2481 | | } |
2482 | | |
2483 | | #[derive(Debug, Clone, Hash, Eq, PartialEq)] |
2484 | | enum ShimKind<'a> { |
2485 | | /// This shim is a late indirect lowering of an imported function in a |
2486 | | /// component which is only possible after prior core wasm modules are |
2487 | | /// instantiated so their memories and functions are available. |
2488 | | IndirectLowering { |
2489 | | /// The name of the interface that's being lowered. |
2490 | | interface: Option<String>, |
2491 | | /// The index within the `lowerings` array of the function being lowered. |
2492 | | index: usize, |
2493 | | /// Which instance to pull the `realloc` function from, if necessary. |
2494 | | realloc: CustomModule<'a>, |
2495 | | /// The string encoding that this lowering is going to use. |
2496 | | encoding: StringEncoding, |
2497 | | }, |
2498 | | /// This shim is a core wasm function defined in an adapter module but isn't |
2499 | | /// available until the adapter module is itself instantiated. |
2500 | | Adapter { |
2501 | | /// The name of the adapter module this shim comes from. |
2502 | | adapter: &'a str, |
2503 | | /// The name of the export in the adapter module this shim points to. |
2504 | | func: &'a str, |
2505 | | }, |
2506 | | /// A shim used as the destructor for a resource which allows defining the |
2507 | | /// resource before the core module being instantiated. |
2508 | | ResourceDtor { |
2509 | | /// Which instance to pull the destructor function from. |
2510 | | module: CustomModule<'a>, |
2511 | | /// The exported function name of this destructor in the core module. |
2512 | | export: &'a str, |
2513 | | }, |
2514 | | /// A shim used for a `{stream|future}.{read|write}` built-in function, |
2515 | | /// which must refer to the core module instance's memory from/to which |
2516 | | /// payload values must be lifted/lowered. |
2517 | | PayloadFunc { |
2518 | | /// Which instance to pull the `realloc` function and string encoding |
2519 | | /// from, if necessary. |
2520 | | for_module: CustomModule<'a>, |
2521 | | /// Additional information regarding the function where this `stream` or |
2522 | | /// `future` type appeared, which we use in combination with |
2523 | | /// `for_module` to determine which `realloc` and string encoding to |
2524 | | /// use, as well as which type to specify when emitting the built-in. |
2525 | | info: &'a PayloadInfo, |
2526 | | /// Which variation of `{stream|future}.{read|write}` we're emitting. |
2527 | | kind: PayloadFuncKind, |
2528 | | }, |
2529 | | /// A shim used for the `waitable-set.wait` built-in function, which must |
2530 | | /// refer to the core module instance's memory to which results will be |
2531 | | /// written. |
2532 | | WaitableSetWait, |
2533 | | /// A shim used for the `waitable-set.poll` built-in function, which must |
2534 | | /// refer to the core module instance's memory to which results will be |
2535 | | /// written. |
2536 | | WaitableSetPoll, |
2537 | | /// Shim for `task.return` to handle a reference to a `memory` which may |
2538 | | TaskReturn { |
2539 | | /// The interface (optional) that owns `func` below. If `None` then it's |
2540 | | /// a world export. |
2541 | | interface: Option<InterfaceId>, |
2542 | | /// The function that this `task.return` is returning for, owned |
2543 | | /// within `interface` above. |
2544 | | func: &'a str, |
2545 | | /// The WIT type that `func` returns. |
2546 | | result: Option<Type>, |
2547 | | /// Which instance to pull the `realloc` function from, if necessary. |
2548 | | for_module: CustomModule<'a>, |
2549 | | /// String encoding to use in the ABI options. |
2550 | | encoding: StringEncoding, |
2551 | | }, |
2552 | | /// A shim used for the `error-context.new` built-in function, which must |
2553 | | /// refer to the core module instance's memory from which the debug message |
2554 | | /// will be read. |
2555 | | ErrorContextNew { |
2556 | | /// String encoding to use when lifting the debug message. |
2557 | | encoding: StringEncoding, |
2558 | | }, |
2559 | | /// A shim used for the `error-context.debug-message` built-in function, |
2560 | | /// which must refer to the core module instance's memory to which results |
2561 | | /// will be written. |
2562 | | ErrorContextDebugMessage { |
2563 | | /// Which instance to pull the `realloc` function from, if necessary. |
2564 | | for_module: CustomModule<'a>, |
2565 | | /// The string encoding to use when lowering the debug message. |
2566 | | encoding: StringEncoding, |
2567 | | }, |
2568 | | /// A shim used for the `thread.new-indirect` built-in function, which |
2569 | | /// must refer to the core module instance's indirect function table. |
2570 | | ThreadNewIndirect { |
2571 | | /// The function type to use when creating the thread. |
2572 | | func_ty: FuncType, |
2573 | | }, |
2574 | | /// A shim for a `realloc` function used as a canonical option. |
2575 | | Realloc { |
2576 | | /// Which instance the `realloc` function is exported from. |
2577 | | module: CustomModule<'a>, |
2578 | | /// The name of the `realloc` export within `module`. |
2579 | | export: &'a str, |
2580 | | }, |
2581 | | } |
2582 | | |
2583 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
2584 | | enum ReallocSite { |
2585 | | /// This `realloc` is needed before the main module is instantiated. |
2586 | | BeforeInstantiation, |
2587 | | /// This `realloc` is needed after the main module is instantiated, but |
2588 | | /// before the fixup module is instantiated. |
2589 | | AfterInstantiation, |
2590 | | } |
2591 | | |
2592 | 230 | fn realloc_needs_shim(world: &ComponentWorld<'_>, site: ReallocSite) -> bool { |
2593 | 230 | match site { |
2594 | | // The function isn't available at all yet, so an indirection is |
2595 | | // required no matter what. |
2596 | 0 | ReallocSite::BeforeInstantiation => true, |
2597 | | |
2598 | | // The function itself is available, but if task hooks are enabled then |
2599 | | // the hooked version of it is defined by the fixup module which hasn't |
2600 | | // been instantiated yet. |
2601 | 230 | ReallocSite::AfterInstantiation => world.info.exports.wasm_task_hook().is_some(), |
2602 | | } |
2603 | 230 | } |
2604 | | |
2605 | | /// Indicator for which module is being used for a lowering or where options |
2606 | | /// like `realloc` are drawn from. |
2607 | | /// |
2608 | | /// This is necessary for situations such as an imported function being lowered |
2609 | | /// into the main module and additionally into an adapter module. For example an |
2610 | | /// adapter might adapt from preview1 to preview2 for the standard library of a |
2611 | | /// programming language but the main module's custom application code may also |
2612 | | /// explicitly import from preview2. These two different lowerings of a preview2 |
2613 | | /// function are parameterized by this enumeration. |
2614 | | #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] |
2615 | | enum CustomModule<'a> { |
2616 | | /// This points to the "main module" which is generally the "output of LLVM" |
2617 | | /// or what a user wrote. |
2618 | | Main, |
2619 | | /// This is selecting an adapter module, identified by name here, where |
2620 | | /// something is being lowered into. |
2621 | | Adapter(&'a str), |
2622 | | } |
2623 | | |
2624 | | impl<'a> CustomModule<'a> { |
2625 | 3.79k | fn debug_name(&self) -> &'a str { |
2626 | 3.79k | match self { |
2627 | 3.79k | CustomModule::Main => "main", |
2628 | 0 | CustomModule::Adapter(s) => s, |
2629 | | } |
2630 | 3.79k | } |
2631 | | |
2632 | | /// Returns the instance, as imported into the fixup module, that this |
2633 | | /// module corresponds to. |
2634 | 14.4k | fn to_imported_instance(&self) -> fixup::ImportedInstance { |
2635 | 14.4k | match self { |
2636 | 14.4k | CustomModule::Main => fixup::ImportedInstance::Main, |
2637 | 0 | CustomModule::Adapter(s) => fixup::ImportedInstance::Adapter(s.to_string()), |
2638 | | } |
2639 | 14.4k | } |
2640 | | } |
2641 | | |
2642 | | impl<'a> Shims<'a> { |
2643 | | /// Adds all shims necessary for the instantiation of `for_module`. |
2644 | | /// |
2645 | | /// This function will iterate over all the imports required by this module |
2646 | | /// and for those that require a shim they're registered here. |
2647 | 3.79k | fn append_indirect( |
2648 | 3.79k | &mut self, |
2649 | 3.79k | world: &'a ComponentWorld<'a>, |
2650 | 3.79k | for_module: CustomModule<'a>, |
2651 | 3.79k | ) -> Result<()> { |
2652 | 3.79k | let module_imports = world.imports_for(for_module); |
2653 | 3.79k | let module_exports = world.exports_for(for_module); |
2654 | 3.79k | let resolve = &world.encoder.metadata.resolve; |
2655 | 3.79k | let memory_available = world.info.imports.imported_memory().is_some(); |
2656 | | |
2657 | 22.9k | for (module, field, import) in module_imports.imports() { |
2658 | 22.9k | match import { |
2659 | | // These imports don't require shims, they can be satisfied |
2660 | | // as-needed when required. |
2661 | | Import::ImportedResourceDrop(..) |
2662 | | | Import::MainModuleMemory(_) |
2663 | | | Import::MainModuleExport { .. } |
2664 | | | Import::Item(_) |
2665 | | | Import::ExportedResourceDrop(..) |
2666 | | | Import::ExportedResourceRep(..) |
2667 | | | Import::ExportedResourceNew(..) |
2668 | | | Import::ExportedTaskCancel |
2669 | | | Import::ErrorContextDrop |
2670 | | | Import::BackpressureInc |
2671 | | | Import::BackpressureDec |
2672 | | | Import::SubtaskDrop |
2673 | | | Import::SubtaskCancel { .. } |
2674 | | | Import::FutureNew(..) |
2675 | | | Import::StreamNew(..) |
2676 | | | Import::FutureForward { .. } |
2677 | | | Import::FutureCancelRead { .. } |
2678 | | | Import::FutureCancelWrite { .. } |
2679 | | | Import::FutureDropWritable { .. } |
2680 | | | Import::FutureDropReadable { .. } |
2681 | | | Import::StreamForward { .. } |
2682 | | | Import::StreamCancelRead { .. } |
2683 | | | Import::StreamCancelWrite { .. } |
2684 | | | Import::StreamDropWritable { .. } |
2685 | | | Import::StreamDropReadable { .. } |
2686 | | | Import::WaitableSetNew |
2687 | | | Import::WaitableSetDrop |
2688 | | | Import::WaitableJoin |
2689 | | | Import::ContextGet { .. } |
2690 | | | Import::ContextSet { .. } |
2691 | | | Import::TlsBaseGet { .. } |
2692 | | | Import::TlsBaseSet { .. } |
2693 | | | Import::ThreadIndex |
2694 | | | Import::ThreadResumeLater |
2695 | | | Import::ThreadSuspend { .. } |
2696 | | | Import::ThreadYield { .. } |
2697 | | | Import::ThreadSuspendThenResume { .. } |
2698 | | | Import::ThreadYieldThenResume { .. } |
2699 | | | Import::ThreadSuspendThenPromote { .. } |
2700 | 16.3k | | Import::ThreadYieldThenPromote { .. } => {} |
2701 | | |
2702 | | // If `task.return` needs to be indirect then generate a shim |
2703 | | // for it, otherwise skip the shim and let it get materialized |
2704 | | // naturally later. |
2705 | 834 | Import::ExportedTaskReturn(key, interface, func) => { |
2706 | 834 | let (options, sig) = task_return_options_and_type(resolve, func); |
2707 | 834 | if options.is_empty() || memory_available { |
2708 | 593 | continue; |
2709 | 241 | } |
2710 | 241 | let name = self.shims.len().to_string(); |
2711 | 241 | let encoding = world |
2712 | 241 | .module_metadata_for(for_module) |
2713 | 241 | .export_encodings |
2714 | 241 | .get(resolve, key, &func.name) |
2715 | 241 | .ok_or_else(|| { |
2716 | 0 | anyhow::anyhow!( |
2717 | | "missing component metadata for export of \ |
2718 | | `{module}::{field}`" |
2719 | | ) |
2720 | 0 | })?; |
2721 | 241 | self.push(Shim { |
2722 | 241 | name, |
2723 | 241 | debug_name: format!("task-return-{}", func.name), |
2724 | 241 | options, |
2725 | 241 | kind: ShimKind::TaskReturn { |
2726 | 241 | interface: *interface, |
2727 | 241 | func: &func.name, |
2728 | 241 | result: func.result, |
2729 | 241 | for_module, |
2730 | 241 | encoding, |
2731 | 241 | }, |
2732 | 241 | sig, |
2733 | 241 | }); |
2734 | | } |
2735 | | |
2736 | 960 | Import::FutureWrite { async_, info } => { |
2737 | 960 | self.append_indirect_payload_push( |
2738 | 960 | world, |
2739 | 960 | for_module, |
2740 | 960 | module, |
2741 | 960 | *async_, |
2742 | 960 | info, |
2743 | 960 | PayloadFuncKind::FutureWrite, |
2744 | 960 | vec![WasmType::I32; 2], |
2745 | 960 | vec![WasmType::I32], |
2746 | 960 | ); |
2747 | 960 | } |
2748 | 960 | Import::FutureRead { async_, info } => { |
2749 | 960 | self.append_indirect_payload_push( |
2750 | 960 | world, |
2751 | 960 | for_module, |
2752 | 960 | module, |
2753 | 960 | *async_, |
2754 | 960 | info, |
2755 | 960 | PayloadFuncKind::FutureRead, |
2756 | 960 | vec![WasmType::I32; 2], |
2757 | 960 | vec![WasmType::I32], |
2758 | 960 | ); |
2759 | 960 | } |
2760 | 137 | Import::StreamWrite { async_, info } => { |
2761 | 137 | self.append_indirect_payload_push( |
2762 | 137 | world, |
2763 | 137 | for_module, |
2764 | 137 | module, |
2765 | 137 | *async_, |
2766 | 137 | info, |
2767 | 137 | PayloadFuncKind::StreamWrite, |
2768 | 137 | vec![WasmType::I32; 3], |
2769 | 137 | vec![WasmType::I32], |
2770 | 137 | ); |
2771 | 137 | } |
2772 | 137 | Import::StreamRead { async_, info } => { |
2773 | 137 | self.append_indirect_payload_push( |
2774 | 137 | world, |
2775 | 137 | for_module, |
2776 | 137 | module, |
2777 | 137 | *async_, |
2778 | 137 | info, |
2779 | 137 | PayloadFuncKind::StreamRead, |
2780 | 137 | vec![WasmType::I32; 3], |
2781 | 137 | vec![WasmType::I32], |
2782 | 137 | ); |
2783 | 137 | } |
2784 | | |
2785 | | Import::WaitableSetWait => { |
2786 | 689 | if memory_available { |
2787 | 0 | continue; |
2788 | 689 | } |
2789 | 689 | let name = self.shims.len().to_string(); |
2790 | 689 | self.push(Shim { |
2791 | 689 | name, |
2792 | 689 | debug_name: "waitable-set.wait".to_string(), |
2793 | 689 | options: RequiredOptions::empty(), |
2794 | 689 | kind: ShimKind::WaitableSetWait, |
2795 | 689 | sig: WasmSignature { |
2796 | 689 | params: vec![WasmType::I32; 2], |
2797 | 689 | results: vec![WasmType::I32], |
2798 | 689 | indirect_params: false, |
2799 | 689 | retptr: false, |
2800 | 689 | }, |
2801 | 689 | }); |
2802 | | } |
2803 | | |
2804 | | Import::WaitableSetPoll => { |
2805 | 689 | if memory_available { |
2806 | 0 | continue; |
2807 | 689 | } |
2808 | 689 | let name = self.shims.len().to_string(); |
2809 | 689 | self.push(Shim { |
2810 | 689 | name, |
2811 | 689 | debug_name: "waitable-set.poll".to_string(), |
2812 | 689 | options: RequiredOptions::empty(), |
2813 | 689 | kind: ShimKind::WaitableSetPoll, |
2814 | 689 | sig: WasmSignature { |
2815 | 689 | params: vec![WasmType::I32; 2], |
2816 | 689 | results: vec![WasmType::I32], |
2817 | 689 | indirect_params: false, |
2818 | 689 | retptr: false, |
2819 | 689 | }, |
2820 | 689 | }); |
2821 | | } |
2822 | | |
2823 | 0 | Import::ErrorContextNew { encoding } => { |
2824 | 0 | if memory_available { |
2825 | 0 | continue; |
2826 | 0 | } |
2827 | 0 | let name = self.shims.len().to_string(); |
2828 | 0 | self.push(Shim { |
2829 | 0 | name, |
2830 | 0 | debug_name: "error-new".to_string(), |
2831 | 0 | options: RequiredOptions::MEMORY | RequiredOptions::STRING_ENCODING, |
2832 | 0 | kind: ShimKind::ErrorContextNew { |
2833 | 0 | encoding: *encoding, |
2834 | 0 | }, |
2835 | 0 | sig: WasmSignature { |
2836 | 0 | params: vec![WasmType::I32; 2], |
2837 | 0 | results: vec![WasmType::I32], |
2838 | 0 | indirect_params: false, |
2839 | 0 | retptr: false, |
2840 | 0 | }, |
2841 | 0 | }); |
2842 | | } |
2843 | | |
2844 | 0 | Import::ErrorContextDebugMessage { encoding } => { |
2845 | 0 | let name = self.shims.len().to_string(); |
2846 | 0 | let options = RequiredOptions::MEMORY |
2847 | 0 | | RequiredOptions::STRING_ENCODING |
2848 | 0 | | RequiredOptions::REALLOC; |
2849 | 0 | self.push(Shim { |
2850 | 0 | name, |
2851 | 0 | debug_name: "error-debug-message".to_string(), |
2852 | 0 | options, |
2853 | 0 | kind: ShimKind::ErrorContextDebugMessage { |
2854 | 0 | for_module, |
2855 | 0 | encoding: *encoding, |
2856 | 0 | }, |
2857 | 0 | sig: WasmSignature { |
2858 | 0 | params: vec![WasmType::I32; 2], |
2859 | 0 | results: vec![], |
2860 | 0 | indirect_params: false, |
2861 | 0 | retptr: false, |
2862 | 0 | }, |
2863 | 0 | }); |
2864 | 0 | let realloc = module_exports.import_realloc_fallback(); |
2865 | 0 | self.push_realloc( |
2866 | 0 | world, |
2867 | 0 | for_module, |
2868 | 0 | ReallocSite::AfterInstantiation, |
2869 | 0 | options, |
2870 | 0 | realloc, |
2871 | 0 | ); |
2872 | 0 | } |
2873 | | |
2874 | 0 | Import::ThreadNewIndirect => { |
2875 | 0 | let name = self.shims.len().to_string(); |
2876 | 0 | self.push(Shim { |
2877 | 0 | name, |
2878 | 0 | debug_name: "thread.new-indirect".to_string(), |
2879 | 0 | options: RequiredOptions::empty(), |
2880 | 0 | kind: ShimKind::ThreadNewIndirect { |
2881 | 0 | // This is fixed for now |
2882 | 0 | func_ty: FuncType::new([ValType::I32], vec![]), |
2883 | 0 | }, |
2884 | 0 | sig: WasmSignature { |
2885 | 0 | params: vec![WasmType::I32; 2], |
2886 | 0 | results: vec![WasmType::I32], |
2887 | 0 | indirect_params: false, |
2888 | 0 | retptr: false, |
2889 | 0 | }, |
2890 | 0 | }); |
2891 | 0 | } |
2892 | | |
2893 | | // Adapter imports into the main module must got through an |
2894 | | // indirection, so that's registered here. |
2895 | 0 | Import::AdapterExport { adapter, func, ty } => { |
2896 | 0 | let name = self.shims.len().to_string(); |
2897 | 0 | log::debug!("shim {name} is adapter `{module}::{field}`"); |
2898 | 0 | self.push(Shim { |
2899 | 0 | name, |
2900 | 0 | debug_name: format!("adapt-{module}-{field}"), |
2901 | 0 | // Pessimistically assume that all adapters require |
2902 | 0 | // memory in one form or another. While this isn't |
2903 | 0 | // technically true it's true enough for WASI. |
2904 | 0 | options: RequiredOptions::MEMORY, |
2905 | 0 | kind: ShimKind::Adapter { adapter, func }, |
2906 | 0 | sig: WasmSignature { |
2907 | 0 | params: ty.params().iter().map(to_wasm_type).collect(), |
2908 | 0 | results: ty.results().iter().map(to_wasm_type).collect(), |
2909 | 0 | indirect_params: false, |
2910 | 0 | retptr: false, |
2911 | 0 | }, |
2912 | 0 | }); |
2913 | | |
2914 | 0 | fn to_wasm_type(ty: &wasmparser::ValType) -> WasmType { |
2915 | 0 | match ty { |
2916 | 0 | wasmparser::ValType::I32 => WasmType::I32, |
2917 | 0 | wasmparser::ValType::I64 => WasmType::I64, |
2918 | 0 | wasmparser::ValType::F32 => WasmType::F32, |
2919 | 0 | wasmparser::ValType::F64 => WasmType::F64, |
2920 | 0 | _ => unreachable!(), |
2921 | | } |
2922 | 0 | } |
2923 | | } |
2924 | | |
2925 | | // WIT-level functions may require an indirection, so yield some |
2926 | | // metadata out of this `match` to the loop below to figure that |
2927 | | // out. |
2928 | 1.40k | Import::InterfaceFunc(key, _, name, abi) => { |
2929 | 1.40k | self.append_indirect_wit_func( |
2930 | 1.40k | world, |
2931 | 1.40k | for_module, |
2932 | 1.40k | module, |
2933 | 1.40k | field, |
2934 | 1.40k | key, |
2935 | 1.40k | name, |
2936 | 1.40k | Some(resolve.name_world_key(key)), |
2937 | 1.40k | *abi, |
2938 | 0 | )?; |
2939 | | } |
2940 | 834 | Import::WorldFunc(key, name, abi) => { |
2941 | 834 | self.append_indirect_wit_func( |
2942 | 834 | world, for_module, module, field, key, name, None, *abi, |
2943 | 0 | )?; |
2944 | | } |
2945 | | } |
2946 | | } |
2947 | | |
2948 | | // In addition to all the shims added for imports above this module also |
2949 | | // requires shims for resource destructors that it exports. Resource |
2950 | | // types are declared before the module is instantiated so the actual |
2951 | | // destructor is registered as a shim (defined here) and it's then |
2952 | | // filled in with the module's exports later. |
2953 | 25.9k | for (export_name, export) in module_exports.iter() { |
2954 | 25.9k | let id = match export { |
2955 | 645 | Export::ResourceDtor(id) => id, |
2956 | 25.2k | _ => continue, |
2957 | | }; |
2958 | 645 | let resource = resolve.types[*id].name.as_ref().unwrap(); |
2959 | 645 | let name = self.shims.len().to_string(); |
2960 | 645 | self.push(Shim { |
2961 | 645 | name, |
2962 | 645 | debug_name: format!("dtor-{resource}"), |
2963 | 645 | options: RequiredOptions::empty(), |
2964 | 645 | kind: ShimKind::ResourceDtor { |
2965 | 645 | module: for_module, |
2966 | 645 | export: export_name, |
2967 | 645 | }, |
2968 | 645 | sig: WasmSignature { |
2969 | 645 | params: vec![WasmType::I32], |
2970 | 645 | results: Vec::new(), |
2971 | 645 | indirect_params: false, |
2972 | 645 | retptr: false, |
2973 | 645 | }, |
2974 | 645 | }); |
2975 | | } |
2976 | | |
2977 | 3.79k | Ok(()) |
2978 | 3.79k | } |
2979 | | |
2980 | | /// Helper of `append_indirect` above which pushes information for |
2981 | | /// futures/streams read/write intrinsics. |
2982 | 2.19k | fn append_indirect_payload_push( |
2983 | 2.19k | &mut self, |
2984 | 2.19k | world: &'a ComponentWorld<'a>, |
2985 | 2.19k | for_module: CustomModule<'a>, |
2986 | 2.19k | module: &str, |
2987 | 2.19k | async_: bool, |
2988 | 2.19k | info: &'a PayloadInfo, |
2989 | 2.19k | kind: PayloadFuncKind, |
2990 | 2.19k | params: Vec<WasmType>, |
2991 | 2.19k | results: Vec<WasmType>, |
2992 | 2.19k | ) { |
2993 | 2.19k | let resolve = &world.encoder.metadata.resolve; |
2994 | 2.19k | let debug_name = format!("{module}-{}", info.name); |
2995 | 2.19k | let name = self.shims.len().to_string(); |
2996 | | |
2997 | 2.19k | let options = RequiredOptions::for_payload(resolve, info, &kind, async_); |
2998 | 2.19k | if !options.contains(RequiredOptions::REALLOC) |
2999 | 2.16k | && world.info.imports.imported_memory().is_some() |
3000 | | { |
3001 | 0 | return; |
3002 | 2.19k | } |
3003 | 2.19k | self.push(Shim { |
3004 | 2.19k | name, |
3005 | 2.19k | debug_name, |
3006 | 2.19k | options, |
3007 | 2.19k | kind: ShimKind::PayloadFunc { |
3008 | 2.19k | for_module, |
3009 | 2.19k | info, |
3010 | 2.19k | kind, |
3011 | 2.19k | }, |
3012 | 2.19k | sig: WasmSignature { |
3013 | 2.19k | params, |
3014 | 2.19k | results, |
3015 | 2.19k | indirect_params: false, |
3016 | 2.19k | retptr: false, |
3017 | 2.19k | }, |
3018 | 2.19k | }); |
3019 | 2.19k | let realloc = payload_realloc_name(world, for_module, info); |
3020 | 2.19k | self.push_realloc( |
3021 | 2.19k | world, |
3022 | 2.19k | for_module, |
3023 | 2.19k | ReallocSite::AfterInstantiation, |
3024 | 2.19k | options, |
3025 | 2.19k | realloc, |
3026 | | ); |
3027 | 2.19k | } |
3028 | | |
3029 | | /// Helper for `append_indirect` above which will conditionally push a shim |
3030 | | /// for the WIT function specified by `interface_key`, `name`, and `abi`. |
3031 | 2.23k | fn append_indirect_wit_func( |
3032 | 2.23k | &mut self, |
3033 | 2.23k | world: &'a ComponentWorld<'a>, |
3034 | 2.23k | for_module: CustomModule<'a>, |
3035 | 2.23k | module: &str, |
3036 | 2.23k | field: &str, |
3037 | 2.23k | key: &WorldKey, |
3038 | 2.23k | name: &String, |
3039 | 2.23k | interface_key: Option<String>, |
3040 | 2.23k | abi: AbiVariant, |
3041 | 2.23k | ) -> Result<()> { |
3042 | 2.23k | let resolve = &world.encoder.metadata.resolve; |
3043 | 2.23k | let metadata = world.module_metadata_for(for_module); |
3044 | 2.23k | let interface = &world.import_map[&interface_key]; |
3045 | 2.23k | let (index, _, lowering) = interface.lowerings.get_full(&(name.clone(), abi)).unwrap(); |
3046 | 2.23k | let shim_name = self.shims.len().to_string(); |
3047 | 2.23k | match lowering { |
3048 | 0 | Lowering::ResourceDrop(_) => {} |
3049 | | |
3050 | 1.42k | Lowering::Direct { options } => { |
3051 | 1.42k | let realloc = world |
3052 | 1.42k | .exports_for(for_module) |
3053 | 1.42k | .import_realloc_for(interface.interface, name); |
3054 | 1.42k | self.push_realloc( |
3055 | 1.42k | world, |
3056 | 1.42k | for_module, |
3057 | 1.42k | ReallocSite::BeforeInstantiation, |
3058 | 1.42k | *options, |
3059 | 1.42k | realloc, |
3060 | 1.42k | ); |
3061 | 1.42k | } |
3062 | | |
3063 | 813 | Lowering::Indirect { sig, options } => { |
3064 | 813 | log::debug!( |
3065 | | "shim {shim_name} is import `{module}::{field}` lowering {index} `{name}`", |
3066 | | ); |
3067 | 813 | let encoding = metadata |
3068 | 813 | .import_encodings |
3069 | 813 | .get(resolve, key, name) |
3070 | 813 | .ok_or_else(|| { |
3071 | 0 | anyhow::anyhow!( |
3072 | | "missing component metadata for import of \ |
3073 | | `{module}::{field}`" |
3074 | | ) |
3075 | 0 | })?; |
3076 | 813 | self.push(Shim { |
3077 | 813 | name: shim_name, |
3078 | 813 | debug_name: format!("indirect-{module}-{field}"), |
3079 | 813 | options: *options, |
3080 | 813 | kind: ShimKind::IndirectLowering { |
3081 | 813 | interface: interface_key, |
3082 | 813 | index, |
3083 | 813 | realloc: for_module, |
3084 | 813 | encoding, |
3085 | 813 | }, |
3086 | 813 | sig: sig.clone(), |
3087 | 813 | }); |
3088 | | |
3089 | 813 | let realloc = world |
3090 | 813 | .exports_for(for_module) |
3091 | 813 | .import_realloc_for(interface.interface, name); |
3092 | 813 | self.push_realloc( |
3093 | 813 | world, |
3094 | 813 | for_module, |
3095 | 813 | ReallocSite::AfterInstantiation, |
3096 | 813 | *options, |
3097 | 813 | realloc, |
3098 | | ); |
3099 | | } |
3100 | | } |
3101 | | |
3102 | 2.23k | Ok(()) |
3103 | 2.23k | } |
3104 | | |
3105 | | /// Registers a shim, if one is necessary, for the `realloc` function named |
3106 | | /// `export` within `for_module` which is going to be used at `site` by |
3107 | | /// something requiring `options`. |
3108 | 4.43k | fn push_realloc( |
3109 | 4.43k | &mut self, |
3110 | 4.43k | world: &'a ComponentWorld<'a>, |
3111 | 4.43k | for_module: CustomModule<'a>, |
3112 | 4.43k | site: ReallocSite, |
3113 | 4.43k | options: RequiredOptions, |
3114 | 4.43k | export: Option<&'a str>, |
3115 | 4.43k | ) { |
3116 | 4.43k | if !options.contains(RequiredOptions::REALLOC) { |
3117 | 4.31k | return; |
3118 | 115 | } |
3119 | 115 | let Some((for_module, export)) = resolve_realloc(world, for_module, export) else { |
3120 | 0 | return; |
3121 | | }; |
3122 | 115 | if !realloc_needs_shim(world, site) { |
3123 | 115 | return; |
3124 | 0 | } |
3125 | 0 | let name = self.shims.len().to_string(); |
3126 | 0 | self.push(Shim { |
3127 | 0 | name, |
3128 | 0 | debug_name: format!("realloc-{}-{export}", for_module.debug_name()), |
3129 | 0 | options: RequiredOptions::empty(), |
3130 | 0 | kind: ShimKind::Realloc { |
3131 | 0 | module: for_module, |
3132 | 0 | export, |
3133 | 0 | }, |
3134 | 0 | sig: WasmSignature { |
3135 | 0 | params: vec![WasmType::I32; 4], |
3136 | 0 | results: vec![WasmType::I32], |
3137 | 0 | indirect_params: false, |
3138 | 0 | retptr: false, |
3139 | 0 | }, |
3140 | 0 | }); |
3141 | 4.43k | } |
3142 | | |
3143 | 5.27k | fn push(&mut self, shim: Shim<'a>) { |
3144 | | // Only one shim per `ShimKind` is retained, so if it's already present |
3145 | | // don't overwrite it. If it's not present though go ahead and insert |
3146 | | // it. |
3147 | 5.27k | if !self.shims.contains_key(&shim.kind) { |
3148 | 4.13k | self.shims.insert(shim.kind.clone(), shim); |
3149 | 4.13k | } |
3150 | 5.27k | } |
3151 | | } |
3152 | | |
3153 | | /// Resolves which module, and which export of it, provides the `realloc` |
3154 | | /// canonical option for `module`, where `export` is `module`'s own realloc |
3155 | | /// export if it has one. |
3156 | | /// |
3157 | | /// Libraries in a shared-everything link which don't export a realloc of their |
3158 | | /// own fall back to the one shared by the rest of the link |
3159 | 749 | fn resolve_realloc<'a>( |
3160 | 749 | world: &'a ComponentWorld<'a>, |
3161 | 749 | module: CustomModule<'a>, |
3162 | 749 | export: Option<&'a str>, |
3163 | 749 | ) -> Option<(CustomModule<'a>, &'a str)> { |
3164 | 749 | if let Some(export) = export { |
3165 | 749 | return Some((module, export)); |
3166 | 0 | } |
3167 | 0 | match module { |
3168 | | // If the main module doesn't have a realloc, then there's nothing to |
3169 | | // use. |
3170 | 0 | CustomModule::Main => None, |
3171 | | |
3172 | | // For libraries fallback to the realloc in the main module, if present. |
3173 | 0 | CustomModule::Adapter(name) => { |
3174 | 0 | let _info = world.adapters[name].library_info.as_ref()?; |
3175 | 0 | let main_realloc = world.info.exports.general_purpose_realloc()?; |
3176 | 0 | Some((CustomModule::Main, main_realloc)) |
3177 | | } |
3178 | | } |
3179 | 749 | } |
3180 | | |
3181 | 3.25k | fn payload_realloc_name<'a>( |
3182 | 3.25k | world: &'a ComponentWorld<'_>, |
3183 | 3.25k | for_module: CustomModule<'_>, |
3184 | 3.25k | info: &PayloadInfo, |
3185 | 3.25k | ) -> Option<&'a str> { |
3186 | 3.25k | let exports = world.exports_for(for_module); |
3187 | 3.25k | match &info.ty { |
3188 | 3.25k | PayloadType::Type { function, .. } => { |
3189 | 3.25k | if info.imported { |
3190 | 688 | exports.import_realloc_for(info.interface, function) |
3191 | | } else { |
3192 | 2.56k | exports.export_realloc_for(&info.key, function) |
3193 | | } |
3194 | | } |
3195 | 0 | PayloadType::UnitFuture | PayloadType::UnitStream => None, |
3196 | | } |
3197 | 3.25k | } |
3198 | | |
3199 | 2.43k | fn task_return_options_and_type( |
3200 | 2.43k | resolve: &Resolve, |
3201 | 2.43k | func: &Function, |
3202 | 2.43k | ) -> (RequiredOptions, WasmSignature) { |
3203 | 2.43k | let func_tmp = Function { |
3204 | 2.43k | name: String::new(), |
3205 | 2.43k | kind: FunctionKind::Freestanding, |
3206 | 2.43k | params: match &func.result { |
3207 | 2.27k | Some(ty) => vec![Param { |
3208 | 2.27k | name: "a".to_string(), |
3209 | 2.27k | ty: *ty, |
3210 | 2.27k | span: Default::default(), |
3211 | 2.27k | }], |
3212 | 162 | None => Vec::new(), |
3213 | | }, |
3214 | 2.43k | result: None, |
3215 | 2.43k | docs: Default::default(), |
3216 | 2.43k | stability: Stability::Unknown, |
3217 | 2.43k | span: Default::default(), |
3218 | 2.43k | external_id: None, |
3219 | | }; |
3220 | 2.43k | let abi = AbiVariant::GuestImport; |
3221 | 2.43k | let mut options = RequiredOptions::for_import(resolve, func, abi); |
3222 | | // `task.return` does not support a `realloc` canonical option. |
3223 | 2.43k | options.remove(RequiredOptions::REALLOC); |
3224 | 2.43k | let sig = resolve.wasm_signature(abi, &func_tmp); |
3225 | 2.43k | (options, sig) |
3226 | 2.43k | } |
3227 | | |
3228 | | /// Alias argument to an instantiation |
3229 | | #[derive(Clone, Debug)] |
3230 | | pub struct Item { |
3231 | | pub alias: String, |
3232 | | pub kind: ExportKind, |
3233 | | pub which: MainOrAdapter, |
3234 | | pub name: String, |
3235 | | } |
3236 | | |
3237 | | /// Module argument to an instantiation |
3238 | | #[derive(Debug, PartialEq, Clone)] |
3239 | | pub enum MainOrAdapter { |
3240 | | Main, |
3241 | | Adapter(String), |
3242 | | } |
3243 | | |
3244 | | impl MainOrAdapter { |
3245 | 0 | fn to_custom_module(&self) -> CustomModule<'_> { |
3246 | 0 | match self { |
3247 | 0 | MainOrAdapter::Main => CustomModule::Main, |
3248 | 0 | MainOrAdapter::Adapter(s) => CustomModule::Adapter(s), |
3249 | | } |
3250 | 0 | } |
3251 | | } |
3252 | | |
3253 | | /// Module instantiation argument |
3254 | | #[derive(Clone)] |
3255 | | pub enum Instance { |
3256 | | /// Module argument |
3257 | | MainOrAdapter(MainOrAdapter), |
3258 | | |
3259 | | /// Alias argument |
3260 | | Items(Vec<Item>), |
3261 | | } |
3262 | | |
3263 | | /// Provides fine-grained control of how a library module is instantiated |
3264 | | /// relative to other module instances |
3265 | | #[derive(Clone)] |
3266 | | pub struct LibraryInfo { |
3267 | | /// Instantiation arguments |
3268 | | pub arguments: Vec<(String, Instance)>, |
3269 | | } |
3270 | | |
3271 | | /// Represents an adapter or library to be instantiated as part of the component |
3272 | | pub(super) struct Adapter { |
3273 | | /// The wasm of the module itself, with `component-type` sections stripped |
3274 | | wasm: Vec<u8>, |
3275 | | |
3276 | | /// The metadata for the adapter |
3277 | | metadata: ModuleMetadata, |
3278 | | |
3279 | | /// The set of exports from the final world which are defined by this |
3280 | | /// adapter or library |
3281 | | required_exports: IndexSet<WorldKey>, |
3282 | | |
3283 | | /// If present, treat this module as a library rather than a "minimal" adapter |
3284 | | /// |
3285 | | /// TODO: We should refactor how various flavors of module are represented |
3286 | | /// and differentiated to avoid mistaking one for another. |
3287 | | library_info: Option<LibraryInfo>, |
3288 | | } |
3289 | | |
3290 | | /// An encoder of components based on `wit` interface definitions. |
3291 | | #[derive(Default)] |
3292 | | pub struct ComponentEncoder { |
3293 | | module: Vec<u8>, |
3294 | | module_import_map: Option<ModuleImportMap>, |
3295 | | pub(super) metadata: Bindgen, |
3296 | | validate: bool, |
3297 | | pub(super) main_module_exports: IndexSet<WorldKey>, |
3298 | | pub(super) adapters: IndexMap<String, Adapter>, |
3299 | | import_name_map: HashMap<String, String>, |
3300 | | realloc_via_memory_grow: bool, |
3301 | | merge_imports_based_on_semver: Option<bool>, |
3302 | | pub(super) reject_legacy_names: bool, |
3303 | | debug_names: bool, |
3304 | | shim_return_call_ref: bool, |
3305 | | emit_canonical_names: bool, |
3306 | | } |
3307 | | |
3308 | | impl ComponentEncoder { |
3309 | | /// Set the core module to encode as a component. |
3310 | | /// This method will also parse any component type information stored in custom sections |
3311 | | /// inside the module and add them as the interface, imports, and exports. |
3312 | | /// It will also add any producers information inside the component type information to the |
3313 | | /// core module. |
3314 | 3.79k | pub fn module(&mut self, module: &[u8]) -> Result<&mut Self> { |
3315 | 3.79k | let (wasm, metadata) = self.decode(module.as_ref())?; |
3316 | 3.79k | let (wasm, module_import_map) = ModuleImportMap::new(wasm)?; |
3317 | 3.79k | self.module = if let Some(producers) = &metadata.producers { |
3318 | 3.79k | producers.add_to_wasm(&wasm)? |
3319 | | } else { |
3320 | 0 | wasm.to_vec() |
3321 | | }; |
3322 | 3.79k | let exports = self |
3323 | 3.79k | .merge_metadata(metadata) |
3324 | 3.79k | .context("failed merge WIT metadata for module with previous metadata")?; |
3325 | 3.79k | self.main_module_exports.extend(exports); |
3326 | 3.79k | self.module_import_map = module_import_map; |
3327 | 3.79k | Ok(self) |
3328 | 3.79k | } |
3329 | | |
3330 | 3.79k | fn decode<'a>(&self, wasm: &'a [u8]) -> Result<(Cow<'a, [u8]>, Bindgen)> { |
3331 | 3.79k | let (bytes, metadata) = metadata::decode(wasm)?; |
3332 | 3.79k | match bytes { |
3333 | 3.79k | Some(wasm) => Ok((Cow::Owned(wasm), metadata)), |
3334 | 0 | None => Ok((Cow::Borrowed(wasm), metadata)), |
3335 | | } |
3336 | 3.79k | } |
3337 | | |
3338 | 3.79k | fn merge_metadata(&mut self, metadata: Bindgen) -> Result<IndexSet<WorldKey>> { |
3339 | 3.79k | self.metadata.merge(metadata) |
3340 | 3.79k | } |
3341 | | |
3342 | | /// Sets whether or not the encoder will validate its output. |
3343 | 0 | pub fn validate(&mut self, validate: bool) -> &mut Self { |
3344 | 0 | self.validate = validate; |
3345 | 0 | self |
3346 | 0 | } |
3347 | | |
3348 | | /// Sets whether or not to generate debug names in the output component. |
3349 | 3.79k | pub fn debug_names(&mut self, debug_names: bool) -> &mut Self { |
3350 | 3.79k | self.debug_names = debug_names; |
3351 | 3.79k | self |
3352 | 3.79k | } |
3353 | | |
3354 | | /// Configures whether the shim module, if needed, will use |
3355 | | /// `return_call_ref` as opposed to the default `call_indirect`. |
3356 | 3.79k | pub fn shim_return_call_ref(&mut self, enable: bool) -> &mut Self { |
3357 | 3.79k | self.shim_return_call_ref = enable; |
3358 | 3.79k | self |
3359 | 3.79k | } |
3360 | | |
3361 | | /// Sets whether to merge imports based on semver to the specified value. |
3362 | | /// |
3363 | | /// This affects how when to WIT worlds are merged together, for example |
3364 | | /// from two different libraries, whether their imports are unified when the |
3365 | | /// semver version ranges for interface allow it. |
3366 | | /// |
3367 | | /// This is enabled by default. |
3368 | 0 | pub fn merge_imports_based_on_semver(&mut self, merge: bool) -> &mut Self { |
3369 | 0 | self.merge_imports_based_on_semver = Some(merge); |
3370 | 0 | self |
3371 | 0 | } |
3372 | | |
3373 | | /// Sets whether to emit canonical interface names in the component binary. |
3374 | | /// |
3375 | | /// When enabled, import/export names use canonical version prefixes (e.g., |
3376 | | /// `wasi:cli/exit@0.2` instead of `wasi:cli/exit@0.2.1`) and the |
3377 | | /// `version_suffix` field is populated. |
3378 | | /// |
3379 | | /// This is disabled by default. |
3380 | 0 | pub fn emit_canonical_names(&mut self, emit: bool) -> &mut Self { |
3381 | 0 | self.emit_canonical_names = emit; |
3382 | 0 | self |
3383 | 0 | } |
3384 | | |
3385 | | /// Sets whether to reject the historical mangling/name scheme for core wasm |
3386 | | /// imports/exports as they map to the component model. |
3387 | | /// |
3388 | | /// The `wit-component` crate supported a different set of names prior to |
3389 | | /// WebAssembly/component-model#378 and this can be used to disable this |
3390 | | /// support. |
3391 | | /// |
3392 | | /// This is disabled by default. |
3393 | 0 | pub fn reject_legacy_names(&mut self, reject: bool) -> &mut Self { |
3394 | 0 | self.reject_legacy_names = reject; |
3395 | 0 | self |
3396 | 0 | } |
3397 | | |
3398 | | /// Specifies a new adapter which is used to translate from a historical |
3399 | | /// wasm ABI to the canonical ABI and the `interface` provided. |
3400 | | /// |
3401 | | /// This is primarily used to polyfill, for example, |
3402 | | /// `wasi_snapshot_preview1` with a component-model using interface. The |
3403 | | /// `name` provided is the module name of the adapter that is being |
3404 | | /// polyfilled, for example `"wasi_snapshot_preview1"`. |
3405 | | /// |
3406 | | /// The `bytes` provided is a core wasm module which implements the `name` |
3407 | | /// interface in terms of the `interface` interface. This core wasm module |
3408 | | /// is severely restricted in its shape, for example it cannot have any data |
3409 | | /// segments or element segments. |
3410 | | /// |
3411 | | /// The `interface` provided is the component-model-using-interface that the |
3412 | | /// wasm module specified by `bytes` imports. The `bytes` will then import |
3413 | | /// `interface` and export functions to get imported from the module `name` |
3414 | | /// in the core wasm that's being wrapped. |
3415 | 0 | pub fn adapter(&mut self, name: &str, bytes: &[u8]) -> Result<&mut Self> { |
3416 | 0 | self.library_or_adapter(name, bytes, None) |
3417 | 0 | } |
3418 | | |
3419 | | /// Specifies a shared-everything library to link into the component. |
3420 | | /// |
3421 | | /// Unlike adapters, libraries _may_ have data and/or element segments, but |
3422 | | /// they must operate on an imported memory and table, respectively. In |
3423 | | /// this case, the correct amount of space is presumed to have been |
3424 | | /// statically allocated in the main module's memory and table at the |
3425 | | /// offsets which the segments target, e.g. as arranged by |
3426 | | /// [super::linking::Linker]. |
3427 | | /// |
3428 | | /// Libraries are treated similarly to adapters, except that they are not |
3429 | | /// "minified" the way adapters are, and instantiation is controlled |
3430 | | /// declaratively via the `library_info` parameter. |
3431 | 0 | pub fn library( |
3432 | 0 | &mut self, |
3433 | 0 | name: &str, |
3434 | 0 | bytes: &[u8], |
3435 | 0 | library_info: LibraryInfo, |
3436 | 0 | ) -> Result<&mut Self> { |
3437 | 0 | self.library_or_adapter(name, bytes, Some(library_info)) |
3438 | 0 | } |
3439 | | |
3440 | 0 | fn library_or_adapter( |
3441 | 0 | &mut self, |
3442 | 0 | name: &str, |
3443 | 0 | bytes: &[u8], |
3444 | 0 | library_info: Option<LibraryInfo>, |
3445 | 0 | ) -> Result<&mut Self> { |
3446 | 0 | let (wasm, mut metadata) = self.decode(bytes)?; |
3447 | | // Merge the adapter's document into our own document to have one large |
3448 | | // document, and then afterwards merge worlds as well. |
3449 | | // |
3450 | | // Note that the `metadata` tracking import/export encodings is removed |
3451 | | // since this adapter can get different lowerings and is allowed to |
3452 | | // differ from the main module. This is then tracked within the |
3453 | | // `Adapter` structure produced below. |
3454 | 0 | let adapter_metadata = mem::take(&mut metadata.metadata); |
3455 | 0 | let exports = self.merge_metadata(metadata).with_context(|| { |
3456 | 0 | format!("failed to merge WIT packages of adapter `{name}` into main packages") |
3457 | 0 | })?; |
3458 | 0 | if let Some(library_info) = &library_info { |
3459 | | // Validate that all referenced modules can be resolved. |
3460 | 0 | for (_, instance) in &library_info.arguments { |
3461 | 0 | let resolve = |which: &_| match which { |
3462 | 0 | MainOrAdapter::Main => Ok(()), |
3463 | 0 | MainOrAdapter::Adapter(name) => { |
3464 | 0 | if self.adapters.contains_key(name.as_str()) { |
3465 | 0 | Ok(()) |
3466 | | } else { |
3467 | 0 | Err(anyhow!("instance refers to unknown adapter `{name}`")) |
3468 | | } |
3469 | | } |
3470 | 0 | }; |
3471 | | |
3472 | 0 | match instance { |
3473 | 0 | Instance::MainOrAdapter(which) => resolve(which)?, |
3474 | 0 | Instance::Items(items) => { |
3475 | 0 | for item in items { |
3476 | 0 | resolve(&item.which)?; |
3477 | | } |
3478 | | } |
3479 | | } |
3480 | | } |
3481 | 0 | } |
3482 | 0 | self.adapters.insert( |
3483 | 0 | name.to_string(), |
3484 | 0 | Adapter { |
3485 | 0 | wasm: wasm.to_vec(), |
3486 | 0 | metadata: adapter_metadata, |
3487 | 0 | required_exports: exports, |
3488 | 0 | library_info, |
3489 | 0 | }, |
3490 | | ); |
3491 | 0 | Ok(self) |
3492 | 0 | } |
3493 | | |
3494 | | /// Whether adapters use `memory.grow` for realloc and stack allocation. |
3495 | | /// |
3496 | | /// By default an adapter imports `cabi_realloc` from the main module. |
3497 | | /// Setting this to `true` makes it allocate with `memory.grow` instead, |
3498 | | /// which can be useful if `cabi_realloc` cannot be called before the host |
3499 | | /// runtime is initialized. |
3500 | | /// |
3501 | | /// This only affects modules added with [`ComponentEncoder::adapter`]. It |
3502 | | /// has no effect on the main module and does not synthesize a |
3503 | | /// `cabi_realloc` for a module that doesn't export one. |
3504 | 3.79k | pub fn realloc_via_memory_grow(&mut self, value: bool) -> &mut Self { |
3505 | 3.79k | self.realloc_via_memory_grow = value; |
3506 | 3.79k | self |
3507 | 3.79k | } |
3508 | | |
3509 | | /// The instance import name map to use. |
3510 | | /// |
3511 | | /// This is used to rename instance imports in the final component. |
3512 | | /// |
3513 | | /// For example, if there is an instance import `foo:bar/baz` and it is |
3514 | | /// desired that the import actually be named `a:b/c@x.y.z`, then |
3515 | | /// `foo:bar/baz` can be mapped to `a:b/c@x.y.z`. |
3516 | | /// |
3517 | | /// Note: the replacement names are not validated during encoding unless |
3518 | | /// the `validate` option is set to true. |
3519 | 0 | pub fn import_name_map(&mut self, map: HashMap<String, String>) -> &mut Self { |
3520 | 0 | self.import_name_map = map; |
3521 | 0 | self |
3522 | 0 | } |
3523 | | |
3524 | | /// Encode the component and return the bytes. |
3525 | 3.79k | pub fn encode(&mut self) -> Result<Vec<u8>> { |
3526 | 3.79k | self.encode_with_fixups(None) |
3527 | 3.79k | } |
3528 | | |
3529 | 3.79k | pub(crate) fn encode_with_fixups( |
3530 | 3.79k | &mut self, |
3531 | 3.79k | fixup_hook: Option<&mut dyn FnMut(&mut fixup::FixupModule) -> Result<()>>, |
3532 | 3.79k | ) -> Result<Vec<u8>> { |
3533 | 3.79k | if self.module.is_empty() { |
3534 | 0 | bail!("a module is required when encoding a component"); |
3535 | 3.79k | } |
3536 | | |
3537 | 3.79k | if self.merge_imports_based_on_semver.unwrap_or(true) { |
3538 | 3.79k | self.metadata |
3539 | 3.79k | .resolve |
3540 | 3.79k | .merge_world_imports_based_on_semver(self.metadata.world)?; |
3541 | 0 | } |
3542 | | |
3543 | 3.79k | if self.emit_canonical_names { |
3544 | 0 | check_duplicate_canonical_names(&self.metadata.resolve, self.metadata.world)?; |
3545 | 3.79k | } |
3546 | | |
3547 | 3.79k | self.finalize_resolve_with_nominal_ids(); |
3548 | | |
3549 | 3.79k | let world = ComponentWorld::new(self).context("failed to decode world from module")?; |
3550 | 3.79k | let mut state = EncodingState { |
3551 | 3.79k | component: ComponentBuilder::default(), |
3552 | 3.79k | module_index: None, |
3553 | 3.79k | instance_index: None, |
3554 | 3.79k | memory_index: None, |
3555 | 3.79k | shim_instance_index: None, |
3556 | 3.79k | adapter_modules: IndexMap::new(), |
3557 | 3.79k | adapter_instances: IndexMap::new(), |
3558 | 3.79k | type_encoding_maps: Default::default(), |
3559 | 3.79k | instances: Default::default(), |
3560 | 3.79k | imported_funcs: Default::default(), |
3561 | 3.79k | aliased_core_items: Default::default(), |
3562 | 3.79k | info: &world, |
3563 | 3.79k | export_task_initialization_wrappers: HashMap::new(), |
3564 | 3.79k | }; |
3565 | 3.79k | state.encode_imports(&self.import_name_map)?; |
3566 | 3.79k | state.encode_core_modules(); |
3567 | 3.79k | state.encode_core_instantiation(fixup_hook)?; |
3568 | 3.79k | state.encode_exports(CustomModule::Main)?; |
3569 | 3.79k | for name in self.adapters.keys() { |
3570 | 0 | state.encode_exports(CustomModule::Adapter(name))?; |
3571 | | } |
3572 | 3.79k | state.component.append_names(); |
3573 | 3.79k | state |
3574 | 3.79k | .component |
3575 | 3.79k | .raw_custom_section(&crate::base_producers().raw_custom_section()); |
3576 | 3.79k | let bytes = state.component.finish(); |
3577 | | |
3578 | 3.79k | if self.validate { |
3579 | 0 | Validator::new_with_features(WasmFeatures::all()) |
3580 | 0 | .validate_all(&bytes) |
3581 | 0 | .context("failed to validate component output")?; |
3582 | 3.79k | } |
3583 | | |
3584 | 3.79k | Ok(bytes) |
3585 | 3.79k | } |
3586 | | |
3587 | | /// Call the `generate_nominal_type_ids` method on the `Resolve` that we're |
3588 | | /// using, adjusting any preexisting keys/pointers as necessary. |
3589 | | /// |
3590 | | /// This is the final step after merging all known `Resolve`s together |
3591 | | /// before a component is actually created. By creating a unique |
3592 | | /// `InterfaceId` for all interfaces it makes the generation process easier |
3593 | | /// since there's no need to fret about whether an `InterfaceId` is an |
3594 | | /// import or an export for example. |
3595 | 3.79k | fn finalize_resolve_with_nominal_ids(&mut self) { |
3596 | | // Before calling `generate_nominal_type_ids` we need to handle the fact |
3597 | | // that the exports of the world are going to be rewritten. The only |
3598 | | // pointers we have into those are the exports of the main module and |
3599 | | // adapters. To handle this, before we generate nominal ids, indices of |
3600 | | // exports are saved here on the stack to get restored later on. |
3601 | | // Effectively we're clearing out the exports and rebuilding them later. |
3602 | 3.79k | let world = &self.metadata.resolve.worlds[self.metadata.world]; |
3603 | 3.79k | let main_module_exports = self |
3604 | 3.79k | .main_module_exports |
3605 | 3.79k | .iter() |
3606 | 6.43k | .map(|i| world.exports.get_index_of(i).unwrap()) |
3607 | 3.79k | .collect::<Vec<_>>(); |
3608 | 3.79k | let adapter_exports = self |
3609 | 3.79k | .adapters |
3610 | 3.79k | .values() |
3611 | 3.79k | .map(|adapter| { |
3612 | 0 | adapter |
3613 | 0 | .required_exports |
3614 | 0 | .iter() |
3615 | 0 | .map(|i| world.exports.get_index_of(i).unwrap()) |
3616 | 0 | .collect::<Vec<_>>() |
3617 | 0 | }) |
3618 | 3.79k | .collect::<Vec<_>>(); |
3619 | | |
3620 | | // With everything saved this will modify `Resolve` to ensure there's a |
3621 | | // nominal identifier for all interfaces (e.g. not both simultaneously |
3622 | | // imported and exported). |
3623 | 3.79k | self.metadata |
3624 | 3.79k | .resolve |
3625 | 3.79k | .generate_nominal_type_ids(self.metadata.world); |
3626 | | |
3627 | | // Rebuild the sets of exports now that the world's exports have been |
3628 | | // clobbered. |
3629 | 3.79k | self.main_module_exports.clear(); |
3630 | 3.79k | let world = &self.metadata.resolve.worlds[self.metadata.world]; |
3631 | 6.43k | for index in main_module_exports { |
3632 | 6.43k | let (key, _) = world.exports.get_index(index).unwrap(); |
3633 | 6.43k | self.main_module_exports.insert(key.clone()); |
3634 | 6.43k | } |
3635 | 3.79k | for (exports, adapter) in adapter_exports.into_iter().zip(self.adapters.values_mut()) { |
3636 | 0 | adapter.required_exports.clear(); |
3637 | 0 | for index in exports { |
3638 | 0 | let (key, _) = world.exports.get_index(index).unwrap(); |
3639 | 0 | adapter.required_exports.insert(key.clone()); |
3640 | 0 | } |
3641 | | } |
3642 | 3.79k | } |
3643 | | } |
3644 | | |
3645 | | impl ComponentWorld<'_> { |
3646 | | /// Convenience function to lookup a module's import map. |
3647 | 7.58k | fn imports_for(&self, module: CustomModule) -> &ImportMap { |
3648 | 7.58k | match module { |
3649 | 7.58k | CustomModule::Main => &self.info.imports, |
3650 | 0 | CustomModule::Adapter(name) => &self.adapters[name].info.imports, |
3651 | | } |
3652 | 7.58k | } |
3653 | | |
3654 | | /// Convenience function to lookup a module's export map. |
3655 | 31.6k | fn exports_for(&self, module: CustomModule) -> &ExportMap { |
3656 | 31.6k | match module { |
3657 | 31.6k | CustomModule::Main => &self.info.exports, |
3658 | 0 | CustomModule::Adapter(name) => &self.adapters[name].info.exports, |
3659 | | } |
3660 | 31.6k | } |
3661 | | |
3662 | | /// Convenience function to lookup a module's metadata. |
3663 | 13.8k | fn module_metadata_for(&self, module: CustomModule) -> &ModuleMetadata { |
3664 | 13.8k | match module { |
3665 | 13.8k | CustomModule::Main => &self.encoder.metadata.metadata, |
3666 | 0 | CustomModule::Adapter(name) => &self.encoder.adapters[name].metadata, |
3667 | | } |
3668 | 13.8k | } |
3669 | | } |
3670 | | |
3671 | | /// Verifies that no two imports, and no two exports, of the world share the |
3672 | | /// same canonical name. |
3673 | 2.40k | pub(crate) fn check_duplicate_canonical_names(resolve: &Resolve, world: WorldId) -> Result<()> { |
3674 | 2.40k | let world = &resolve.worlds[world]; |
3675 | 2.40k | let mut names = HashMap::new(); |
3676 | 2.77k | for key in world.imports.keys() { |
3677 | 2.77k | let WorldKey::Interface(id) = key else { |
3678 | 2.32k | continue; |
3679 | | }; |
3680 | 451 | let canonical = resolve.canonicalized_id_of(*id).unwrap(); |
3681 | 451 | if let Some(prev) = names.insert(canonical.clone(), *id) { |
3682 | 0 | bail!( |
3683 | | "import `{}` and import `{}` both have the canonical name \ |
3684 | | `{canonical}`; semver-compatible imports must be merged \ |
3685 | | before emitting canonical names", |
3686 | 0 | resolve.id_of(prev).unwrap(), |
3687 | 0 | resolve.id_of(*id).unwrap(), |
3688 | | ); |
3689 | 451 | } |
3690 | | } |
3691 | 2.40k | names.clear(); |
3692 | 5.10k | for key in world.exports.keys() { |
3693 | 5.10k | let WorldKey::Interface(id) = key else { |
3694 | 5.04k | continue; |
3695 | | }; |
3696 | 56 | let canonical = resolve.canonicalized_id_of(*id).unwrap(); |
3697 | 56 | if let Some(prev) = names.insert(canonical.clone(), *id) { |
3698 | 0 | bail!( |
3699 | | "export `{}` and export `{}` both have the canonical name \ |
3700 | | `{canonical}`; semver-compatible versions of the same \ |
3701 | | interface cannot both be exported when emitting canonical \ |
3702 | | names", |
3703 | 0 | resolve.id_of(prev).unwrap(), |
3704 | 0 | resolve.id_of(*id).unwrap(), |
3705 | | ); |
3706 | 56 | } |
3707 | | } |
3708 | 2.40k | Ok(()) |
3709 | 2.40k | } |
3710 | | |
3711 | | #[cfg(all(test, feature = "dummy-module"))] |
3712 | | mod test { |
3713 | | use super::*; |
3714 | | use crate::{dummy_module, embed_component_metadata}; |
3715 | | use wit_parser::ManglingAndAbi; |
3716 | | |
3717 | | #[test] |
3718 | | fn it_renames_imports() { |
3719 | | let mut resolve = Resolve::new(); |
3720 | | let pkg = resolve |
3721 | | .push_str( |
3722 | | "test.wit", |
3723 | | r#" |
3724 | | package test:wit; |
3725 | | |
3726 | | interface i { |
3727 | | f: func(); |
3728 | | } |
3729 | | |
3730 | | world test { |
3731 | | import i; |
3732 | | import foo: interface { |
3733 | | f: func(); |
3734 | | } |
3735 | | } |
3736 | | "#, |
3737 | | ) |
3738 | | .unwrap(); |
3739 | | let world = resolve.select_world(&[pkg], None).unwrap(); |
3740 | | |
3741 | | let mut module = dummy_module(&resolve, world, ManglingAndAbi::Standard32); |
3742 | | |
3743 | | embed_component_metadata(&mut module, &resolve, world, StringEncoding::UTF8, true).unwrap(); |
3744 | | |
3745 | | let encoded = ComponentEncoder::default() |
3746 | | .import_name_map(HashMap::from([ |
3747 | | ("foo".to_string(), "foo:bar/foo@1.0.0".to_string()), |
3748 | | ("test:wit/i".to_string(), "foo:bar/i@1.2.3".to_string()), |
3749 | | ])) |
3750 | | .module(&module) |
3751 | | .unwrap() |
3752 | | .validate(true) |
3753 | | .encode() |
3754 | | .unwrap(); |
3755 | | |
3756 | | let wat = wasmprinter::print_bytes(encoded).unwrap(); |
3757 | | assert!(wat.contains("(import \"foo:bar/foo@1.0.0\"")); |
3758 | | assert!(wat.contains("(import \"foo:bar/i@1.2.3\"")); |
3759 | | assert!(!wat.contains("(import \"foo\"")); |
3760 | | assert!(!wat.contains("(import \"test:wit/i\"")); |
3761 | | } |
3762 | | } |