/src/wasm-tools/crates/wit-component/src/validation.rs
Line | Count | Source |
1 | | use crate::encoding::{Instance, Item, LibraryInfo, MainOrAdapter, ModuleImportMap}; |
2 | | use crate::{ComponentEncoder, StringEncoding}; |
3 | | use anyhow::{Context, Result, anyhow, bail}; |
4 | | use indexmap::{IndexMap, IndexSet, map::Entry}; |
5 | | use std::fmt; |
6 | | use std::hash::Hash; |
7 | | use std::mem; |
8 | | use wasm_encoder::ExportKind; |
9 | | use wasmparser::names::{ComponentName, ComponentNameKind}; |
10 | | use wasmparser::{ |
11 | | Encoding, ExternalKind, FuncType, MemoryType, Parser, Payload, TypeRef, ValType, ValidPayload, |
12 | | Validator, WasmFeatures, types::TypesRef, |
13 | | }; |
14 | | use wit_parser::{ |
15 | | Function, InterfaceId, PackageName, Resolve, Type, TypeDefKind, TypeId, World, WorldId, |
16 | | WorldItem, WorldKey, |
17 | | abi::{AbiVariant, WasmSignature, WasmType}, |
18 | | }; |
19 | | |
20 | 4.24k | fn wasm_sig_to_func_type(signature: WasmSignature) -> FuncType { |
21 | 8.73k | fn from_wasm_type(ty: &WasmType) -> ValType { |
22 | 8.73k | match ty { |
23 | 7.22k | WasmType::I32 => ValType::I32, |
24 | 104 | WasmType::I64 => ValType::I64, |
25 | 265 | WasmType::F32 => ValType::F32, |
26 | 183 | WasmType::F64 => ValType::F64, |
27 | 790 | WasmType::Pointer => ValType::I32, |
28 | 3 | WasmType::PointerOrI64 => ValType::I64, |
29 | 165 | WasmType::Length => ValType::I32, |
30 | | } |
31 | 8.73k | } |
32 | | |
33 | 4.24k | FuncType::new( |
34 | 4.24k | signature.params.iter().map(from_wasm_type), |
35 | 4.24k | signature.results.iter().map(from_wasm_type), |
36 | | ) |
37 | 4.24k | } |
38 | | |
39 | | /// Metadata about a validated module and what was found internally. |
40 | | /// |
41 | | /// This structure houses information about `imports` and `exports` to the |
42 | | /// module. Each of these specialized types contains "connection" information |
43 | | /// between a module's imports/exports and the WIT or component-level constructs |
44 | | /// they correspond to. |
45 | | |
46 | | #[derive(Default)] |
47 | | pub struct ValidatedModule { |
48 | | /// Information about a module's imports. |
49 | | pub imports: ImportMap, |
50 | | |
51 | | /// Information about a module's exports. |
52 | | pub exports: ExportMap, |
53 | | } |
54 | | |
55 | | impl ValidatedModule { |
56 | 593 | fn new( |
57 | 593 | encoder: &ComponentEncoder, |
58 | 593 | bytes: &[u8], |
59 | 593 | exports: &IndexSet<WorldKey>, |
60 | 593 | import_map: Option<&ModuleImportMap>, |
61 | 593 | info: Option<&LibraryInfo>, |
62 | 593 | ) -> Result<ValidatedModule> { |
63 | 593 | let mut validator = Validator::new_with_features(WasmFeatures::all()); |
64 | 593 | let mut ret = ValidatedModule::default(); |
65 | | |
66 | 10.5k | for payload in Parser::new(0).parse_all(bytes) { |
67 | 10.5k | let payload = payload?; |
68 | 10.5k | if let ValidPayload::End(_) = validator.payload(&payload)? { |
69 | 593 | break; |
70 | 9.95k | } |
71 | | |
72 | 9.95k | let types = validator.types(0).unwrap(); |
73 | | |
74 | 593 | match payload { |
75 | 593 | Payload::Version { encoding, .. } if encoding != Encoding::Module => { |
76 | 0 | bail!("data is not a WebAssembly module"); |
77 | | } |
78 | 304 | Payload::ImportSection(s) => { |
79 | 4.80k | for import in s.into_imports() { |
80 | 4.80k | let import = import?; |
81 | 4.80k | ret.imports.add(import, encoder, import_map, info, types)?; |
82 | | } |
83 | | } |
84 | 593 | Payload::ExportSection(s) => { |
85 | 6.08k | for export in s { |
86 | 6.08k | let export = export?; |
87 | 6.08k | ret.exports.add(export, encoder, &exports, types)?; |
88 | | } |
89 | | } |
90 | 9.05k | _ => continue, |
91 | | } |
92 | | } |
93 | | |
94 | 593 | ret.exports.validate(encoder, exports)?; |
95 | | |
96 | 593 | Ok(ret) |
97 | 593 | } |
98 | | } |
99 | | |
100 | | /// Metadata information about a module's imports. |
101 | | /// |
102 | | /// This structure maintains the connection between component model "things" and |
103 | | /// core wasm "things" by ensuring that all imports to the core wasm module are |
104 | | /// classified by the `Import` enumeration. |
105 | | #[derive(Default)] |
106 | | pub struct ImportMap { |
107 | | /// The first level of the map here is the module namespace of the import |
108 | | /// and the second level of the map is the field namespace. The item is then |
109 | | /// how the import is satisfied. |
110 | | names: IndexMap<String, ImportInstance>, |
111 | | |
112 | | /// Cache for the last-inserted `MainModuleMemory` into `names` |
113 | | imported_memory: Option<MemoryType>, |
114 | | } |
115 | | |
116 | | pub enum ImportInstance { |
117 | | /// This import is satisfied by an entire instance of another |
118 | | /// adapter/module. |
119 | | Whole(MainOrAdapter), |
120 | | |
121 | | /// This import is satisfied by filling out each name possibly differently. |
122 | | Names(IndexMap<String, Import>), |
123 | | } |
124 | | |
125 | | /// Represents metadata about a `stream<T>` or `future<T>` type for a specific |
126 | | /// payload type `T`. |
127 | | /// |
128 | | /// Currently, the name mangling scheme we use to represent `stream` and |
129 | | /// `future` intrinsics as core module function imports refers to a specific |
130 | | /// `stream` or `future` type by naming an imported or exported component |
131 | | /// function which has that type as a parameter or return type (where the |
132 | | /// specific type is referred to using an ordinal numbering scheme). Not only |
133 | | /// does this approach unambiguously indicate the type of interest, but it |
134 | | /// allows us to reuse the `realloc`, string encoding, memory, etc. used by that |
135 | | /// function when emitting intrinsic declarations. |
136 | | /// |
137 | | /// TODO: Rather than reusing the same canon opts as the function in which the |
138 | | /// type appears, consider encoding them in the name mangling stream on an |
139 | | /// individual basis, similar to how we encode `error-context.*` built-in |
140 | | /// imports. |
141 | | #[derive(Debug, Eq, PartialEq, Clone, Hash)] |
142 | | pub struct PayloadInfo { |
143 | | /// The original, mangled import name used to import this built-in |
144 | | /// (currently used only for hashing and debugging). |
145 | | pub name: String, |
146 | | /// The resolved type id for the `stream` or `future` type of interest. |
147 | | /// |
148 | | /// If `Unit{Future,Stream}` this means that it's a "unit" payload or has no associated |
149 | | /// type being sent. |
150 | | pub ty: PayloadType, |
151 | | /// The world key representing the import or export context of `function`. |
152 | | pub key: WorldKey, |
153 | | /// The interface that `function` was imported from or exported in, if any. |
154 | | pub interface: Option<InterfaceId>, |
155 | | /// Whether `function` is being imported or exported. |
156 | | /// |
157 | | /// This may affect how we emit the declaration of the built-in, e.g. if the |
158 | | /// payload type is an exported resource. |
159 | | pub imported: bool, |
160 | | } |
161 | | |
162 | | /// The type of future/stream referenced by a `PayloadInfo` |
163 | | #[derive(Debug, Eq, PartialEq, Clone, Hash)] |
164 | | pub enum PayloadType { |
165 | | /// This is a future or stream located in a `Resolve` where `id` points to |
166 | | /// either of `TypeDefKind::{Future, Stream}`. |
167 | | Type { |
168 | | id: TypeId, |
169 | | /// The component-level function import or export where the type |
170 | | /// appeared as a parameter or result type. |
171 | | function: String, |
172 | | }, |
173 | | /// This is a `future` (no type) |
174 | | UnitFuture, |
175 | | /// This is a `stream` (no type) |
176 | | UnitStream, |
177 | | } |
178 | | |
179 | | impl PayloadInfo { |
180 | | /// Returns the payload type that this future/stream type is using. |
181 | 252 | pub fn payload(&self, resolve: &Resolve) -> Option<Type> { |
182 | 252 | let id = match self.ty { |
183 | 252 | PayloadType::Type { id, .. } => id, |
184 | 0 | PayloadType::UnitFuture | PayloadType::UnitStream => return None, |
185 | | }; |
186 | 252 | match resolve.types[id].kind { |
187 | 252 | TypeDefKind::Future(payload) | TypeDefKind::Stream(payload) => payload, |
188 | 0 | _ => unreachable!(), |
189 | | } |
190 | 252 | } |
191 | | } |
192 | | |
193 | | /// The different kinds of items that a module or an adapter can import. |
194 | | /// |
195 | | /// This is intended to be an exhaustive definition of what can be imported into |
196 | | /// core modules within a component that wit-component supports. This doesn't |
197 | | /// get down to the level of storing any idx numbers; at its most specific, it |
198 | | /// gives a name. |
199 | | #[derive(Debug, Clone)] |
200 | | pub enum Import { |
201 | | /// A top-level world function, with the name provided here, is imported |
202 | | /// into the module. |
203 | | WorldFunc(WorldKey, String, AbiVariant), |
204 | | |
205 | | /// An interface's function is imported into the module. |
206 | | /// |
207 | | /// The `WorldKey` here is the name of the interface in the world in |
208 | | /// question. The `InterfaceId` is the interface that was imported from and |
209 | | /// `String` is the WIT name of the function. |
210 | | InterfaceFunc(WorldKey, InterfaceId, String, AbiVariant), |
211 | | |
212 | | /// An imported resource's destructor is imported. |
213 | | /// |
214 | | /// The key provided indicates whether it's for the top-level types of the |
215 | | /// world (`None`) or an interface (`Some` with the name of the interface). |
216 | | /// The `TypeId` is what resource is being dropped. |
217 | | ImportedResourceDrop(WorldKey, Option<InterfaceId>, TypeId), |
218 | | |
219 | | /// A `canon resource.drop` intrinsic for an exported item is being |
220 | | /// imported. |
221 | | /// |
222 | | /// This lists the key of the interface that's exporting the resource plus |
223 | | /// the id within that interface. |
224 | | ExportedResourceDrop(WorldKey, TypeId), |
225 | | |
226 | | /// A `canon resource.new` intrinsic for an exported item is being |
227 | | /// imported. |
228 | | /// |
229 | | /// This lists the key of the interface that's exporting the resource plus |
230 | | /// the id within that interface. |
231 | | ExportedResourceNew(WorldKey, TypeId), |
232 | | |
233 | | /// A `canon resource.rep` intrinsic for an exported item is being |
234 | | /// imported. |
235 | | /// |
236 | | /// This lists the key of the interface that's exporting the resource plus |
237 | | /// the id within that interface. |
238 | | ExportedResourceRep(WorldKey, TypeId), |
239 | | |
240 | | /// An export of an adapter is being imported with the specified type. |
241 | | /// |
242 | | /// This is used for when the main module imports an adapter function. The |
243 | | /// adapter name and function name match the module's own import, and the |
244 | | /// type must match that listed here. |
245 | | AdapterExport { |
246 | | adapter: String, |
247 | | func: String, |
248 | | ty: FuncType, |
249 | | }, |
250 | | |
251 | | /// An adapter is importing the memory of the main module. |
252 | | /// |
253 | | /// (should be combined with `MainModuleExport` below one day) |
254 | | MainModuleMemory(MemoryType), |
255 | | |
256 | | /// An adapter is importing an arbitrary item from the main module. |
257 | | MainModuleExport { name: String, kind: ExportKind }, |
258 | | |
259 | | /// An arbitrary item from either the main module or an adapter is being |
260 | | /// imported. |
261 | | /// |
262 | | /// (should probably subsume `MainModule*` and maybe `AdapterExport` above |
263 | | /// one day. |
264 | | Item(Item), |
265 | | |
266 | | /// A `canon task.return` intrinsic for an exported function. |
267 | | /// |
268 | | /// This allows an exported function to return a value and then continue |
269 | | /// running. |
270 | | /// |
271 | | /// As of this writing, only async-lifted exports use `task.return`, but the |
272 | | /// plan is to also support it for sync-lifted exports in the future as |
273 | | /// well. |
274 | | ExportedTaskReturn(WorldKey, Option<InterfaceId>, Function), |
275 | | |
276 | | /// A `canon task.cancel` intrinsic for an exported function. |
277 | | /// |
278 | | /// This allows an exported function to acknowledge a `CANCELLED` event. |
279 | | ExportedTaskCancel, |
280 | | |
281 | | /// The `context.get` intrinsic for the nth slot of storage. |
282 | | ContextGet { |
283 | | /// The type of the slot (`i32` or `i64`). |
284 | | ty: ValType, |
285 | | /// The index of the storage slot. |
286 | | slot: u32, |
287 | | }, |
288 | | /// The `context.set` intrinsic for the nth slot of storage. |
289 | | ContextSet { |
290 | | /// The type of the slot (`i32` or `i64`). |
291 | | ty: ValType, |
292 | | /// The index of the storage slot. |
293 | | slot: u32, |
294 | | }, |
295 | | |
296 | | /// The `__wasm_get_tls_base` function that LLVM emits to read the base |
297 | | /// pointer of this module's thread-local storage. |
298 | | /// |
299 | | /// Unlike [`Import::ContextGet`] this is not tied to a particular storage |
300 | | /// mechanism: how it's satisfied depends on whether the program uses |
301 | | /// cooperative threading. See |
302 | | /// `EncodingState::materialize_tls_base_import` for the details. |
303 | | TlsBaseGet { |
304 | | /// The type of the base pointer (`i32` or `i64`). |
305 | | ty: ValType, |
306 | | }, |
307 | | |
308 | | /// The `__wasm_set_tls_base` counterpart to [`Import::TlsBaseGet`]. |
309 | | TlsBaseSet { |
310 | | /// The type of the base pointer (`i32` or `i64`). |
311 | | ty: ValType, |
312 | | }, |
313 | | |
314 | | /// A `canon backpressure.inc` intrinsic. |
315 | | BackpressureInc, |
316 | | |
317 | | /// A `canon backpressure.dec` intrinsic. |
318 | | BackpressureDec, |
319 | | |
320 | | /// A `waitable-set.new` intrinsic. |
321 | | WaitableSetNew, |
322 | | |
323 | | /// A `canon waitable-set.wait` intrinsic. |
324 | | /// |
325 | | /// This allows the guest to wait for any pending calls to async-lowered |
326 | | /// imports and/or `stream` and `future` operations to complete without |
327 | | /// unwinding the current Wasm stack. |
328 | | WaitableSetWait { cancellable: bool }, |
329 | | |
330 | | /// A `canon waitable.poll` intrinsic. |
331 | | /// |
332 | | /// This allows the guest to check whether any pending calls to |
333 | | /// async-lowered imports and/or `stream` and `future` operations have |
334 | | /// completed without unwinding the current Wasm stack and without blocking. |
335 | | WaitableSetPoll { cancellable: bool }, |
336 | | |
337 | | /// A `waitable-set.drop` intrinsic. |
338 | | WaitableSetDrop, |
339 | | |
340 | | /// A `waitable.join` intrinsic. |
341 | | WaitableJoin, |
342 | | |
343 | | /// A `canon subtask.drop` intrinsic. |
344 | | /// |
345 | | /// This allows the guest to release its handle to a completed subtask. |
346 | | SubtaskDrop, |
347 | | |
348 | | /// A `canon subtask.cancel` intrinsic. |
349 | | /// |
350 | | /// This allows the guest to cancel an in-progress subtask. |
351 | | SubtaskCancel { async_: bool }, |
352 | | |
353 | | /// A `canon stream.new` intrinsic. |
354 | | /// |
355 | | /// This allows the guest to create a new `stream` of the specified type. |
356 | | StreamNew(PayloadInfo), |
357 | | |
358 | | /// A `canon stream.read` intrinsic. |
359 | | /// |
360 | | /// This allows the guest to read the next values (if any) from the specified |
361 | | /// stream. |
362 | | StreamRead { async_: bool, info: PayloadInfo }, |
363 | | |
364 | | /// A `canon stream.write` intrinsic. |
365 | | /// |
366 | | /// This allows the guest to write one or more values to the specified |
367 | | /// stream. |
368 | | StreamWrite { async_: bool, info: PayloadInfo }, |
369 | | |
370 | | /// A `canon stream.cancel-read` intrinsic. |
371 | | /// |
372 | | /// This allows the guest to cancel a pending read it initiated earlier (but |
373 | | /// which may have already partially or entirely completed). |
374 | | StreamCancelRead { info: PayloadInfo, async_: bool }, |
375 | | |
376 | | /// A `canon stream.cancel-write` intrinsic. |
377 | | /// |
378 | | /// This allows the guest to cancel a pending write it initiated earlier |
379 | | /// (but which may have already partially or entirely completed). |
380 | | StreamCancelWrite { info: PayloadInfo, async_: bool }, |
381 | | |
382 | | /// A `canon stream.drop-readable` intrinsic. |
383 | | /// |
384 | | /// This allows the guest to drop the readable end of a `stream`. |
385 | | StreamDropReadable(PayloadInfo), |
386 | | |
387 | | /// A `canon stream.drop-writable` intrinsic. |
388 | | /// |
389 | | /// This allows the guest to drop the writable end of a `stream`. |
390 | | StreamDropWritable(PayloadInfo), |
391 | | |
392 | | /// A `canon future.new` intrinsic. |
393 | | /// |
394 | | /// This allows the guest to create a new `future` of the specified type. |
395 | | FutureNew(PayloadInfo), |
396 | | |
397 | | /// A `canon future.read` intrinsic. |
398 | | /// |
399 | | /// This allows the guest to read the value (if any) from the specified |
400 | | /// future. |
401 | | FutureRead { async_: bool, info: PayloadInfo }, |
402 | | |
403 | | /// A `canon future.write` intrinsic. |
404 | | /// |
405 | | /// This allows the guest to write a value to the specified future. |
406 | | FutureWrite { async_: bool, info: PayloadInfo }, |
407 | | |
408 | | /// A `canon future.cancel-read` intrinsic. |
409 | | /// |
410 | | /// This allows the guest to cancel a pending read it initiated earlier (but |
411 | | /// which may have already completed). |
412 | | FutureCancelRead { info: PayloadInfo, async_: bool }, |
413 | | |
414 | | /// A `canon future.cancel-write` intrinsic. |
415 | | /// |
416 | | /// This allows the guest to cancel a pending write it initiated earlier |
417 | | /// (but which may have already completed). |
418 | | FutureCancelWrite { info: PayloadInfo, async_: bool }, |
419 | | |
420 | | /// A `canon future.drop-readable` intrinsic. |
421 | | /// |
422 | | /// This allows the guest to drop the readable end of a `future`. |
423 | | FutureDropReadable(PayloadInfo), |
424 | | |
425 | | /// A `canon future.drop-writable` intrinsic. |
426 | | /// |
427 | | /// This allows the guest to drop the writable end of a `future`. |
428 | | FutureDropWritable(PayloadInfo), |
429 | | |
430 | | /// A `canon error-context.new` intrinsic. |
431 | | /// |
432 | | /// This allows the guest to create a new `error-context` instance with a |
433 | | /// specified debug message. |
434 | | ErrorContextNew { encoding: StringEncoding }, |
435 | | |
436 | | /// A `canon error-context.debug-message` intrinsic. |
437 | | /// |
438 | | /// This allows the guest to retrieve the debug message from a |
439 | | /// `error-context` instance. Note that the content of this message might |
440 | | /// not be identical to what was passed in to `error-context.new`. |
441 | | ErrorContextDebugMessage { encoding: StringEncoding }, |
442 | | |
443 | | /// A `canon error-context.drop` intrinsic. |
444 | | /// |
445 | | /// This allows the guest to release its handle to the specified |
446 | | /// `error-context` instance. |
447 | | ErrorContextDrop, |
448 | | |
449 | | /// A `canon thread.index` intrinsic. |
450 | | /// |
451 | | /// This allows the guest to get the index of the current thread. |
452 | | ThreadIndex, |
453 | | |
454 | | /// A `canon thread.new-indirect` intrinsic. |
455 | | /// |
456 | | /// This allows the guest to create a new thread running a specified function. |
457 | | ThreadNewIndirect, |
458 | | |
459 | | /// A `canon thread.resume-later` intrinsic. |
460 | | ThreadResumeLater, |
461 | | |
462 | | /// A `canon thread.suspend` intrinsic. |
463 | | ThreadSuspend { cancellable: bool }, |
464 | | |
465 | | /// A `canon thread.yield` intrinsic. |
466 | | ThreadYield { cancellable: bool }, |
467 | | |
468 | | /// A `canon thread.suspend-then-resume` intrinsic. |
469 | | ThreadSuspendThenResume { cancellable: bool }, |
470 | | |
471 | | /// A `canon thread.yield-then-resume` intrinsic. |
472 | | ThreadYieldThenResume { cancellable: bool }, |
473 | | |
474 | | /// A `canon thread.suspend-then-promote` intrinsic. |
475 | | ThreadSuspendThenPromote { cancellable: bool }, |
476 | | |
477 | | /// A `canon thread.yield-then-promote` intrinsic. |
478 | | ThreadYieldThenPromote { cancellable: bool }, |
479 | | } |
480 | | |
481 | | impl ImportMap { |
482 | | /// Returns the list of items that the adapter named `name` must export. |
483 | 0 | pub fn required_from_adapter(&self, name: &str) -> IndexMap<String, FuncType> { |
484 | 0 | let names = match self.names.get(name) { |
485 | 0 | Some(ImportInstance::Names(names)) => names, |
486 | 0 | _ => return IndexMap::new(), |
487 | | }; |
488 | 0 | names |
489 | 0 | .iter() |
490 | 0 | .map(|(_, import)| match import { |
491 | 0 | Import::AdapterExport { ty, func, adapter } => { |
492 | 0 | assert_eq!(adapter, name); |
493 | 0 | (func.clone(), ty.clone()) |
494 | | } |
495 | 0 | _ => unreachable!(), |
496 | 0 | }) |
497 | 0 | .collect() |
498 | 0 | } |
499 | | |
500 | | /// Returns an iterator over all individual imports registered in this map. |
501 | | /// |
502 | | /// Note that this doesn't iterate over the "whole instance" imports. |
503 | 2.96k | pub fn imports(&self) -> impl Iterator<Item = (&str, &str, &Import)> + '_ { |
504 | 2.96k | self.names |
505 | 2.96k | .iter() |
506 | 3.49k | .filter_map(|(module, m)| match m { |
507 | 3.49k | ImportInstance::Names(names) => Some((module, names)), |
508 | 0 | ImportInstance::Whole(_) => None, |
509 | 3.49k | }) |
510 | 3.49k | .flat_map(|(module, m)| { |
511 | 3.49k | m.iter() |
512 | 24.0k | .map(move |(field, import)| (module.as_str(), field.as_str(), import)) |
513 | 3.49k | }) |
514 | 2.96k | } |
515 | | |
516 | | /// Returns the map for how all imports must be satisfied. |
517 | 593 | pub fn modules(&self) -> &IndexMap<String, ImportInstance> { |
518 | 593 | &self.names |
519 | 593 | } |
520 | | |
521 | | /// Returns the type of the `env::memory` import of this module, if present. |
522 | 1.81k | pub fn imported_memory(&self) -> Option<MemoryType> { |
523 | 1.81k | self.imported_memory |
524 | 1.81k | } |
525 | | |
526 | | /// Classify an import and call `insert_import()` on it. Used during |
527 | | /// validation to build up this `ImportMap`. |
528 | 4.80k | fn add( |
529 | 4.80k | &mut self, |
530 | 4.80k | import: wasmparser::Import<'_>, |
531 | 4.80k | encoder: &ComponentEncoder, |
532 | 4.80k | import_map: Option<&ModuleImportMap>, |
533 | 4.80k | library_info: Option<&LibraryInfo>, |
534 | 4.80k | types: TypesRef<'_>, |
535 | 4.80k | ) -> Result<()> { |
536 | 4.80k | if self.classify_import_with_library(import, library_info)? { |
537 | 0 | return Ok(()); |
538 | 4.80k | } |
539 | 4.80k | let mut import_to_classify = import; |
540 | 4.80k | if let Some(map) = import_map { |
541 | 885 | if let Some(original_name) = map.original_name(&import) { |
542 | 798 | import_to_classify.name = original_name; |
543 | 798 | } |
544 | 3.91k | } |
545 | 4.80k | let item = self |
546 | 4.80k | .classify(import_to_classify, encoder, types) |
547 | 4.80k | .with_context(|| { |
548 | 0 | format!( |
549 | | "failed to resolve import `{}::{}`", |
550 | | import.module, import.name, |
551 | | ) |
552 | 0 | })?; |
553 | 4.80k | self.insert_import(import, item) |
554 | 4.80k | } |
555 | | |
556 | | /// Determines what kind of thing is being imported: maps it from the |
557 | | /// module/name/type triple in the raw wasm module to an enum. |
558 | | /// |
559 | | /// Handles a few special cases, then delegates to |
560 | | /// `classify_component_model_import()`. |
561 | 4.80k | fn classify( |
562 | 4.80k | &self, |
563 | 4.80k | import: wasmparser::Import<'_>, |
564 | 4.80k | encoder: &ComponentEncoder, |
565 | 4.80k | types: TypesRef<'_>, |
566 | 4.80k | ) -> Result<Import> { |
567 | | // Special-case the main module's memory imported into adapters which |
568 | | // currently with `wasm-ld` is not easily configurable. |
569 | 4.80k | if import.module == "env" && import.name == "memory" { |
570 | 0 | if let TypeRef::Memory(ty) = import.ty { |
571 | 0 | return Ok(Import::MainModuleMemory(ty)); |
572 | 0 | } |
573 | 4.80k | } |
574 | | |
575 | | // Special-case imports from the main module into adapters. |
576 | 4.80k | if import.module == "__main_module__" { |
577 | | return Ok(Import::MainModuleExport { |
578 | 0 | name: import.name.to_string(), |
579 | 0 | kind: match import.ty { |
580 | 0 | TypeRef::Func(_) => ExportKind::Func, |
581 | 0 | TypeRef::Table(_) => ExportKind::Table, |
582 | 0 | TypeRef::Memory(_) => ExportKind::Memory, |
583 | 0 | TypeRef::Global(_) => ExportKind::Global, |
584 | 0 | TypeRef::Tag(_) => ExportKind::Tag, |
585 | 0 | TypeRef::FuncExact(_) => bail!("Unexpected func_exact export"), |
586 | | }, |
587 | | }); |
588 | 4.80k | } |
589 | | |
590 | 4.80k | let ty_index = match import.ty { |
591 | 4.80k | TypeRef::Func(ty) => ty, |
592 | 0 | _ => bail!("module is only allowed to import functions"), |
593 | | }; |
594 | 4.80k | let ty = types[types.core_type_at_in_module(ty_index)].unwrap_func(); |
595 | | |
596 | | // Handle main module imports that match known adapters and set it up as |
597 | | // an import of an adapter export. |
598 | 4.80k | if encoder.adapters.contains_key(import.module) { |
599 | 0 | return Ok(Import::AdapterExport { |
600 | 0 | adapter: import.module.to_string(), |
601 | 0 | func: import.name.to_string(), |
602 | 0 | ty: ty.clone(), |
603 | 0 | }); |
604 | 4.80k | } |
605 | | |
606 | 4.80k | let (module, names) = match import.module.strip_prefix("cm32p2") { |
607 | 53 | Some(suffix) => (suffix, STANDARD), |
608 | 0 | None if encoder.reject_legacy_names => (import.module, STANDARD), |
609 | 4.75k | None => (import.module, LEGACY), |
610 | | }; |
611 | 4.80k | self.classify_component_model_import(module, import.name, encoder, ty, names) |
612 | 4.80k | } |
613 | | |
614 | | /// Attempts to classify the import `{module}::{name}` with the rules |
615 | | /// specified in WebAssembly/component-model#378 |
616 | 4.80k | fn classify_component_model_import( |
617 | 4.80k | &self, |
618 | 4.80k | module: &str, |
619 | 4.80k | name: &str, |
620 | 4.80k | encoder: &ComponentEncoder, |
621 | 4.80k | ty: &FuncType, |
622 | 4.80k | names: &dyn NameMangling, |
623 | 4.80k | ) -> Result<Import> { |
624 | 4.80k | let resolve = &encoder.metadata.resolve; |
625 | 4.80k | let world_id = encoder.metadata.world; |
626 | 4.80k | let world = &resolve.worlds[world_id]; |
627 | | |
628 | 4.80k | if module == names.import_root() { |
629 | 2.20k | if names.error_context_drop(name) { |
630 | 0 | let expected = FuncType::new([ValType::I32], []); |
631 | 0 | validate_func_sig(name, &expected, ty)?; |
632 | 0 | return Ok(Import::ErrorContextDrop); |
633 | 2.20k | } |
634 | | |
635 | 2.20k | if names.backpressure_inc(name) { |
636 | 166 | let expected = FuncType::new([], []); |
637 | 166 | validate_func_sig(name, &expected, ty)?; |
638 | 166 | return Ok(Import::BackpressureInc); |
639 | 2.04k | } |
640 | | |
641 | 2.04k | if names.backpressure_dec(name) { |
642 | 166 | let expected = FuncType::new([], []); |
643 | 166 | validate_func_sig(name, &expected, ty)?; |
644 | 166 | return Ok(Import::BackpressureDec); |
645 | 1.87k | } |
646 | | |
647 | 1.87k | if names.waitable_set_new(name) { |
648 | 166 | let expected = FuncType::new([], [ValType::I32]); |
649 | 166 | validate_func_sig(name, &expected, ty)?; |
650 | 166 | return Ok(Import::WaitableSetNew); |
651 | 1.71k | } |
652 | | |
653 | 1.71k | if let Some((info, result_ty)) = names.waitable_set_wait(name) { |
654 | 166 | let expected = FuncType::new([ValType::I32, result_ty], [ValType::I32]); |
655 | 166 | validate_func_sig(name, &expected, ty)?; |
656 | 166 | return Ok(Import::WaitableSetWait { |
657 | 166 | cancellable: info.cancellable, |
658 | 166 | }); |
659 | 1.54k | } |
660 | | |
661 | 1.54k | if let Some((info, result_ty)) = names.waitable_set_poll(name) { |
662 | 166 | let expected = FuncType::new([ValType::I32, result_ty], [ValType::I32]); |
663 | 166 | validate_func_sig(name, &expected, ty)?; |
664 | 166 | return Ok(Import::WaitableSetPoll { |
665 | 166 | cancellable: info.cancellable, |
666 | 166 | }); |
667 | 1.37k | } |
668 | | |
669 | 1.37k | if names.waitable_set_drop(name) { |
670 | 166 | let expected = FuncType::new([ValType::I32], []); |
671 | 166 | validate_func_sig(name, &expected, ty)?; |
672 | 166 | return Ok(Import::WaitableSetDrop); |
673 | 1.21k | } |
674 | | |
675 | 1.21k | if names.waitable_join(name) { |
676 | 166 | let expected = FuncType::new([ValType::I32; 2], []); |
677 | 166 | validate_func_sig(name, &expected, ty)?; |
678 | 166 | return Ok(Import::WaitableJoin); |
679 | 1.04k | } |
680 | | |
681 | 1.04k | if names.subtask_drop(name) { |
682 | 166 | let expected = FuncType::new([ValType::I32], []); |
683 | 166 | validate_func_sig(name, &expected, ty)?; |
684 | 166 | return Ok(Import::SubtaskDrop); |
685 | 880 | } |
686 | | |
687 | 880 | if let Some(info) = names.subtask_cancel(name) { |
688 | 166 | let expected = FuncType::new([ValType::I32], [ValType::I32]); |
689 | 166 | validate_func_sig(name, &expected, ty)?; |
690 | 166 | return Ok(Import::SubtaskCancel { |
691 | 166 | async_: info.async_lowered, |
692 | 166 | }); |
693 | 714 | } |
694 | | |
695 | 714 | if let Some(encoding) = names.error_context_new(name) { |
696 | 0 | let expected = FuncType::new([ValType::I32; 2], [ValType::I32]); |
697 | 0 | validate_func_sig(name, &expected, ty)?; |
698 | 0 | return Ok(Import::ErrorContextNew { encoding }); |
699 | 714 | } |
700 | | |
701 | 714 | if let Some(encoding) = names.error_context_debug_message(name) { |
702 | 0 | let expected = FuncType::new([ValType::I32; 2], []); |
703 | 0 | validate_func_sig(name, &expected, ty)?; |
704 | 0 | return Ok(Import::ErrorContextDebugMessage { encoding }); |
705 | 714 | } |
706 | | |
707 | 714 | if let Some((slot_ty, slot)) = names.context_get(name) { |
708 | 166 | let expected = FuncType::new([], [slot_ty]); |
709 | 166 | validate_func_sig(name, &expected, ty)?; |
710 | 166 | return Ok(Import::ContextGet { ty: slot_ty, slot }); |
711 | 548 | } |
712 | 548 | if let Some((slot_ty, slot)) = names.context_set(name) { |
713 | 166 | let expected = FuncType::new([slot_ty], []); |
714 | 166 | validate_func_sig(name, &expected, ty)?; |
715 | 166 | return Ok(Import::ContextSet { ty: slot_ty, slot }); |
716 | 382 | } |
717 | 382 | if names.thread_index(name) { |
718 | 0 | let expected = FuncType::new([], [ValType::I32]); |
719 | 0 | validate_func_sig(name, &expected, ty)?; |
720 | 0 | return Ok(Import::ThreadIndex); |
721 | 382 | } |
722 | 382 | if names.thread_new_indirect(name) { |
723 | 0 | let expected = FuncType::new([ValType::I32; 2], [ValType::I32]); |
724 | 0 | validate_func_sig(name, &expected, ty)?; |
725 | 0 | return Ok(Import::ThreadNewIndirect); |
726 | 382 | } |
727 | 382 | if names.thread_resume_later(name) { |
728 | 0 | let expected = FuncType::new([ValType::I32], []); |
729 | 0 | validate_func_sig(name, &expected, ty)?; |
730 | 0 | return Ok(Import::ThreadResumeLater); |
731 | 382 | } |
732 | 382 | if let Some(info) = names.thread_suspend(name) { |
733 | 0 | let expected = FuncType::new([], [ValType::I32]); |
734 | 0 | validate_func_sig(name, &expected, ty)?; |
735 | 0 | return Ok(Import::ThreadSuspend { |
736 | 0 | cancellable: info.cancellable, |
737 | 0 | }); |
738 | 382 | } |
739 | 382 | if let Some(info) = names.thread_yield(name) { |
740 | 166 | let expected = FuncType::new([], [ValType::I32]); |
741 | 166 | validate_func_sig(name, &expected, ty)?; |
742 | 166 | return Ok(Import::ThreadYield { |
743 | 166 | cancellable: info.cancellable, |
744 | 166 | }); |
745 | 216 | } |
746 | 216 | if let Some(info) = names.thread_suspend_then_resume(name) { |
747 | 0 | let expected = FuncType::new([ValType::I32], [ValType::I32]); |
748 | 0 | validate_func_sig(name, &expected, ty)?; |
749 | 0 | return Ok(Import::ThreadSuspendThenResume { |
750 | 0 | cancellable: info.cancellable, |
751 | 0 | }); |
752 | 216 | } |
753 | 216 | if let Some(info) = names.thread_yield_then_resume(name) { |
754 | 0 | let expected = FuncType::new([ValType::I32], [ValType::I32]); |
755 | 0 | validate_func_sig(name, &expected, ty)?; |
756 | 0 | return Ok(Import::ThreadYieldThenResume { |
757 | 0 | cancellable: info.cancellable, |
758 | 0 | }); |
759 | 216 | } |
760 | 216 | if let Some(info) = names.thread_suspend_then_promote(name) { |
761 | 0 | let expected = FuncType::new([ValType::I32], [ValType::I32]); |
762 | 0 | validate_func_sig(name, &expected, ty)?; |
763 | 0 | return Ok(Import::ThreadSuspendThenPromote { |
764 | 0 | cancellable: info.cancellable, |
765 | 0 | }); |
766 | 216 | } |
767 | 216 | if let Some(info) = names.thread_yield_then_promote(name) { |
768 | 0 | let expected = FuncType::new([ValType::I32], [ValType::I32]); |
769 | 0 | validate_func_sig(name, &expected, ty)?; |
770 | 0 | return Ok(Import::ThreadYieldThenPromote { |
771 | 0 | cancellable: info.cancellable, |
772 | 0 | }); |
773 | 216 | } |
774 | | |
775 | 216 | let (key_name, abi) = names.world_key_name_and_abi(name); |
776 | 216 | let key = WorldKey::Name(key_name.to_string()); |
777 | 216 | if let Some(WorldItem::Function(func)) = world.imports.get(&key) { |
778 | 174 | validate_func(resolve, ty, func, abi)?; |
779 | 174 | return Ok(Import::WorldFunc(key, func.name.clone(), abi)); |
780 | 42 | } |
781 | | |
782 | 42 | if let Some(import) = |
783 | 42 | self.maybe_classify_wit_intrinsic(name, None, encoder, ty, true, names)? |
784 | | { |
785 | 42 | return Ok(import); |
786 | 0 | } |
787 | | |
788 | 0 | match world.imports.get(&key) { |
789 | 0 | Some(_) => bail!("expected world top-level import `{name}` to be a function"), |
790 | 0 | None => bail!("no top-level imported function `{name}` specified"), |
791 | | } |
792 | 2.59k | } |
793 | | |
794 | 2.59k | if module == "env" { |
795 | 0 | if let Some(import) = names.env_import(name, ty) { |
796 | 0 | return Ok(import); |
797 | 0 | } |
798 | 2.59k | } |
799 | | |
800 | | // Check for `[export]$root::[task-return]foo` or similar |
801 | 187 | if matches!( |
802 | 2.59k | module.strip_prefix(names.import_exported_intrinsic_prefix()), |
803 | 2.02k | Some(module) if module == names.import_root() |
804 | | ) { |
805 | 187 | if let Some(import) = |
806 | 187 | self.maybe_classify_wit_intrinsic(name, None, encoder, ty, false, names)? |
807 | | { |
808 | 187 | return Ok(import); |
809 | 0 | } |
810 | 2.40k | } |
811 | | |
812 | 2.40k | let interface = match module.strip_prefix(names.import_non_root_prefix()) { |
813 | 2.40k | Some(name) => name, |
814 | 0 | None => bail!("unknown or invalid component model import syntax"), |
815 | | }; |
816 | | |
817 | 2.40k | if let Some(interface) = interface.strip_prefix(names.import_exported_intrinsic_prefix()) { |
818 | 1.83k | let (key, id) = names.module_to_interface(interface, resolve, &world.exports)?; |
819 | | |
820 | 1.83k | if let Some(import) = |
821 | 1.83k | self.maybe_classify_wit_intrinsic(name, Some((key, id)), encoder, ty, false, names)? |
822 | | { |
823 | 1.83k | return Ok(import); |
824 | 0 | } |
825 | 0 | bail!("unknown function `{name}`") |
826 | 571 | } |
827 | | |
828 | 571 | let (key, id) = names.module_to_interface(interface, resolve, &world.imports)?; |
829 | 571 | let interface = &resolve.interfaces[id]; |
830 | 571 | let (function_name, abi) = names.interface_function_name_and_abi(name); |
831 | 571 | if let Some(f) = interface.functions.get(function_name) { |
832 | 525 | validate_func(resolve, ty, f, abi).with_context(|| { |
833 | 0 | let name = resolve.name_world_key(&key); |
834 | 0 | format!("failed to validate import interface `{name}`") |
835 | 0 | })?; |
836 | 525 | return Ok(Import::InterfaceFunc(key, id, f.name.clone(), abi)); |
837 | 46 | } |
838 | | |
839 | 46 | if let Some(import) = |
840 | 46 | self.maybe_classify_wit_intrinsic(name, Some((key, id)), encoder, ty, true, names)? |
841 | | { |
842 | 46 | return Ok(import); |
843 | 0 | } |
844 | 0 | bail!( |
845 | | "import interface `{module}` is missing function \ |
846 | | `{name}` that is required by the module", |
847 | | ) |
848 | 4.80k | } |
849 | | |
850 | | /// Attempts to detect and classify `name` as a WIT intrinsic. |
851 | | /// |
852 | | /// This function is a bit of a sprawling sequence of matches used to |
853 | | /// detect whether `name` corresponds to a WIT intrinsic, so specifically |
854 | | /// not a WIT function itself. This is only used for functions imported |
855 | | /// into a module but the import could be for an imported item in a world |
856 | | /// or an exported item. |
857 | | /// |
858 | | /// ## Parameters |
859 | | /// |
860 | | /// * `name` - the core module name which is being pattern-matched. This |
861 | | /// should be the "field" of the import. This may include the "[async-lower]" |
862 | | /// or "[cancellable]" prefixes. |
863 | | /// * `key_and_id` - this is the inferred "container" for the function |
864 | | /// being described which is inferred from the module portion of the core |
865 | | /// wasm import field. This is `None` for root-level function/type |
866 | | /// imports, such as when referring to `import x: func();`. This is `Some` |
867 | | /// when an interface is used (either `import x: interface { .. }` or a |
868 | | /// standalone `interface`) where the world key is specified for the |
869 | | /// interface in addition to the interface that was identified. |
870 | | /// * `encoder` - this is the encoder state that contains |
871 | | /// `Resolve`/metadata information. |
872 | | /// * `ty` - the core wasm type of this import. |
873 | | /// * `import` - whether or not this core wasm import is operating on a WIT |
874 | | /// level import or export. An example of this being an export is when a |
875 | | /// core module imports a destructor for an exported resource. |
876 | | /// * `names` - the name mangling scheme that's configured to be used. |
877 | 2.11k | fn maybe_classify_wit_intrinsic( |
878 | 2.11k | &self, |
879 | 2.11k | name: &str, |
880 | 2.11k | key_and_id: Option<(WorldKey, InterfaceId)>, |
881 | 2.11k | encoder: &ComponentEncoder, |
882 | 2.11k | ty: &FuncType, |
883 | 2.11k | import: bool, |
884 | 2.11k | names: &dyn NameMangling, |
885 | 2.11k | ) -> Result<Option<Import>> { |
886 | 2.11k | let resolve = &encoder.metadata.resolve; |
887 | 2.11k | let world_id = encoder.metadata.world; |
888 | 2.11k | let world = &resolve.worlds[world_id]; |
889 | | |
890 | | // Separate out `Option<WorldKey>` and `Option<InterfaceId>`. If an |
891 | | // interface is NOT specified then the `WorldKey` which is attached to |
892 | | // imports is going to be calculated based on the name of the item |
893 | | // extracted, such as the resource or function referenced. |
894 | 2.11k | let (key, id) = match key_and_id { |
895 | 1.88k | Some((key, id)) => (Some(key), Some(id)), |
896 | 229 | None => (None, None), |
897 | | }; |
898 | | |
899 | | // Tests whether `name` is a resource within `id` (or `world_id`). |
900 | 2.11k | let resource_test = |name: &str| match id { |
901 | 361 | Some(id) => resource_test_for_interface(resolve, id)(name), |
902 | 28 | None => resource_test_for_world(resolve, world_id)(name), |
903 | 389 | }; |
904 | | |
905 | | // Test whether this is a `resource.drop` intrinsic. |
906 | 2.11k | if let Some(resource) = names.resource_drop_name(name) { |
907 | 179 | if let Some(resource_id) = resource_test(resource) { |
908 | 179 | let key = key.unwrap_or_else(|| WorldKey::Name(resource.to_string())); |
909 | 179 | let expected = FuncType::new([ValType::I32], []); |
910 | 179 | validate_func_sig(name, &expected, ty)?; |
911 | 179 | return Ok(Some(if import { |
912 | 74 | Import::ImportedResourceDrop(key, id, resource_id) |
913 | | } else { |
914 | 105 | Import::ExportedResourceDrop(key, resource_id) |
915 | | })); |
916 | 0 | } |
917 | 1.93k | } |
918 | | |
919 | | // There are some intrinsics which are only applicable to exported |
920 | | // functions/resources, so check those use cases here. |
921 | 1.93k | if !import { |
922 | 1.91k | if let Some(name) = names.resource_new_name(name) { |
923 | 105 | if let Some(id) = resource_test(name) { |
924 | 105 | let key = key.unwrap_or_else(|| WorldKey::Name(name.to_string())); |
925 | 105 | let expected = FuncType::new([ValType::I32], [ValType::I32]); |
926 | 105 | validate_func_sig(name, &expected, ty)?; |
927 | 105 | return Ok(Some(Import::ExportedResourceNew(key, id))); |
928 | 0 | } |
929 | 1.81k | } |
930 | 1.81k | if let Some(name) = names.resource_rep_name(name) { |
931 | 105 | if let Some(id) = resource_test(name) { |
932 | 105 | let key = key.unwrap_or_else(|| WorldKey::Name(name.to_string())); |
933 | 105 | let expected = FuncType::new([ValType::I32], [ValType::I32]); |
934 | 105 | validate_func_sig(name, &expected, ty)?; |
935 | 105 | return Ok(Some(Import::ExportedResourceRep(key, id))); |
936 | 0 | } |
937 | 1.70k | } |
938 | 1.70k | if let Some(name) = names.task_return_name(name) { |
939 | 675 | let func = get_function(resolve, world, name, id, import)?; |
940 | 675 | let key = key.unwrap_or_else(|| WorldKey::Name(name.to_string())); |
941 | | // TODO: should call `validate_func_sig` but would require |
942 | | // calculating the expected signature based of `func.result`. |
943 | 675 | return Ok(Some(Import::ExportedTaskReturn(key, id, func.clone()))); |
944 | 1.03k | } |
945 | 1.03k | if names.task_cancel(name) { |
946 | 166 | let expected = FuncType::new([], []); |
947 | 166 | validate_func_sig(name, &expected, ty)?; |
948 | 166 | return Ok(Some(Import::ExportedTaskCancel)); |
949 | 868 | } |
950 | 14 | } |
951 | | |
952 | 882 | let lookup_context = PayloadLookupContext { |
953 | 882 | resolve, |
954 | 882 | world, |
955 | 882 | key, |
956 | 882 | id, |
957 | 882 | import, |
958 | 882 | }; |
959 | | |
960 | | // Test for a number of async-related intrinsics. All intrinsics are |
961 | | // prefixed with `[...-N]` where `...` is the name of the intrinsic and |
962 | | // the `N` is the indexed future/stream that is being referred to. |
963 | 882 | let import = if let Some(info) = names.future_new(&lookup_context, name) { |
964 | 121 | validate_func_sig(name, &FuncType::new([], [ValType::I64]), ty)?; |
965 | 121 | Import::FutureNew(info) |
966 | 761 | } else if let Some(info) = names.future_write(&lookup_context, name) { |
967 | 121 | validate_func_sig(name, &FuncType::new([ValType::I32; 2], [ValType::I32]), ty)?; |
968 | 121 | Import::FutureWrite { |
969 | 121 | async_: info.async_lowered, |
970 | 121 | info: info.inner, |
971 | 121 | } |
972 | 640 | } else if let Some(info) = names.future_read(&lookup_context, name) { |
973 | 121 | validate_func_sig(name, &FuncType::new([ValType::I32; 2], [ValType::I32]), ty)?; |
974 | 121 | Import::FutureRead { |
975 | 121 | async_: info.async_lowered, |
976 | 121 | info: info.inner, |
977 | 121 | } |
978 | 519 | } else if let Some(info) = names.future_cancel_write(&lookup_context, name) { |
979 | 121 | validate_func_sig(name, &FuncType::new([ValType::I32], [ValType::I32]), ty)?; |
980 | 121 | Import::FutureCancelWrite { |
981 | 121 | async_: info.async_lowered, |
982 | 121 | info: info.inner, |
983 | 121 | } |
984 | 398 | } else if let Some(info) = names.future_cancel_read(&lookup_context, name) { |
985 | 121 | validate_func_sig(name, &FuncType::new([ValType::I32], [ValType::I32]), ty)?; |
986 | 121 | Import::FutureCancelRead { |
987 | 121 | async_: info.async_lowered, |
988 | 121 | info: info.inner, |
989 | 121 | } |
990 | 277 | } else if let Some(info) = names.future_drop_writable(&lookup_context, name) { |
991 | 121 | validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?; |
992 | 121 | Import::FutureDropWritable(info) |
993 | 156 | } else if let Some(info) = names.future_drop_readable(&lookup_context, name) { |
994 | 121 | validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?; |
995 | 121 | Import::FutureDropReadable(info) |
996 | 35 | } else if let Some(info) = names.stream_new(&lookup_context, name) { |
997 | 5 | validate_func_sig(name, &FuncType::new([], [ValType::I64]), ty)?; |
998 | 5 | Import::StreamNew(info) |
999 | 30 | } else if let Some(info) = names.stream_write(&lookup_context, name) { |
1000 | 5 | validate_func_sig(name, &FuncType::new([ValType::I32; 3], [ValType::I32]), ty)?; |
1001 | 5 | Import::StreamWrite { |
1002 | 5 | async_: info.async_lowered, |
1003 | 5 | info: info.inner, |
1004 | 5 | } |
1005 | 25 | } else if let Some(info) = names.stream_read(&lookup_context, name) { |
1006 | 5 | validate_func_sig(name, &FuncType::new([ValType::I32; 3], [ValType::I32]), ty)?; |
1007 | 5 | Import::StreamRead { |
1008 | 5 | async_: info.async_lowered, |
1009 | 5 | info: info.inner, |
1010 | 5 | } |
1011 | 20 | } else if let Some(info) = names.stream_cancel_write(&lookup_context, name) { |
1012 | 5 | validate_func_sig(name, &FuncType::new([ValType::I32], [ValType::I32]), ty)?; |
1013 | 5 | Import::StreamCancelWrite { |
1014 | 5 | async_: info.async_lowered, |
1015 | 5 | info: info.inner, |
1016 | 5 | } |
1017 | 15 | } else if let Some(info) = names.stream_cancel_read(&lookup_context, name) { |
1018 | 5 | validate_func_sig(name, &FuncType::new([ValType::I32], [ValType::I32]), ty)?; |
1019 | 5 | Import::StreamCancelRead { |
1020 | 5 | async_: info.async_lowered, |
1021 | 5 | info: info.inner, |
1022 | 5 | } |
1023 | 10 | } else if let Some(info) = names.stream_drop_writable(&lookup_context, name) { |
1024 | 5 | validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?; |
1025 | 5 | Import::StreamDropWritable(info) |
1026 | 5 | } else if let Some(info) = names.stream_drop_readable(&lookup_context, name) { |
1027 | 5 | validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?; |
1028 | 5 | Import::StreamDropReadable(info) |
1029 | | } else { |
1030 | 0 | return Ok(None); |
1031 | | }; |
1032 | 882 | Ok(Some(import)) |
1033 | 2.11k | } |
1034 | | |
1035 | 4.80k | fn classify_import_with_library( |
1036 | 4.80k | &mut self, |
1037 | 4.80k | import: wasmparser::Import<'_>, |
1038 | 4.80k | library_info: Option<&LibraryInfo>, |
1039 | 4.80k | ) -> Result<bool> { |
1040 | 4.80k | let info = match library_info { |
1041 | 0 | Some(info) => info, |
1042 | 4.80k | None => return Ok(false), |
1043 | | }; |
1044 | 0 | let Some((_, instance)) = info |
1045 | 0 | .arguments |
1046 | 0 | .iter() |
1047 | 0 | .find(|(name, _items)| *name == import.module) |
1048 | | else { |
1049 | 0 | return Ok(false); |
1050 | | }; |
1051 | 0 | match instance { |
1052 | 0 | Instance::MainOrAdapter(module) => match self.names.get(import.module) { |
1053 | 0 | Some(ImportInstance::Whole(which)) => { |
1054 | 0 | if which != module { |
1055 | 0 | bail!("different whole modules imported under the same name"); |
1056 | 0 | } |
1057 | | } |
1058 | | Some(ImportInstance::Names(_)) => { |
1059 | 0 | bail!("cannot mix individual imports and whole module imports") |
1060 | | } |
1061 | 0 | None => { |
1062 | 0 | let instance = ImportInstance::Whole(module.clone()); |
1063 | 0 | self.names.insert(import.module.to_string(), instance); |
1064 | 0 | } |
1065 | | }, |
1066 | 0 | Instance::Items(items) => { |
1067 | 0 | let Some(item) = items.iter().find(|i| i.alias == import.name) else { |
1068 | 0 | return Ok(false); |
1069 | | }; |
1070 | 0 | self.insert_import(import, Import::Item(item.clone()))?; |
1071 | | } |
1072 | | } |
1073 | 0 | Ok(true) |
1074 | 4.80k | } |
1075 | | |
1076 | | /// Map an imported item, by module and field name in `self.names`, to the |
1077 | | /// kind of `Import` it is: for example, a certain-typed function from an |
1078 | | /// adapter. |
1079 | 4.80k | fn insert_import(&mut self, import: wasmparser::Import<'_>, item: Import) -> Result<()> { |
1080 | 4.80k | if let Import::MainModuleMemory(ty) = item { |
1081 | 0 | if self.imported_memory.is_some() { |
1082 | 0 | bail!("module has multiple imports for memory"); |
1083 | 0 | } |
1084 | 0 | self.imported_memory = Some(ty); |
1085 | 4.80k | } |
1086 | 4.80k | let entry = self |
1087 | 4.80k | .names |
1088 | 4.80k | .entry(import.module.to_string()) |
1089 | 4.80k | .or_insert(ImportInstance::Names(IndexMap::default())); |
1090 | 4.80k | let names = match entry { |
1091 | 4.80k | ImportInstance::Names(names) => names, |
1092 | 0 | _ => bail!("cannot mix individual imports with module imports"), |
1093 | | }; |
1094 | 4.80k | let entry = match names.entry(import.name.to_string()) { |
1095 | | Entry::Occupied(_) => { |
1096 | 0 | bail!( |
1097 | | "module has duplicate import for `{}::{}`", |
1098 | | import.module, |
1099 | | import.name |
1100 | | ); |
1101 | | } |
1102 | 4.80k | Entry::Vacant(v) => v, |
1103 | | }; |
1104 | 4.80k | log::trace!( |
1105 | | "classifying import `{}::{} as {item:?}", |
1106 | | import.module, |
1107 | | import.name |
1108 | | ); |
1109 | 4.80k | entry.insert(item); |
1110 | 4.80k | Ok(()) |
1111 | 4.80k | } |
1112 | | } |
1113 | | |
1114 | | /// Dual of `ImportMap` except describes the exports of a module instead of the |
1115 | | /// imports. |
1116 | | #[derive(Default)] |
1117 | | pub struct ExportMap { |
1118 | | names: IndexMap<String, Export>, |
1119 | | raw_exports: IndexMap<String, FuncType>, |
1120 | | } |
1121 | | |
1122 | | /// All possible (known) exports from a core wasm module that are recognized and |
1123 | | /// handled during the componentization process. |
1124 | | #[derive(Debug)] |
1125 | | pub enum Export { |
1126 | | /// An export of a top-level function of a world, where the world function |
1127 | | /// is named here. |
1128 | | WorldFunc(WorldKey, String, AbiVariant), |
1129 | | |
1130 | | /// A post-return for a top-level function of a world. |
1131 | | WorldFuncPostReturn(WorldKey), |
1132 | | |
1133 | | /// An export of a function in an interface. |
1134 | | InterfaceFunc(WorldKey, InterfaceId, String, AbiVariant), |
1135 | | |
1136 | | /// A post-return for the above function. |
1137 | | InterfaceFuncPostReturn(WorldKey, String), |
1138 | | |
1139 | | /// A destructor for an exported resource. |
1140 | | ResourceDtor(TypeId), |
1141 | | |
1142 | | /// Memory, typically for an adapter. |
1143 | | Memory, |
1144 | | |
1145 | | /// `cabi_realloc` |
1146 | | GeneralPurposeRealloc, |
1147 | | |
1148 | | /// `cabi_export_realloc` |
1149 | | GeneralPurposeExportRealloc, |
1150 | | |
1151 | | /// `cabi_import_realloc` |
1152 | | GeneralPurposeImportRealloc, |
1153 | | |
1154 | | /// `_initialize` |
1155 | | Initialize, |
1156 | | |
1157 | | /// `cabi_realloc_adapter` |
1158 | | ReallocForAdapter, |
1159 | | |
1160 | | WorldFuncCallback(WorldKey), |
1161 | | |
1162 | | InterfaceFuncCallback(WorldKey, String), |
1163 | | |
1164 | | /// __indirect_function_table, used for `thread.new-indirect` |
1165 | | IndirectFunctionTable, |
1166 | | |
1167 | | /// Used to hook lifecycle events for tasks. |
1168 | | WasmTaskHook, |
1169 | | } |
1170 | | |
1171 | | impl ExportMap { |
1172 | 6.08k | fn add( |
1173 | 6.08k | &mut self, |
1174 | 6.08k | export: wasmparser::Export<'_>, |
1175 | 6.08k | encoder: &ComponentEncoder, |
1176 | 6.08k | exports: &IndexSet<WorldKey>, |
1177 | 6.08k | types: TypesRef<'_>, |
1178 | 6.08k | ) -> Result<()> { |
1179 | 6.08k | if let Some(item) = self.classify(export, encoder, exports, types)? { |
1180 | 6.08k | log::debug!("classifying export `{}` as {item:?}", export.name); |
1181 | 6.08k | let prev = self.names.insert(export.name.to_string(), item); |
1182 | 6.08k | assert!(prev.is_none()); |
1183 | 0 | } |
1184 | 6.08k | Ok(()) |
1185 | 6.08k | } |
1186 | | |
1187 | 6.08k | fn classify( |
1188 | 6.08k | &mut self, |
1189 | 6.08k | export: wasmparser::Export<'_>, |
1190 | 6.08k | encoder: &ComponentEncoder, |
1191 | 6.08k | exports: &IndexSet<WorldKey>, |
1192 | 6.08k | types: TypesRef<'_>, |
1193 | 6.08k | ) -> Result<Option<Export>> { |
1194 | 6.08k | match export.kind { |
1195 | 5.49k | ExternalKind::Func => { |
1196 | 5.49k | let ty = types[types.core_function_at(export.index)].unwrap_func(); |
1197 | 5.49k | self.raw_exports.insert(export.name.to_string(), ty.clone()); |
1198 | 5.49k | } |
1199 | 593 | _ => {} |
1200 | | } |
1201 | | |
1202 | | // Handle a few special-cased names first. |
1203 | 6.08k | if export.name == "canonical_abi_realloc" { |
1204 | 0 | return Ok(Some(Export::GeneralPurposeRealloc)); |
1205 | 6.08k | } else if export.name == "cabi_import_realloc" { |
1206 | 0 | return Ok(Some(Export::GeneralPurposeImportRealloc)); |
1207 | 6.08k | } else if export.name == "cabi_export_realloc" { |
1208 | 0 | return Ok(Some(Export::GeneralPurposeExportRealloc)); |
1209 | 6.08k | } else if export.name == "cabi_realloc_adapter" { |
1210 | 0 | return Ok(Some(Export::ReallocForAdapter)); |
1211 | 6.08k | } |
1212 | | |
1213 | 6.08k | let (name, names) = match export.name.strip_prefix("cm32p2") { |
1214 | 301 | Some(name) => (name, STANDARD), |
1215 | 0 | None if encoder.reject_legacy_names => return Ok(None), |
1216 | 5.78k | None => (export.name, LEGACY), |
1217 | | }; |
1218 | | |
1219 | 6.08k | if let Some(export) = self |
1220 | 6.08k | .classify_component_export(names, name, &export, encoder, exports, types) |
1221 | 6.08k | .with_context(|| format!("failed to classify export `{}`", export.name))? |
1222 | | { |
1223 | 6.08k | return Ok(Some(export)); |
1224 | 0 | } |
1225 | 0 | log::debug!("unknown export `{}`", export.name); |
1226 | 0 | Ok(None) |
1227 | 6.08k | } |
1228 | | |
1229 | 6.08k | fn classify_component_export( |
1230 | 6.08k | &mut self, |
1231 | 6.08k | names: &dyn NameMangling, |
1232 | 6.08k | name: &str, |
1233 | 6.08k | export: &wasmparser::Export<'_>, |
1234 | 6.08k | encoder: &ComponentEncoder, |
1235 | 6.08k | exports: &IndexSet<WorldKey>, |
1236 | 6.08k | types: TypesRef<'_>, |
1237 | 6.08k | ) -> Result<Option<Export>> { |
1238 | 6.08k | let resolve = &encoder.metadata.resolve; |
1239 | 6.08k | let world = encoder.metadata.world; |
1240 | 6.08k | match export.kind { |
1241 | 5.49k | ExternalKind::Func => {} |
1242 | | ExternalKind::Memory => { |
1243 | 593 | if name == names.export_memory() { |
1244 | 593 | return Ok(Some(Export::Memory)); |
1245 | 0 | } |
1246 | 0 | return Ok(None); |
1247 | | } |
1248 | | ExternalKind::Table => { |
1249 | 0 | if Some(name) == names.export_indirect_function_table() { |
1250 | 0 | return Ok(Some(Export::IndirectFunctionTable)); |
1251 | 0 | } |
1252 | 0 | return Ok(None); |
1253 | | } |
1254 | 0 | _ => return Ok(None), |
1255 | | } |
1256 | 5.49k | let ty = types[types.core_function_at(export.index)].unwrap_func(); |
1257 | | |
1258 | | // Handle a few special-cased names first. |
1259 | 5.49k | if name == names.export_realloc() { |
1260 | 593 | let expected = FuncType::new([ValType::I32; 4], [ValType::I32]); |
1261 | 593 | validate_func_sig(name, &expected, ty)?; |
1262 | 593 | return Ok(Some(Export::GeneralPurposeRealloc)); |
1263 | 4.90k | } else if name == names.export_initialize() { |
1264 | 593 | let expected = FuncType::new([], []); |
1265 | 593 | validate_func_sig(name, &expected, ty)?; |
1266 | 593 | return Ok(Some(Export::Initialize)); |
1267 | 4.30k | } else if Some(name) == names.export_wasm_task_hook() { |
1268 | 0 | let expected = FuncType::new([ValType::I32], []); |
1269 | 0 | validate_func_sig(name, &expected, ty)?; |
1270 | 0 | return Ok(Some(Export::WasmTaskHook)); |
1271 | 4.30k | } |
1272 | | |
1273 | 4.30k | let full_name = name; |
1274 | 4.30k | let (abi, name) = if let Some(name) = names.async_lift_name(name) { |
1275 | 656 | (AbiVariant::GuestExportAsync, name) |
1276 | 3.65k | } else if let Some(name) = names.async_lift_stackful_name(name) { |
1277 | 2 | (AbiVariant::GuestExportAsyncStackful, name) |
1278 | | } else { |
1279 | 3.65k | (AbiVariant::GuestExport, name) |
1280 | | }; |
1281 | | |
1282 | | // Try to match this to a known WIT export that `exports` allows. |
1283 | 4.30k | if let Some((key, id, f)) = names.match_wit_export(name, resolve, world, exports) { |
1284 | 2.10k | validate_func(resolve, ty, f, abi).with_context(|| { |
1285 | 0 | let key = resolve.name_world_key(key); |
1286 | 0 | format!("failed to validate export for `{key}`") |
1287 | 0 | })?; |
1288 | 2.10k | match id { |
1289 | 2.03k | Some(id) => { |
1290 | 2.03k | return Ok(Some(Export::InterfaceFunc( |
1291 | 2.03k | key.clone(), |
1292 | 2.03k | id, |
1293 | 2.03k | f.name.clone(), |
1294 | 2.03k | abi, |
1295 | 2.03k | ))); |
1296 | | } |
1297 | | None => { |
1298 | 70 | return Ok(Some(Export::WorldFunc(key.clone(), f.name.clone(), abi))); |
1299 | | } |
1300 | | } |
1301 | 2.20k | } |
1302 | | |
1303 | | // See if this is a post-return for any known WIT export. |
1304 | 2.20k | if let Some(remaining) = names.strip_post_return(name) { |
1305 | 1.44k | if let Some((key, id, f)) = names.match_wit_export(remaining, resolve, world, exports) { |
1306 | 1.44k | validate_post_return(resolve, ty, f).with_context(|| { |
1307 | 0 | let key = resolve.name_world_key(key); |
1308 | 0 | format!("failed to validate export for `{key}`") |
1309 | 0 | })?; |
1310 | 1.44k | match id { |
1311 | 1.37k | Some(_id) => { |
1312 | 1.37k | return Ok(Some(Export::InterfaceFuncPostReturn( |
1313 | 1.37k | key.clone(), |
1314 | 1.37k | f.name.clone(), |
1315 | 1.37k | ))); |
1316 | | } |
1317 | | None => { |
1318 | 68 | return Ok(Some(Export::WorldFuncPostReturn(key.clone()))); |
1319 | | } |
1320 | | } |
1321 | 0 | } |
1322 | 761 | } |
1323 | | |
1324 | 761 | if let Some(suffix) = names.async_lift_callback_name(full_name) { |
1325 | 656 | if let Some((key, id, f)) = names.match_wit_export(suffix, resolve, world, exports) { |
1326 | 656 | validate_func_sig( |
1327 | 656 | full_name, |
1328 | 656 | &FuncType::new([ValType::I32; 3], [ValType::I32]), |
1329 | 656 | ty, |
1330 | 0 | )?; |
1331 | 656 | return Ok(Some(if id.is_some() { |
1332 | 656 | Export::InterfaceFuncCallback(key.clone(), f.name.clone()) |
1333 | | } else { |
1334 | 0 | Export::WorldFuncCallback(key.clone()) |
1335 | | })); |
1336 | 0 | } |
1337 | 105 | } |
1338 | | |
1339 | | // And, finally, see if it matches a known destructor. |
1340 | 105 | if let Some(dtor) = names.match_wit_resource_dtor(name, resolve, world, exports) { |
1341 | 105 | let expected = FuncType::new([ValType::I32], []); |
1342 | 105 | validate_func_sig(full_name, &expected, ty)?; |
1343 | 105 | return Ok(Some(Export::ResourceDtor(dtor))); |
1344 | 0 | } |
1345 | | |
1346 | 0 | Ok(None) |
1347 | 6.08k | } |
1348 | | |
1349 | | /// Returns the name of the post-return export, if any, for the `key` and |
1350 | | /// `func` combo. |
1351 | 2.10k | pub fn post_return(&self, key: &WorldKey, func: &Function) -> Option<&str> { |
1352 | 131k | self.find(|m| match m { |
1353 | 580 | Export::WorldFuncPostReturn(k) => k == key, |
1354 | 51.3k | Export::InterfaceFuncPostReturn(k, f) => k == key && func.name == *f, |
1355 | 80.0k | _ => false, |
1356 | 131k | }) |
1357 | 2.10k | } |
1358 | | |
1359 | | /// Returns the name of the async callback export, if any, for the `key` and |
1360 | | /// `func` combo. |
1361 | 2.10k | pub fn callback(&self, key: &WorldKey, func: &Function) -> Option<&str> { |
1362 | 228k | self.find(|m| match m { |
1363 | 0 | Export::WorldFuncCallback(k) => k == key, |
1364 | 6.16k | Export::InterfaceFuncCallback(k, f) => k == key && func.name == *f, |
1365 | 221k | _ => false, |
1366 | 228k | }) |
1367 | 2.10k | } |
1368 | | |
1369 | 2.10k | pub fn abi(&self, key: &WorldKey, func: &Function) -> Option<AbiVariant> { |
1370 | 2.10k | self.names.values().find_map(|m| match m { |
1371 | 583 | Export::WorldFunc(k, f, abi) if k == key && func.name == *f => Some(*abi), |
1372 | 57.4k | Export::InterfaceFunc(k, _, f, abi) if k == key && func.name == *f => Some(*abi), |
1373 | 114k | _ => None, |
1374 | 116k | }) |
1375 | 2.10k | } |
1376 | | |
1377 | | /// Returns the realloc that the exported function `interface` and `func` |
1378 | | /// are using. |
1379 | 445 | pub fn export_realloc_for(&self, key: &WorldKey, func: &str) -> Option<&str> { |
1380 | | // TODO: This realloc detection should probably be improved with |
1381 | | // some sort of scheme to have per-function reallocs like |
1382 | | // `cabi_realloc_{name}` or something like that. |
1383 | 445 | let _ = (key, func); |
1384 | | |
1385 | 26.6k | if let Some(name) = self.find(|m| matches!(m, Export::GeneralPurposeExportRealloc)) { |
1386 | 0 | return Some(name); |
1387 | 445 | } |
1388 | 445 | self.general_purpose_realloc() |
1389 | 445 | } |
1390 | | |
1391 | | /// Returns the realloc that the imported function `interface` and `func` |
1392 | | /// are using. |
1393 | 1.41k | pub fn import_realloc_for(&self, interface: Option<InterfaceId>, func: &str) -> Option<&str> { |
1394 | | // TODO: This realloc detection should probably be improved with |
1395 | | // some sort of scheme to have per-function reallocs like |
1396 | | // `cabi_realloc_{name}` or something like that. |
1397 | 1.41k | let _ = (interface, func); |
1398 | | |
1399 | 1.41k | self.import_realloc_fallback() |
1400 | 1.41k | } |
1401 | | |
1402 | | /// Returns the general-purpose realloc function to use for imports. |
1403 | | /// |
1404 | | /// Note that `import_realloc_for` should be used instead where possible. |
1405 | 1.41k | pub fn import_realloc_fallback(&self) -> Option<&str> { |
1406 | 48.0k | if let Some(name) = self.find(|m| matches!(m, Export::GeneralPurposeImportRealloc)) { |
1407 | 0 | return Some(name); |
1408 | 1.41k | } |
1409 | 1.41k | self.general_purpose_realloc() |
1410 | 1.41k | } |
1411 | | |
1412 | | /// Returns the realloc that the main module is exporting into the adapter. |
1413 | 0 | pub fn realloc_to_import_into_adapter(&self) -> Option<&str> { |
1414 | 0 | if let Some(name) = self.find(|m| matches!(m, Export::ReallocForAdapter)) { |
1415 | 0 | return Some(name); |
1416 | 0 | } |
1417 | 0 | self.general_purpose_realloc() |
1418 | 0 | } |
1419 | | |
1420 | 1.86k | pub fn general_purpose_realloc(&self) -> Option<&str> { |
1421 | 72.8k | self.find(|m| matches!(m, Export::GeneralPurposeRealloc)) |
1422 | 1.86k | } |
1423 | | |
1424 | | /// Returns an iterator over all `realloc` functions exported by this module |
1425 | | /// which may be used as a `realloc` canonical option. |
1426 | | /// |
1427 | | /// Note that `cabi_realloc_adapter` is intentionally not included here as |
1428 | | /// that's only ever imported directly into an adapter module and is never |
1429 | | /// used as a canonical option. |
1430 | 0 | pub fn reallocs(&self) -> impl Iterator<Item = &str> + '_ { |
1431 | 0 | self.names.iter().filter_map(|(name, export)| match export { |
1432 | | Export::GeneralPurposeRealloc |
1433 | | | Export::GeneralPurposeExportRealloc |
1434 | 0 | | Export::GeneralPurposeImportRealloc => Some(name.as_str()), |
1435 | 0 | _ => None, |
1436 | 0 | }) |
1437 | 0 | } |
1438 | | |
1439 | | /// Returns the memory, if exported, for this module. |
1440 | 593 | pub fn memory(&self) -> Option<&str> { |
1441 | 4.90k | self.find(|m| matches!(m, Export::Memory)) |
1442 | 593 | } |
1443 | | |
1444 | | /// Returns the indirect function table, if exported, for this module. |
1445 | 0 | pub fn indirect_function_table(&self) -> Option<&str> { |
1446 | 0 | self.find(|t| matches!(t, Export::IndirectFunctionTable)) |
1447 | 0 | } |
1448 | | |
1449 | | /// Returns the hook for tasks, if exported. |
1450 | 653 | pub fn wasm_task_hook(&self) -> Option<&str> { |
1451 | 10.0k | self.find(|t| matches!(t, Export::WasmTaskHook)) |
1452 | 653 | } |
1453 | | |
1454 | | /// Returns the `_initialize` intrinsic, if exported, for this module. |
1455 | 593 | pub fn initialize(&self) -> Option<&str> { |
1456 | 6.08k | self.find(|m| matches!(m, Export::Initialize)) |
1457 | 593 | } |
1458 | | |
1459 | | /// Returns destructor for the exported resource `ty`, if it was listed. |
1460 | 105 | pub fn resource_dtor(&self, ty: TypeId) -> Option<&str> { |
1461 | 8.22k | self.find(|m| match m { |
1462 | 361 | Export::ResourceDtor(t) => *t == ty, |
1463 | 7.86k | _ => false, |
1464 | 8.22k | }) |
1465 | 105 | } |
1466 | | |
1467 | | /// NB: this is a linear search and if that's ever a problem this should |
1468 | | /// build up an inverse map during construction to accelerate it. |
1469 | 11.9k | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { |
1470 | 653k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#2}::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 91 | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#1}::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 116k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::initialize::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 6.08k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::post_return::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 131k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::resource_dtor::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 8.22k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::wasm_task_hook::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 10.0k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::export_realloc_for::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 26.6k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::general_purpose_realloc::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 72.8k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::import_realloc_fallback::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 48.0k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
Unexecuted instantiation: <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::indirect_function_table::{closure#0}>::{closure#0}Unexecuted instantiation: <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::realloc_to_import_into_adapter::{closure#0}>::{closure#0}<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::memory::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 4.90k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::callback::{closure#0}>::{closure#0}Line | Count | Source | 1470 | 228k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; |
|
1471 | 7.35k | Some(name) |
1472 | 11.9k | } <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#2}::{closure#0}>Line | Count | Source | 1469 | 70 | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 70 | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 70 | Some(name) | 1472 | 70 | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#1}::{closure#0}>Line | Count | Source | 1469 | 2.03k | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 2.03k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 2.03k | Some(name) | 1472 | 2.03k | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::initialize::{closure#0}>Line | Count | Source | 1469 | 593 | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 593 | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 593 | Some(name) | 1472 | 593 | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::post_return::{closure#0}>Line | Count | Source | 1469 | 2.10k | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 2.10k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 1.44k | Some(name) | 1472 | 2.10k | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::resource_dtor::{closure#0}>Line | Count | Source | 1469 | 105 | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 105 | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 105 | Some(name) | 1472 | 105 | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::wasm_task_hook::{closure#0}>Line | Count | Source | 1469 | 653 | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 653 | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 0 | Some(name) | 1472 | 653 | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::export_realloc_for::{closure#0}>Line | Count | Source | 1469 | 445 | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 445 | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 0 | Some(name) | 1472 | 445 | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::general_purpose_realloc::{closure#0}>Line | Count | Source | 1469 | 1.86k | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 1.86k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 1.86k | Some(name) | 1472 | 1.86k | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::import_realloc_fallback::{closure#0}>Line | Count | Source | 1469 | 1.41k | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 1.41k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 0 | Some(name) | 1472 | 1.41k | } |
Unexecuted instantiation: <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::indirect_function_table::{closure#0}>Unexecuted instantiation: <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::realloc_to_import_into_adapter::{closure#0}><wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::memory::{closure#0}>Line | Count | Source | 1469 | 593 | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 593 | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 593 | Some(name) | 1472 | 593 | } |
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::callback::{closure#0}>Line | Count | Source | 1469 | 2.10k | fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> { | 1470 | 2.10k | let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?; | 1471 | 656 | Some(name) | 1472 | 2.10k | } |
|
1473 | | |
1474 | | /// Iterates over all exports of this module. |
1475 | 898 | pub fn iter(&self) -> impl Iterator<Item = (&str, &Export)> + '_ { |
1476 | 11.3k | self.names.iter().map(|(n, e)| (n.as_str(), e)) |
1477 | 898 | } |
1478 | | |
1479 | 593 | fn validate(&self, encoder: &ComponentEncoder, exports: &IndexSet<WorldKey>) -> Result<()> { |
1480 | 593 | let resolve = &encoder.metadata.resolve; |
1481 | 593 | let world = encoder.metadata.world; |
1482 | | // Multi-memory isn't supported because otherwise we don't know what |
1483 | | // memory to put things in. |
1484 | 593 | if self |
1485 | 593 | .names |
1486 | 593 | .values() |
1487 | 6.08k | .filter(|m| matches!(m, Export::Memory)) |
1488 | 593 | .count() |
1489 | | > 1 |
1490 | | { |
1491 | 0 | bail!("cannot componentize module that exports multiple memories") |
1492 | 593 | } |
1493 | | |
1494 | | // Every async-with-callback-lifted export must have a callback. |
1495 | 6.08k | for (name, export) in &self.names { |
1496 | 70 | match export { |
1497 | | Export::WorldFunc(_, _, AbiVariant::GuestExportAsync) => { |
1498 | 0 | if !matches!( |
1499 | 0 | self.names.get(&format!("[callback]{name}")), |
1500 | | Some(Export::WorldFuncCallback(_)) |
1501 | | ) { |
1502 | 0 | bail!("missing callback for `{name}`"); |
1503 | 0 | } |
1504 | | } |
1505 | | Export::InterfaceFunc(_, _, _, AbiVariant::GuestExportAsync) => { |
1506 | 0 | if !matches!( |
1507 | 656 | self.names.get(&format!("[callback]{name}")), |
1508 | | Some(Export::InterfaceFuncCallback(_, _)) |
1509 | | ) { |
1510 | 0 | bail!("missing callback for `{name}`"); |
1511 | 656 | } |
1512 | | } |
1513 | 5.43k | _ => {} |
1514 | | } |
1515 | | } |
1516 | | |
1517 | | // All of `exports` must be exported and found within this module. |
1518 | 1.20k | for export in exports { |
1519 | 2.03k | let require_interface_func = |interface: InterfaceId, name: &str| -> Result<()> { |
1520 | 116k | let result = self.find(|e| match e { |
1521 | 57.4k | Export::InterfaceFunc(_, id, s, _) => interface == *id && name == s, |
1522 | 59.2k | _ => false, |
1523 | 116k | }); |
1524 | 2.03k | if result.is_some() { |
1525 | 2.03k | Ok(()) |
1526 | | } else { |
1527 | 0 | let export = resolve.name_world_key(export); |
1528 | 0 | bail!("failed to find export of interface `{export}` function `{name}`") |
1529 | | } |
1530 | 2.03k | }; |
1531 | 1.20k | let require_world_func = |name: &str| -> Result<()> { |
1532 | 91 | let result = self.find(|e| match e { |
1533 | 81 | Export::WorldFunc(_, s, _) => name == s, |
1534 | 10 | _ => false, |
1535 | 91 | }); |
1536 | 70 | if result.is_some() { |
1537 | 70 | Ok(()) |
1538 | | } else { |
1539 | 0 | bail!("failed to find export of function `{name}`") |
1540 | | } |
1541 | 70 | }; |
1542 | 1.20k | match &resolve.worlds[world].exports[export] { |
1543 | 1.13k | WorldItem::Interface { id, .. } => { |
1544 | 2.03k | for (name, _) in resolve.interfaces[*id].functions.iter() { |
1545 | 2.03k | require_interface_func(*id, name)?; |
1546 | | } |
1547 | | } |
1548 | 70 | WorldItem::Function(f) => { |
1549 | 70 | require_world_func(&f.name)?; |
1550 | | } |
1551 | 0 | WorldItem::Type { .. } => unreachable!(), |
1552 | | } |
1553 | | } |
1554 | | |
1555 | 593 | Ok(()) |
1556 | 593 | } |
1557 | | } |
1558 | | |
1559 | | /// A builtin that may be declared as cancellable. |
1560 | | struct MaybeCancellable<T> { |
1561 | | #[allow(unused)] |
1562 | | inner: T, |
1563 | | cancellable: bool, |
1564 | | } |
1565 | | |
1566 | | /// A builtin that may be declared as async-lowered. |
1567 | | struct MaybeAsyncLowered<T> { |
1568 | | inner: T, |
1569 | | async_lowered: bool, |
1570 | | } |
1571 | | |
1572 | | /// Context passed to `NameMangling` implementations of stream and future functions |
1573 | | /// to help with looking up payload information. |
1574 | | struct PayloadLookupContext<'a> { |
1575 | | resolve: &'a Resolve, |
1576 | | world: &'a World, |
1577 | | id: Option<InterfaceId>, |
1578 | | import: bool, |
1579 | | key: Option<WorldKey>, |
1580 | | } |
1581 | | |
1582 | | /// Trait dispatch and definition for parsing and interpreting "mangled names" |
1583 | | /// which show up in imports and exports of the component model. |
1584 | | /// |
1585 | | /// This trait is used to implement classification of imports and exports in the |
1586 | | /// component model. The methods on `ImportMap` and `ExportMap` will use this to |
1587 | | /// determine what an import is and how it's lifted/lowered in the world being |
1588 | | /// bound. |
1589 | | /// |
1590 | | /// This trait has a bit of history behind it as well. Before |
1591 | | /// WebAssembly/component-model#378 there was no standard naming scheme for core |
1592 | | /// wasm imports or exports when componenitizing. This meant that |
1593 | | /// `wit-component` implemented a particular scheme which mostly worked but was |
1594 | | /// mostly along the lines of "this at least works" rather than "someone sat |
1595 | | /// down and designed this". Since then, however, an standard naming scheme has |
1596 | | /// now been specified which was indeed designed. |
1597 | | /// |
1598 | | /// This trait serves as the bridge between these two. The historical naming |
1599 | | /// scheme is still supported for now through the `Legacy` implementation below |
1600 | | /// and will be for some time. The transition plan at this time is to support |
1601 | | /// the new scheme, eventually get it supported in bindings generators, and once |
1602 | | /// that's all propagated remove support for the legacy scheme. |
1603 | | trait NameMangling { |
1604 | | fn import_root(&self) -> &str; |
1605 | | fn import_non_root_prefix(&self) -> &str; |
1606 | | fn import_exported_intrinsic_prefix(&self) -> &str; |
1607 | | fn export_memory(&self) -> &str; |
1608 | | fn export_initialize(&self) -> &str; |
1609 | | fn export_realloc(&self) -> &str; |
1610 | | fn export_indirect_function_table(&self) -> Option<&str>; |
1611 | | fn export_wasm_task_hook(&self) -> Option<&str>; |
1612 | | fn resource_drop_name<'a>(&self, name: &'a str) -> Option<&'a str>; |
1613 | | fn resource_new_name<'a>(&self, name: &'a str) -> Option<&'a str>; |
1614 | | fn resource_rep_name<'a>(&self, name: &'a str) -> Option<&'a str>; |
1615 | | fn task_return_name<'a>(&self, name: &'a str) -> Option<&'a str>; |
1616 | | fn task_cancel(&self, name: &str) -> bool; |
1617 | | fn backpressure_inc(&self, name: &str) -> bool; |
1618 | | fn backpressure_dec(&self, name: &str) -> bool; |
1619 | | fn waitable_set_new(&self, name: &str) -> bool; |
1620 | | fn waitable_set_wait(&self, name: &str) -> Option<(MaybeCancellable<()>, ValType)>; |
1621 | | fn waitable_set_poll(&self, name: &str) -> Option<(MaybeCancellable<()>, ValType)>; |
1622 | | fn waitable_set_drop(&self, name: &str) -> bool; |
1623 | | fn waitable_join(&self, name: &str) -> bool; |
1624 | | fn subtask_drop(&self, name: &str) -> bool; |
1625 | | fn subtask_cancel(&self, name: &str) -> Option<MaybeAsyncLowered<()>>; |
1626 | | fn async_lift_callback_name<'a>(&self, name: &'a str) -> Option<&'a str>; |
1627 | | fn async_lift_name<'a>(&self, name: &'a str) -> Option<&'a str>; |
1628 | | fn async_lift_stackful_name<'a>(&self, name: &'a str) -> Option<&'a str>; |
1629 | | fn error_context_new(&self, name: &str) -> Option<StringEncoding>; |
1630 | | fn error_context_debug_message(&self, name: &str) -> Option<StringEncoding>; |
1631 | | fn error_context_drop(&self, name: &str) -> bool; |
1632 | | fn context_get(&self, name: &str) -> Option<(ValType, u32)>; |
1633 | | fn context_set(&self, name: &str) -> Option<(ValType, u32)>; |
1634 | | fn future_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option<PayloadInfo>; |
1635 | | fn future_write( |
1636 | | &self, |
1637 | | lookup_context: &PayloadLookupContext, |
1638 | | name: &str, |
1639 | | ) -> Option<MaybeAsyncLowered<PayloadInfo>>; |
1640 | | fn future_read( |
1641 | | &self, |
1642 | | lookup_context: &PayloadLookupContext, |
1643 | | name: &str, |
1644 | | ) -> Option<MaybeAsyncLowered<PayloadInfo>>; |
1645 | | fn future_cancel_write( |
1646 | | &self, |
1647 | | lookup_context: &PayloadLookupContext, |
1648 | | name: &str, |
1649 | | ) -> Option<MaybeAsyncLowered<PayloadInfo>>; |
1650 | | fn future_cancel_read( |
1651 | | &self, |
1652 | | lookup_context: &PayloadLookupContext, |
1653 | | name: &str, |
1654 | | ) -> Option<MaybeAsyncLowered<PayloadInfo>>; |
1655 | | fn future_drop_writable( |
1656 | | &self, |
1657 | | lookup_context: &PayloadLookupContext, |
1658 | | name: &str, |
1659 | | ) -> Option<PayloadInfo>; |
1660 | | fn future_drop_readable( |
1661 | | &self, |
1662 | | lookup_context: &PayloadLookupContext, |
1663 | | name: &str, |
1664 | | ) -> Option<PayloadInfo>; |
1665 | | fn stream_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option<PayloadInfo>; |
1666 | | fn stream_write( |
1667 | | &self, |
1668 | | lookup_context: &PayloadLookupContext, |
1669 | | name: &str, |
1670 | | ) -> Option<MaybeAsyncLowered<PayloadInfo>>; |
1671 | | fn stream_read( |
1672 | | &self, |
1673 | | lookup_context: &PayloadLookupContext, |
1674 | | name: &str, |
1675 | | ) -> Option<MaybeAsyncLowered<PayloadInfo>>; |
1676 | | fn stream_cancel_write( |
1677 | | &self, |
1678 | | lookup_context: &PayloadLookupContext, |
1679 | | name: &str, |
1680 | | ) -> Option<MaybeAsyncLowered<PayloadInfo>>; |
1681 | | fn stream_cancel_read( |
1682 | | &self, |
1683 | | lookup_context: &PayloadLookupContext, |
1684 | | name: &str, |
1685 | | ) -> Option<MaybeAsyncLowered<PayloadInfo>>; |
1686 | | fn stream_drop_writable( |
1687 | | &self, |
1688 | | lookup_context: &PayloadLookupContext, |
1689 | | name: &str, |
1690 | | ) -> Option<PayloadInfo>; |
1691 | | fn stream_drop_readable( |
1692 | | &self, |
1693 | | lookup_context: &PayloadLookupContext, |
1694 | | name: &str, |
1695 | | ) -> Option<PayloadInfo>; |
1696 | | fn thread_index(&self, name: &str) -> bool; |
1697 | | fn thread_new_indirect(&self, name: &str) -> bool; |
1698 | | fn thread_resume_later(&self, name: &str) -> bool; |
1699 | | fn thread_suspend(&self, name: &str) -> Option<MaybeCancellable<()>>; |
1700 | | fn thread_yield(&self, name: &str) -> Option<MaybeCancellable<()>>; |
1701 | | fn thread_suspend_then_resume(&self, name: &str) -> Option<MaybeCancellable<()>>; |
1702 | | fn thread_yield_then_resume(&self, name: &str) -> Option<MaybeCancellable<()>>; |
1703 | | fn thread_suspend_then_promote(&self, name: &str) -> Option<MaybeCancellable<()>>; |
1704 | | fn thread_yield_then_promote(&self, name: &str) -> Option<MaybeCancellable<()>>; |
1705 | | fn module_to_interface( |
1706 | | &self, |
1707 | | module: &str, |
1708 | | resolve: &Resolve, |
1709 | | items: &IndexMap<WorldKey, WorldItem>, |
1710 | | ) -> Result<(WorldKey, InterfaceId)>; |
1711 | | fn strip_post_return<'a>(&self, name: &'a str) -> Option<&'a str>; |
1712 | | fn match_wit_export<'a>( |
1713 | | &self, |
1714 | | export_name: &str, |
1715 | | resolve: &'a Resolve, |
1716 | | world: WorldId, |
1717 | | exports: &'a IndexSet<WorldKey>, |
1718 | | ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)>; |
1719 | | fn match_wit_resource_dtor<'a>( |
1720 | | &self, |
1721 | | export_name: &str, |
1722 | | resolve: &'a Resolve, |
1723 | | world: WorldId, |
1724 | | exports: &'a IndexSet<WorldKey>, |
1725 | | ) -> Option<TypeId>; |
1726 | | fn world_key_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant); |
1727 | | fn interface_function_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant); |
1728 | | fn env_import(&self, name: &str, ty: &FuncType) -> Option<Import>; |
1729 | | } |
1730 | | |
1731 | | /// Definition of the "standard" naming scheme which currently starts with |
1732 | | /// "cm32p2". Note that wasm64 is not supported at this time. |
1733 | | struct Standard; |
1734 | | |
1735 | | const STANDARD: &'static dyn NameMangling = &Standard; |
1736 | | |
1737 | | impl NameMangling for Standard { |
1738 | 53 | fn import_root(&self) -> &str { |
1739 | 53 | "" |
1740 | 53 | } |
1741 | 26 | fn import_non_root_prefix(&self) -> &str { |
1742 | 26 | "|" |
1743 | 26 | } |
1744 | 52 | fn import_exported_intrinsic_prefix(&self) -> &str { |
1745 | 52 | "_ex_" |
1746 | 52 | } |
1747 | 62 | fn export_memory(&self) -> &str { |
1748 | 62 | "_memory" |
1749 | 62 | } |
1750 | 177 | fn export_initialize(&self) -> &str { |
1751 | 177 | "_initialize" |
1752 | 177 | } |
1753 | 239 | fn export_realloc(&self) -> &str { |
1754 | 239 | "_realloc" |
1755 | 239 | } |
1756 | 0 | fn export_indirect_function_table(&self) -> Option<&str> { |
1757 | 0 | None |
1758 | 0 | } |
1759 | 115 | fn export_wasm_task_hook(&self) -> Option<&str> { |
1760 | 115 | None |
1761 | 115 | } |
1762 | 12 | fn resource_drop_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
1763 | 12 | name.strip_suffix("_drop") |
1764 | 12 | } |
1765 | 2 | fn resource_new_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
1766 | 2 | name.strip_suffix("_new") |
1767 | 2 | } |
1768 | 1 | fn resource_rep_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
1769 | 1 | name.strip_suffix("_rep") |
1770 | 1 | } |
1771 | 0 | fn task_return_name<'a>(&self, _name: &'a str) -> Option<&'a str> { |
1772 | 0 | None |
1773 | 0 | } |
1774 | 0 | fn task_cancel(&self, _name: &str) -> bool { |
1775 | 0 | false |
1776 | 0 | } |
1777 | 27 | fn backpressure_inc(&self, _name: &str) -> bool { |
1778 | 27 | false |
1779 | 27 | } |
1780 | 27 | fn backpressure_dec(&self, _name: &str) -> bool { |
1781 | 27 | false |
1782 | 27 | } |
1783 | 27 | fn waitable_set_new(&self, _name: &str) -> bool { |
1784 | 27 | false |
1785 | 27 | } |
1786 | 27 | fn waitable_set_wait(&self, _name: &str) -> Option<(MaybeCancellable<()>, ValType)> { |
1787 | 27 | None |
1788 | 27 | } |
1789 | 27 | fn waitable_set_poll(&self, _name: &str) -> Option<(MaybeCancellable<()>, ValType)> { |
1790 | 27 | None |
1791 | 27 | } |
1792 | 27 | fn waitable_set_drop(&self, _name: &str) -> bool { |
1793 | 27 | false |
1794 | 27 | } |
1795 | 27 | fn waitable_join(&self, _name: &str) -> bool { |
1796 | 27 | false |
1797 | 27 | } |
1798 | 27 | fn subtask_drop(&self, _name: &str) -> bool { |
1799 | 27 | false |
1800 | 27 | } |
1801 | 27 | fn subtask_cancel(&self, _name: &str) -> Option<MaybeAsyncLowered<()>> { |
1802 | 27 | None |
1803 | 27 | } |
1804 | 1 | fn async_lift_callback_name<'a>(&self, _name: &'a str) -> Option<&'a str> { |
1805 | 1 | None |
1806 | 1 | } |
1807 | 115 | fn async_lift_name<'a>(&self, _name: &'a str) -> Option<&'a str> { |
1808 | 115 | None |
1809 | 115 | } |
1810 | 115 | fn async_lift_stackful_name<'a>(&self, _name: &'a str) -> Option<&'a str> { |
1811 | 115 | None |
1812 | 115 | } |
1813 | 27 | fn error_context_new(&self, _name: &str) -> Option<StringEncoding> { |
1814 | 27 | None |
1815 | 27 | } |
1816 | 27 | fn error_context_debug_message(&self, _name: &str) -> Option<StringEncoding> { |
1817 | 27 | None |
1818 | 27 | } |
1819 | 27 | fn error_context_drop(&self, _name: &str) -> bool { |
1820 | 27 | false |
1821 | 27 | } |
1822 | 27 | fn context_get(&self, _name: &str) -> Option<(ValType, u32)> { |
1823 | 27 | None |
1824 | 27 | } |
1825 | 27 | fn context_set(&self, _name: &str) -> Option<(ValType, u32)> { |
1826 | 27 | None |
1827 | 27 | } |
1828 | 27 | fn thread_index(&self, _name: &str) -> bool { |
1829 | 27 | false |
1830 | 27 | } |
1831 | 27 | fn thread_new_indirect(&self, _name: &str) -> bool { |
1832 | 27 | false |
1833 | 27 | } |
1834 | 27 | fn thread_resume_later(&self, _name: &str) -> bool { |
1835 | 27 | false |
1836 | 27 | } |
1837 | 27 | fn thread_suspend(&self, _name: &str) -> Option<MaybeCancellable<()>> { |
1838 | 27 | None |
1839 | 27 | } |
1840 | 27 | fn thread_yield(&self, _name: &str) -> Option<MaybeCancellable<()>> { |
1841 | 27 | None |
1842 | 27 | } |
1843 | 27 | fn thread_suspend_then_resume(&self, _name: &str) -> Option<MaybeCancellable<()>> { |
1844 | 27 | None |
1845 | 27 | } |
1846 | 27 | fn thread_yield_then_resume(&self, _name: &str) -> Option<MaybeCancellable<()>> { |
1847 | 27 | None |
1848 | 27 | } |
1849 | 27 | fn thread_suspend_then_promote(&self, _name: &str) -> Option<MaybeCancellable<()>> { |
1850 | 27 | None |
1851 | 27 | } |
1852 | 27 | fn thread_yield_then_promote(&self, _name: &str) -> Option<MaybeCancellable<()>> { |
1853 | 27 | None |
1854 | 27 | } |
1855 | 0 | fn future_new( |
1856 | 0 | &self, |
1857 | 0 | _lookup_context: &PayloadLookupContext, |
1858 | 0 | _name: &str, |
1859 | 0 | ) -> Option<PayloadInfo> { |
1860 | 0 | None |
1861 | 0 | } |
1862 | 0 | fn future_write( |
1863 | 0 | &self, |
1864 | 0 | _lookup_context: &PayloadLookupContext, |
1865 | 0 | _name: &str, |
1866 | 0 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
1867 | 0 | None |
1868 | 0 | } |
1869 | 0 | fn future_read( |
1870 | 0 | &self, |
1871 | 0 | _lookup_context: &PayloadLookupContext, |
1872 | 0 | _name: &str, |
1873 | 0 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
1874 | 0 | None |
1875 | 0 | } |
1876 | 0 | fn future_cancel_write( |
1877 | 0 | &self, |
1878 | 0 | _lookup_context: &PayloadLookupContext, |
1879 | 0 | _name: &str, |
1880 | 0 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
1881 | 0 | None |
1882 | 0 | } |
1883 | 0 | fn future_cancel_read( |
1884 | 0 | &self, |
1885 | 0 | _lookup_context: &PayloadLookupContext, |
1886 | 0 | _name: &str, |
1887 | 0 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
1888 | 0 | None |
1889 | 0 | } |
1890 | 0 | fn future_drop_writable( |
1891 | 0 | &self, |
1892 | 0 | _lookup_context: &PayloadLookupContext, |
1893 | 0 | _name: &str, |
1894 | 0 | ) -> Option<PayloadInfo> { |
1895 | 0 | None |
1896 | 0 | } |
1897 | 0 | fn future_drop_readable( |
1898 | 0 | &self, |
1899 | 0 | _lookup_context: &PayloadLookupContext, |
1900 | 0 | _name: &str, |
1901 | 0 | ) -> Option<PayloadInfo> { |
1902 | 0 | None |
1903 | 0 | } |
1904 | 0 | fn stream_new( |
1905 | 0 | &self, |
1906 | 0 | _lookup_context: &PayloadLookupContext, |
1907 | 0 | _name: &str, |
1908 | 0 | ) -> Option<PayloadInfo> { |
1909 | 0 | None |
1910 | 0 | } |
1911 | 0 | fn stream_write( |
1912 | 0 | &self, |
1913 | 0 | _lookup_context: &PayloadLookupContext, |
1914 | 0 | _name: &str, |
1915 | 0 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
1916 | 0 | None |
1917 | 0 | } |
1918 | 0 | fn stream_read( |
1919 | 0 | &self, |
1920 | 0 | _lookup_context: &PayloadLookupContext, |
1921 | 0 | _name: &str, |
1922 | 0 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
1923 | 0 | None |
1924 | 0 | } |
1925 | 0 | fn stream_cancel_write( |
1926 | 0 | &self, |
1927 | 0 | _lookup_context: &PayloadLookupContext, |
1928 | 0 | _name: &str, |
1929 | 0 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
1930 | 0 | None |
1931 | 0 | } |
1932 | 0 | fn stream_cancel_read( |
1933 | 0 | &self, |
1934 | 0 | _lookup_context: &PayloadLookupContext, |
1935 | 0 | _name: &str, |
1936 | 0 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
1937 | 0 | None |
1938 | 0 | } |
1939 | 0 | fn stream_drop_writable( |
1940 | 0 | &self, |
1941 | 0 | _lookup_context: &PayloadLookupContext, |
1942 | 0 | _name: &str, |
1943 | 0 | ) -> Option<PayloadInfo> { |
1944 | 0 | None |
1945 | 0 | } |
1946 | 0 | fn stream_drop_readable( |
1947 | 0 | &self, |
1948 | 0 | _lookup_context: &PayloadLookupContext, |
1949 | 0 | _name: &str, |
1950 | 0 | ) -> Option<PayloadInfo> { |
1951 | 0 | None |
1952 | 0 | } |
1953 | 26 | fn module_to_interface( |
1954 | 26 | &self, |
1955 | 26 | interface: &str, |
1956 | 26 | resolve: &Resolve, |
1957 | 26 | items: &IndexMap<WorldKey, WorldItem>, |
1958 | 26 | ) -> Result<(WorldKey, InterfaceId)> { |
1959 | 39 | for (key, item) in items.iter() { |
1960 | 39 | let id = match key { |
1961 | | // Bare keys are matched exactly against `interface` |
1962 | 15 | WorldKey::Name(name) => match item { |
1963 | 12 | WorldItem::Interface { id, .. } if name == interface => *id, |
1964 | 3 | _ => continue, |
1965 | | }, |
1966 | | // ID-identified keys are matched with their "canonical name" |
1967 | 24 | WorldKey::Interface(id) => { |
1968 | 24 | if resolve.canonicalized_id_of(*id).as_deref() != Some(interface) { |
1969 | 10 | continue; |
1970 | 14 | } |
1971 | 14 | *id |
1972 | | } |
1973 | | }; |
1974 | 26 | return Ok((key.clone(), id)); |
1975 | | } |
1976 | 0 | bail!("failed to find world item corresponding to interface `{interface}`") |
1977 | 26 | } |
1978 | 58 | fn strip_post_return<'a>(&self, name: &'a str) -> Option<&'a str> { |
1979 | 58 | name.strip_suffix("_post") |
1980 | 58 | } |
1981 | 172 | fn match_wit_export<'a>( |
1982 | 172 | &self, |
1983 | 172 | export_name: &str, |
1984 | 172 | resolve: &'a Resolve, |
1985 | 172 | world: WorldId, |
1986 | 172 | exports: &'a IndexSet<WorldKey>, |
1987 | 172 | ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)> { |
1988 | 172 | if let Some(world_export_name) = export_name.strip_prefix("||") { |
1989 | 27 | let key = exports.get(&WorldKey::Name(world_export_name.to_string()))?; |
1990 | 18 | match &resolve.worlds[world].exports[key] { |
1991 | 18 | WorldItem::Function(f) => return Some((key, None, f)), |
1992 | 0 | _ => return None, |
1993 | | } |
1994 | 145 | } |
1995 | | |
1996 | 145 | let (key, id, func_name) = |
1997 | 145 | self.match_wit_interface(export_name, resolve, world, exports)?; |
1998 | 145 | let func = resolve.interfaces[id].functions.get(func_name)?; |
1999 | 96 | Some((key, Some(id), func)) |
2000 | 172 | } |
2001 | | |
2002 | 1 | fn match_wit_resource_dtor<'a>( |
2003 | 1 | &self, |
2004 | 1 | export_name: &str, |
2005 | 1 | resolve: &'a Resolve, |
2006 | 1 | world: WorldId, |
2007 | 1 | exports: &'a IndexSet<WorldKey>, |
2008 | 1 | ) -> Option<TypeId> { |
2009 | 1 | let (_key, id, name) = |
2010 | 1 | self.match_wit_interface(export_name.strip_suffix("_dtor")?, resolve, world, exports)?; |
2011 | 1 | let ty = *resolve.interfaces[id].types.get(name)?; |
2012 | 1 | match resolve.types[ty].kind { |
2013 | 1 | TypeDefKind::Resource => Some(ty), |
2014 | 0 | _ => None, |
2015 | | } |
2016 | 1 | } |
2017 | | |
2018 | 27 | fn world_key_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant) { |
2019 | 27 | (name, AbiVariant::GuestImport) |
2020 | 27 | } |
2021 | 23 | fn interface_function_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant) { |
2022 | 23 | (name, AbiVariant::GuestImport) |
2023 | 23 | } |
2024 | 0 | fn env_import(&self, _name: &str, _ty: &FuncType) -> Option<Import> { |
2025 | 0 | None |
2026 | 0 | } |
2027 | | } |
2028 | | |
2029 | | impl Standard { |
2030 | 146 | fn match_wit_interface<'a, 'b>( |
2031 | 146 | &self, |
2032 | 146 | export_name: &'b str, |
2033 | 146 | resolve: &'a Resolve, |
2034 | 146 | world: WorldId, |
2035 | 146 | exports: &'a IndexSet<WorldKey>, |
2036 | 146 | ) -> Option<(&'a WorldKey, InterfaceId, &'b str)> { |
2037 | 146 | let world = &resolve.worlds[world]; |
2038 | 146 | let export_name = export_name.strip_prefix("|")?; |
2039 | | |
2040 | 486 | for export in exports { |
2041 | 486 | let id = match &world.exports[export] { |
2042 | 466 | WorldItem::Interface { id, .. } => *id, |
2043 | 20 | WorldItem::Function(_) => continue, |
2044 | 0 | WorldItem::Type { .. } => unreachable!(), |
2045 | | }; |
2046 | 466 | let remaining = match export { |
2047 | 459 | WorldKey::Name(name) => export_name.strip_prefix(name), |
2048 | | WorldKey::Interface(_) => { |
2049 | 7 | let prefix = resolve.canonicalized_id_of(id).unwrap(); |
2050 | 7 | export_name.strip_prefix(&prefix) |
2051 | | } |
2052 | | }; |
2053 | 466 | let item_name = match remaining.and_then(|s| s.strip_prefix("|")) { |
2054 | 146 | Some(name) => name, |
2055 | 320 | None => continue, |
2056 | | }; |
2057 | 146 | return Some((export, id, item_name)); |
2058 | | } |
2059 | | |
2060 | 0 | None |
2061 | 146 | } |
2062 | | } |
2063 | | |
2064 | | /// Definition of wit-component's "legacy" naming scheme which predates |
2065 | | /// WebAssembly/component-model#378. |
2066 | | struct Legacy; |
2067 | | |
2068 | | const LEGACY: &'static dyn NameMangling = &Legacy; |
2069 | | |
2070 | | impl Legacy { |
2071 | | // Looks for `[$prefix-N]foo` within `name`. If found then `foo` is |
2072 | | // used to find a function within `id` and `world` above. Once found |
2073 | | // then `N` is used to index within that function to extract a |
2074 | | // future/stream type. If that's all found then a `PayloadInfo` is |
2075 | | // returned to get attached to an intrinsic. |
2076 | 3.77k | fn prefixed_payload( |
2077 | 3.77k | &self, |
2078 | 3.77k | lookup_context: &PayloadLookupContext, |
2079 | 3.77k | name: &str, |
2080 | 3.77k | prefix: &str, |
2081 | 3.77k | ) -> Option<PayloadInfo> { |
2082 | | // parse the `prefix` into `func_name` and `type_index`, bailing out |
2083 | | // with `None` if anything doesn't match. |
2084 | 3.77k | let (index_or_unit, func_name) = prefixed_intrinsic(name, prefix)?; |
2085 | 882 | let ty = match index_or_unit { |
2086 | 882 | "unit" => { |
2087 | 0 | if name.starts_with("[future") { |
2088 | 0 | PayloadType::UnitFuture |
2089 | 0 | } else if name.starts_with("[stream") { |
2090 | 0 | PayloadType::UnitStream |
2091 | | } else { |
2092 | 0 | unreachable!() |
2093 | | } |
2094 | | } |
2095 | 882 | other => { |
2096 | | // Note that this is parsed as a `u32` to ensure that the |
2097 | | // integer parsing is the same across platforms regardless of |
2098 | | // the the width of `usize`. |
2099 | 882 | let type_index = other.parse::<u32>().ok()? as usize; |
2100 | | |
2101 | | // Double-check that `func_name` is indeed a function name within |
2102 | | // this interface/world. Then additionally double-check that |
2103 | | // `type_index` is indeed a valid index for this function's type |
2104 | | // signature. |
2105 | 882 | let function = get_function( |
2106 | 882 | lookup_context.resolve, |
2107 | 882 | lookup_context.world, |
2108 | 882 | func_name, |
2109 | 882 | lookup_context.id, |
2110 | 882 | lookup_context.import, |
2111 | | ) |
2112 | 882 | .ok()?; |
2113 | | PayloadType::Type { |
2114 | 882 | id: *function |
2115 | 882 | .find_futures_and_streams(lookup_context.resolve) |
2116 | 882 | .get(type_index)?, |
2117 | 882 | function: function.name.clone(), |
2118 | | } |
2119 | | } |
2120 | | }; |
2121 | | |
2122 | | // And if all that passes wrap up everything in a `PayloadInfo`. |
2123 | | Some(PayloadInfo { |
2124 | 882 | name: name.to_string(), |
2125 | 882 | ty, |
2126 | 882 | key: lookup_context |
2127 | 882 | .key |
2128 | 882 | .clone() |
2129 | 882 | .unwrap_or_else(|| WorldKey::Name(name.to_string())), |
2130 | 882 | interface: lookup_context.id, |
2131 | 882 | imported: lookup_context.import, |
2132 | | }) |
2133 | 3.77k | } |
2134 | | |
2135 | 2.40k | fn maybe_async_lowered_payload( |
2136 | 2.40k | &self, |
2137 | 2.40k | lookup_context: &PayloadLookupContext, |
2138 | 2.40k | name: &str, |
2139 | 2.40k | prefix: &str, |
2140 | 2.40k | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2141 | 2.40k | let (async_lowered, clean_name) = self.strip_async_lowered_prefix(name); |
2142 | 2.40k | let payload = self.prefixed_payload(lookup_context, clean_name, prefix)?; |
2143 | 504 | Some(MaybeAsyncLowered { |
2144 | 504 | inner: payload, |
2145 | 504 | async_lowered, |
2146 | 504 | }) |
2147 | 2.40k | } |
2148 | | |
2149 | 3.99k | fn strip_async_lowered_prefix<'a>(&self, name: &'a str) -> (bool, &'a str) { |
2150 | 3.99k | name.strip_prefix("[async-lower]") |
2151 | 3.99k | .map_or((false, name), |s| (true, s)) |
2152 | 3.99k | } |
2153 | 853 | fn match_with_async_lowered_prefix( |
2154 | 853 | &self, |
2155 | 853 | name: &str, |
2156 | 853 | expected: &str, |
2157 | 853 | ) -> Option<MaybeAsyncLowered<()>> { |
2158 | 853 | let (async_lowered, clean_name) = self.strip_async_lowered_prefix(name); |
2159 | 853 | if clean_name == expected { |
2160 | 166 | Some(MaybeAsyncLowered { |
2161 | 166 | inner: (), |
2162 | 166 | async_lowered, |
2163 | 166 | }) |
2164 | | } else { |
2165 | 687 | None |
2166 | | } |
2167 | 853 | } |
2168 | 4.66k | fn strip_cancellable_prefix<'a>(&self, name: &'a str) -> (bool, &'a str) { |
2169 | 4.66k | name.strip_prefix("[cancellable]") |
2170 | 4.66k | .map_or((false, name), |s| (true, s)) |
2171 | 4.66k | } |
2172 | 1.46k | fn match_with_cancellable_prefix( |
2173 | 1.46k | &self, |
2174 | 1.46k | name: &str, |
2175 | 1.46k | expected: &str, |
2176 | 1.46k | ) -> Option<MaybeCancellable<()>> { |
2177 | 1.46k | let (cancellable, clean_name) = self.strip_cancellable_prefix(name); |
2178 | 1.46k | if clean_name == expected { |
2179 | 166 | Some(MaybeCancellable { |
2180 | 166 | inner: (), |
2181 | 166 | cancellable, |
2182 | 166 | }) |
2183 | | } else { |
2184 | 1.30k | None |
2185 | | } |
2186 | 1.46k | } |
2187 | | |
2188 | | /// Matches a name with the given prefix and either no suffix (for backwards compat) or |
2189 | | /// "-i32" or "-i64". |
2190 | | /// Returns a `ValType` based on the suffix and defaults to `I32`. |
2191 | 3.20k | fn match_with_optional_type_suffix(name: &str, match_prefix: &str) -> Option<ValType> { |
2192 | 3.20k | let tail = name.strip_prefix(match_prefix)?.strip_suffix(']')?; |
2193 | 332 | if tail.is_empty() { |
2194 | 332 | Some(ValType::I32) |
2195 | | } else { |
2196 | 0 | match tail.strip_prefix('-')? { |
2197 | 0 | "i32" => Some(ValType::I32), |
2198 | 0 | "i64" => Some(ValType::I64), |
2199 | | // Other suffixes |
2200 | 0 | _ => None, |
2201 | | } |
2202 | | } |
2203 | 3.20k | } |
2204 | | } |
2205 | | |
2206 | | impl NameMangling for Legacy { |
2207 | 6.77k | fn import_root(&self) -> &str { |
2208 | 6.77k | "$root" |
2209 | 6.77k | } |
2210 | 2.38k | fn import_non_root_prefix(&self) -> &str { |
2211 | 2.38k | "" |
2212 | 2.38k | } |
2213 | 4.95k | fn import_exported_intrinsic_prefix(&self) -> &str { |
2214 | 4.95k | "[export]" |
2215 | 4.95k | } |
2216 | 531 | fn export_memory(&self) -> &str { |
2217 | 531 | "memory" |
2218 | 531 | } |
2219 | 4.72k | fn export_initialize(&self) -> &str { |
2220 | 4.72k | "_initialize" |
2221 | 4.72k | } |
2222 | 5.25k | fn export_realloc(&self) -> &str { |
2223 | 5.25k | "cabi_realloc" |
2224 | 5.25k | } |
2225 | 0 | fn export_indirect_function_table(&self) -> Option<&str> { |
2226 | 0 | Some("__indirect_function_table") |
2227 | 0 | } |
2228 | 4.19k | fn export_wasm_task_hook(&self) -> Option<&str> { |
2229 | 4.19k | Some(crate::linking::metadata::TASK_HOOK) |
2230 | 4.19k | } |
2231 | 2.10k | fn resource_drop_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
2232 | 2.10k | name.strip_prefix("[resource-drop]") |
2233 | 2.10k | } |
2234 | 1.91k | fn resource_new_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
2235 | 1.91k | name.strip_prefix("[resource-new]") |
2236 | 1.91k | } |
2237 | 1.81k | fn resource_rep_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
2238 | 1.81k | name.strip_prefix("[resource-rep]") |
2239 | 1.81k | } |
2240 | 1.70k | fn task_return_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
2241 | 1.70k | name.strip_prefix("[task-return]") |
2242 | 1.70k | } |
2243 | 1.03k | fn task_cancel(&self, name: &str) -> bool { |
2244 | 1.03k | name == "[task-cancel]" |
2245 | 1.03k | } |
2246 | 2.18k | fn backpressure_inc(&self, name: &str) -> bool { |
2247 | 2.18k | name == "[backpressure-inc]" |
2248 | 2.18k | } |
2249 | 2.01k | fn backpressure_dec(&self, name: &str) -> bool { |
2250 | 2.01k | name == "[backpressure-dec]" |
2251 | 2.01k | } |
2252 | 1.84k | fn waitable_set_new(&self, name: &str) -> bool { |
2253 | 1.84k | name == "[waitable-set-new]" |
2254 | 1.84k | } |
2255 | 1.68k | fn waitable_set_wait(&self, name: &str) -> Option<(MaybeCancellable<()>, ValType)> { |
2256 | 1.68k | let (cancellable, clean_name) = self.strip_cancellable_prefix(name); |
2257 | 1.68k | let mb_cancellable = MaybeCancellable { |
2258 | 1.68k | inner: (), |
2259 | 1.68k | cancellable, |
2260 | 1.68k | }; |
2261 | 1.68k | let result_ty = Legacy::match_with_optional_type_suffix(clean_name, "[waitable-set-wait")?; |
2262 | 166 | Some((mb_cancellable, result_ty)) |
2263 | 1.68k | } |
2264 | 1.51k | fn waitable_set_poll(&self, name: &str) -> Option<(MaybeCancellable<()>, ValType)> { |
2265 | 1.51k | let (cancellable, clean_name) = self.strip_cancellable_prefix(name); |
2266 | 1.51k | let mb_cancellable = MaybeCancellable { |
2267 | 1.51k | inner: (), |
2268 | 1.51k | cancellable, |
2269 | 1.51k | }; |
2270 | 1.51k | let result_ty = Legacy::match_with_optional_type_suffix(clean_name, "[waitable-set-poll")?; |
2271 | 166 | Some((mb_cancellable, result_ty)) |
2272 | 1.51k | } |
2273 | 1.35k | fn waitable_set_drop(&self, name: &str) -> bool { |
2274 | 1.35k | name == "[waitable-set-drop]" |
2275 | 1.35k | } |
2276 | 1.18k | fn waitable_join(&self, name: &str) -> bool { |
2277 | 1.18k | name == "[waitable-join]" |
2278 | 1.18k | } |
2279 | 1.01k | fn subtask_drop(&self, name: &str) -> bool { |
2280 | 1.01k | name == "[subtask-drop]" |
2281 | 1.01k | } |
2282 | 853 | fn subtask_cancel(&self, name: &str) -> Option<MaybeAsyncLowered<()>> { |
2283 | 853 | self.match_with_async_lowered_prefix(name, "[subtask-cancel]") |
2284 | 853 | } |
2285 | 760 | fn async_lift_callback_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
2286 | 760 | name.strip_prefix("[callback][async-lift]") |
2287 | 760 | } |
2288 | 4.19k | fn async_lift_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
2289 | 4.19k | name.strip_prefix("[async-lift]") |
2290 | 4.19k | } |
2291 | 3.53k | fn async_lift_stackful_name<'a>(&self, name: &'a str) -> Option<&'a str> { |
2292 | 3.53k | name.strip_prefix("[async-lift-stackful]") |
2293 | 3.53k | } |
2294 | 687 | fn error_context_new(&self, name: &str) -> Option<StringEncoding> { |
2295 | 687 | match name { |
2296 | 687 | "[error-context-new-utf8]" => Some(StringEncoding::UTF8), |
2297 | 687 | "[error-context-new-utf16]" => Some(StringEncoding::UTF16), |
2298 | 687 | "[error-context-new-latin1+utf16]" => Some(StringEncoding::CompactUTF16), |
2299 | 687 | _ => None, |
2300 | | } |
2301 | 687 | } |
2302 | 687 | fn error_context_debug_message(&self, name: &str) -> Option<StringEncoding> { |
2303 | 687 | match name { |
2304 | 687 | "[error-context-debug-message-utf8]" => Some(StringEncoding::UTF8), |
2305 | 687 | "[error-context-debug-message-utf16]" => Some(StringEncoding::UTF16), |
2306 | 687 | "[error-context-debug-message-latin1+utf16]" => Some(StringEncoding::CompactUTF16), |
2307 | 687 | _ => None, |
2308 | | } |
2309 | 687 | } |
2310 | 2.18k | fn error_context_drop(&self, name: &str) -> bool { |
2311 | 2.18k | name == "[error-context-drop]" |
2312 | 2.18k | } |
2313 | 687 | fn context_get(&self, name: &str) -> Option<(ValType, u32)> { |
2314 | 687 | parse_context_name(name, "[context-get-") |
2315 | 687 | } |
2316 | 521 | fn context_set(&self, name: &str) -> Option<(ValType, u32)> { |
2317 | 521 | parse_context_name(name, "[context-set-") |
2318 | 521 | } |
2319 | 355 | fn thread_index(&self, name: &str) -> bool { |
2320 | 355 | name == "[thread-index]" |
2321 | 355 | } |
2322 | 355 | fn thread_new_indirect(&self, name: &str) -> bool { |
2323 | | // For now, we'll fix the type of the start function and the table to extract it from |
2324 | 355 | name == "[thread-new-indirect-v0]" |
2325 | 355 | } |
2326 | 355 | fn thread_resume_later(&self, name: &str) -> bool { |
2327 | 355 | name == "[thread-resume-later]" |
2328 | 355 | } |
2329 | 355 | fn thread_suspend(&self, name: &str) -> Option<MaybeCancellable<()>> { |
2330 | 355 | self.match_with_cancellable_prefix(name, "[thread-suspend]") |
2331 | 355 | } |
2332 | 355 | fn thread_yield(&self, name: &str) -> Option<MaybeCancellable<()>> { |
2333 | 355 | self.match_with_cancellable_prefix(name, "[thread-yield]") |
2334 | 355 | } |
2335 | 189 | fn thread_suspend_then_resume(&self, name: &str) -> Option<MaybeCancellable<()>> { |
2336 | 189 | self.match_with_cancellable_prefix(name, "[thread-suspend-then-resume]") |
2337 | 189 | } |
2338 | 189 | fn thread_yield_then_resume(&self, name: &str) -> Option<MaybeCancellable<()>> { |
2339 | 189 | self.match_with_cancellable_prefix(name, "[thread-yield-then-resume]") |
2340 | 189 | } |
2341 | 189 | fn thread_suspend_then_promote(&self, name: &str) -> Option<MaybeCancellable<()>> { |
2342 | 189 | self.match_with_cancellable_prefix(name, "[thread-suspend-then-promote]") |
2343 | 189 | } |
2344 | 189 | fn thread_yield_then_promote(&self, name: &str) -> Option<MaybeCancellable<()>> { |
2345 | 189 | self.match_with_cancellable_prefix(name, "[thread-yield-then-promote]") |
2346 | 189 | } |
2347 | 882 | fn future_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option<PayloadInfo> { |
2348 | 882 | self.prefixed_payload(lookup_context, name, "[future-new-") |
2349 | 882 | } |
2350 | 761 | fn future_write( |
2351 | 761 | &self, |
2352 | 761 | lookup_context: &PayloadLookupContext, |
2353 | 761 | name: &str, |
2354 | 761 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2355 | 761 | self.maybe_async_lowered_payload(lookup_context, name, "[future-write-") |
2356 | 761 | } |
2357 | 640 | fn future_read( |
2358 | 640 | &self, |
2359 | 640 | lookup_context: &PayloadLookupContext, |
2360 | 640 | name: &str, |
2361 | 640 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2362 | 640 | self.maybe_async_lowered_payload(lookup_context, name, "[future-read-") |
2363 | 640 | } |
2364 | 519 | fn future_cancel_write( |
2365 | 519 | &self, |
2366 | 519 | lookup_context: &PayloadLookupContext, |
2367 | 519 | name: &str, |
2368 | 519 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2369 | 519 | self.maybe_async_lowered_payload(lookup_context, name, "[future-cancel-write-") |
2370 | 519 | } |
2371 | 398 | fn future_cancel_read( |
2372 | 398 | &self, |
2373 | 398 | lookup_context: &PayloadLookupContext, |
2374 | 398 | name: &str, |
2375 | 398 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2376 | 398 | self.maybe_async_lowered_payload(lookup_context, name, "[future-cancel-read-") |
2377 | 398 | } |
2378 | 277 | fn future_drop_writable( |
2379 | 277 | &self, |
2380 | 277 | lookup_context: &PayloadLookupContext, |
2381 | 277 | name: &str, |
2382 | 277 | ) -> Option<PayloadInfo> { |
2383 | 277 | self.prefixed_payload(lookup_context, name, "[future-drop-writable-") |
2384 | 277 | } |
2385 | 156 | fn future_drop_readable( |
2386 | 156 | &self, |
2387 | 156 | lookup_context: &PayloadLookupContext, |
2388 | 156 | name: &str, |
2389 | 156 | ) -> Option<PayloadInfo> { |
2390 | 156 | self.prefixed_payload(lookup_context, name, "[future-drop-readable-") |
2391 | 156 | } |
2392 | 35 | fn stream_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option<PayloadInfo> { |
2393 | 35 | self.prefixed_payload(lookup_context, name, "[stream-new-") |
2394 | 35 | } |
2395 | 30 | fn stream_write( |
2396 | 30 | &self, |
2397 | 30 | lookup_context: &PayloadLookupContext, |
2398 | 30 | name: &str, |
2399 | 30 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2400 | 30 | self.maybe_async_lowered_payload(lookup_context, name, "[stream-write-") |
2401 | 30 | } |
2402 | 25 | fn stream_read( |
2403 | 25 | &self, |
2404 | 25 | lookup_context: &PayloadLookupContext, |
2405 | 25 | name: &str, |
2406 | 25 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2407 | 25 | self.maybe_async_lowered_payload(lookup_context, name, "[stream-read-") |
2408 | 25 | } |
2409 | 20 | fn stream_cancel_write( |
2410 | 20 | &self, |
2411 | 20 | lookup_context: &PayloadLookupContext, |
2412 | 20 | name: &str, |
2413 | 20 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2414 | 20 | self.maybe_async_lowered_payload(lookup_context, name, "[stream-cancel-write-") |
2415 | 20 | } |
2416 | 15 | fn stream_cancel_read( |
2417 | 15 | &self, |
2418 | 15 | lookup_context: &PayloadLookupContext, |
2419 | 15 | name: &str, |
2420 | 15 | ) -> Option<MaybeAsyncLowered<PayloadInfo>> { |
2421 | 15 | self.maybe_async_lowered_payload(lookup_context, name, "[stream-cancel-read-") |
2422 | 15 | } |
2423 | 10 | fn stream_drop_writable( |
2424 | 10 | &self, |
2425 | 10 | lookup_context: &PayloadLookupContext, |
2426 | 10 | name: &str, |
2427 | 10 | ) -> Option<PayloadInfo> { |
2428 | 10 | self.prefixed_payload(lookup_context, name, "[stream-drop-writable-") |
2429 | 10 | } |
2430 | 5 | fn stream_drop_readable( |
2431 | 5 | &self, |
2432 | 5 | lookup_context: &PayloadLookupContext, |
2433 | 5 | name: &str, |
2434 | 5 | ) -> Option<PayloadInfo> { |
2435 | 5 | self.prefixed_payload(lookup_context, name, "[stream-drop-readable-") |
2436 | 5 | } |
2437 | 2.38k | fn module_to_interface( |
2438 | 2.38k | &self, |
2439 | 2.38k | module: &str, |
2440 | 2.38k | resolve: &Resolve, |
2441 | 2.38k | items: &IndexMap<WorldKey, WorldItem>, |
2442 | 2.38k | ) -> Result<(WorldKey, InterfaceId)> { |
2443 | | // First see if this is a bare name |
2444 | 2.38k | let bare_name = WorldKey::Name(module.to_string()); |
2445 | 2.38k | if let Some(WorldItem::Interface { id, .. }) = items.get(&bare_name) { |
2446 | 2.01k | return Ok((bare_name, *id)); |
2447 | 372 | } |
2448 | | |
2449 | | // ... and if this isn't a bare name then it's time to do some parsing |
2450 | | // related to interfaces, versions, and such. First up the `module` name |
2451 | | // is parsed as a normal component name from `wasmparser` to see if it's |
2452 | | // of the "interface kind". If it's not then that means the above match |
2453 | | // should have been a hit but it wasn't, so an error is returned. |
2454 | 372 | let kebab_name = ComponentName::new(module, 0); |
2455 | 372 | let name = match kebab_name.as_ref().map(|k| k.kind()) { |
2456 | 372 | Ok(ComponentNameKind::Interface(name)) => name, |
2457 | 0 | _ => bail!("module requires an import interface named `{module}`"), |
2458 | | }; |
2459 | | |
2460 | | // FIXME: this prevents core wasm from importing from `@1` or |
2461 | | // `@0.1`, for example. More refactoring will be necessary to enable |
2462 | | // that. |
2463 | 372 | let version = name.version(None)?; |
2464 | | |
2465 | | // Prioritize an exact match based on versions, so try that first. |
2466 | 372 | let pkgname = PackageName { |
2467 | 372 | namespace: name.namespace().to_string(), |
2468 | 372 | name: name.package().to_string(), |
2469 | 372 | version: version.clone(), |
2470 | 372 | }; |
2471 | 372 | if let Some(pkg) = resolve.package_names.get(&pkgname) { |
2472 | 372 | if let Some(id) = resolve.packages[*pkg] |
2473 | 372 | .interfaces |
2474 | 372 | .get(name.interface().as_str()) |
2475 | | { |
2476 | | // If the interface from the package is directly in `items` then |
2477 | | // return that. |
2478 | 372 | let key = WorldKey::Interface(*id); |
2479 | 372 | if items.contains_key(&key) { |
2480 | 363 | return Ok((key, *id)); |
2481 | 9 | } |
2482 | | |
2483 | | // .. otherwise see if any interface in `items` is a clone of |
2484 | | // the package's interface. This means it's created by |
2485 | | // `generate_nominal_type_ids` and is used to match up exports |
2486 | | // to their nominal clone since the original is no longer |
2487 | | // exported. |
2488 | 9 | for k in items.keys() { |
2489 | 9 | let i = match *k { |
2490 | 9 | WorldKey::Interface(id) => id, |
2491 | 0 | WorldKey::Name(_) => continue, |
2492 | | }; |
2493 | 9 | if resolve.interfaces[i].clone_of == Some(*id) { |
2494 | 9 | return Ok((WorldKey::Interface(i), i)); |
2495 | 0 | } |
2496 | | } |
2497 | 0 | } |
2498 | 0 | } |
2499 | | |
2500 | | // If an exact match wasn't found then instead search for the first |
2501 | | // match based on versions. This means that a core wasm import for |
2502 | | // "1.2.3" might end up matching an interface at "1.2.4", for example. |
2503 | | // (or "1.2.2", depending on what's available). |
2504 | 0 | for (key, _) in items { |
2505 | 0 | let id = match key { |
2506 | 0 | WorldKey::Interface(id) => *id, |
2507 | 0 | WorldKey::Name(_) => continue, |
2508 | | }; |
2509 | | // Make sure the interface names match |
2510 | 0 | let interface = &resolve.interfaces[id]; |
2511 | 0 | if interface.name.as_ref().unwrap() != name.interface().as_str() { |
2512 | 0 | continue; |
2513 | 0 | } |
2514 | | |
2515 | | // Make sure the package name (without version) matches |
2516 | 0 | let pkg = &resolve.packages[interface.package.unwrap()]; |
2517 | 0 | if pkg.name.namespace != pkgname.namespace || pkg.name.name != pkgname.name { |
2518 | 0 | continue; |
2519 | 0 | } |
2520 | | |
2521 | 0 | let module_version = match &version { |
2522 | 0 | Some(version) => version, |
2523 | 0 | None => continue, |
2524 | | }; |
2525 | 0 | let pkg_version = match &pkg.name.version { |
2526 | 0 | Some(version) => version, |
2527 | 0 | None => continue, |
2528 | | }; |
2529 | | |
2530 | | // Test if the two semver versions are compatible |
2531 | 0 | let module_compat = PackageName::version_compat_track(&module_version); |
2532 | 0 | let pkg_compat = PackageName::version_compat_track(pkg_version); |
2533 | 0 | if module_compat == pkg_compat { |
2534 | 0 | return Ok((key.clone(), id)); |
2535 | 0 | } |
2536 | | } |
2537 | | |
2538 | 0 | bail!("module requires an import interface named `{module}`") |
2539 | 2.38k | } |
2540 | 2.14k | fn strip_post_return<'a>(&self, name: &'a str) -> Option<&'a str> { |
2541 | 2.14k | name.strip_prefix("cabi_post_") |
2542 | 2.14k | } |
2543 | 6.23k | fn match_wit_export<'a>( |
2544 | 6.23k | &self, |
2545 | 6.23k | export_name: &str, |
2546 | 6.23k | resolve: &'a Resolve, |
2547 | 6.23k | world: WorldId, |
2548 | 6.23k | exports: &'a IndexSet<WorldKey>, |
2549 | 6.23k | ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)> { |
2550 | 6.23k | let world = &resolve.worlds[world]; |
2551 | 23.8k | for name in exports { |
2552 | 23.8k | match &world.exports[name] { |
2553 | 1.73k | WorldItem::Function(f) => { |
2554 | 1.73k | if f.legacy_core_export_name(None) == export_name { |
2555 | 120 | return Some((name, None, f)); |
2556 | 1.61k | } |
2557 | | } |
2558 | 22.1k | WorldItem::Interface { id, .. } => { |
2559 | 22.1k | let string = resolve.name_world_key(name); |
2560 | 234k | for (_, func) in resolve.interfaces[*id].functions.iter() { |
2561 | 234k | if func.legacy_core_export_name(Some(&string)) == export_name { |
2562 | 3.97k | return Some((name, Some(*id), func)); |
2563 | 230k | } |
2564 | | } |
2565 | | } |
2566 | | |
2567 | 0 | WorldItem::Type { .. } => unreachable!(), |
2568 | | } |
2569 | | } |
2570 | | |
2571 | 2.14k | None |
2572 | 6.23k | } |
2573 | | |
2574 | 104 | fn match_wit_resource_dtor<'a>( |
2575 | 104 | &self, |
2576 | 104 | export_name: &str, |
2577 | 104 | resolve: &'a Resolve, |
2578 | 104 | world: WorldId, |
2579 | 104 | exports: &'a IndexSet<WorldKey>, |
2580 | 104 | ) -> Option<TypeId> { |
2581 | 104 | let world = &resolve.worlds[world]; |
2582 | 391 | for name in exports { |
2583 | 391 | let id = match &world.exports[name] { |
2584 | 356 | WorldItem::Interface { id, .. } => *id, |
2585 | 35 | WorldItem::Function(_) => continue, |
2586 | 0 | WorldItem::Type { .. } => unreachable!(), |
2587 | | }; |
2588 | 356 | let name = resolve.name_world_key(name); |
2589 | 356 | let resource = match export_name |
2590 | 356 | .strip_prefix(&name) |
2591 | 356 | .and_then(|s| s.strip_prefix("#[dtor]")) |
2592 | 356 | .and_then(|r| resolve.interfaces[id].types.get(r)) |
2593 | | { |
2594 | 104 | Some(id) => *id, |
2595 | 252 | None => continue, |
2596 | | }; |
2597 | | |
2598 | 104 | match resolve.types[resource].kind { |
2599 | 104 | TypeDefKind::Resource => {} |
2600 | 0 | _ => continue, |
2601 | | } |
2602 | | |
2603 | 104 | return Some(resource); |
2604 | | } |
2605 | | |
2606 | 0 | None |
2607 | 104 | } |
2608 | | |
2609 | 189 | fn world_key_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant) { |
2610 | 189 | let (async_abi, name) = self.strip_async_lowered_prefix(name); |
2611 | | ( |
2612 | 189 | name, |
2613 | 189 | if async_abi { |
2614 | 13 | AbiVariant::GuestImportAsync |
2615 | | } else { |
2616 | 176 | AbiVariant::GuestImport |
2617 | | }, |
2618 | | ) |
2619 | 189 | } |
2620 | 548 | fn interface_function_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant) { |
2621 | 548 | let (async_abi, name) = self.strip_async_lowered_prefix(name); |
2622 | | ( |
2623 | 548 | name, |
2624 | 548 | if async_abi { |
2625 | 48 | AbiVariant::GuestImportAsync |
2626 | | } else { |
2627 | 500 | AbiVariant::GuestImport |
2628 | | }, |
2629 | | ) |
2630 | 548 | } |
2631 | 0 | fn env_import(&self, name: &str, ty: &FuncType) -> Option<Import> { |
2632 | 0 | match name { |
2633 | 0 | "__wasm_get_stack_pointer" => { |
2634 | 0 | let ty = *ty.results().get(0)?; |
2635 | 0 | Some(Import::ContextGet { ty, slot: 0 }) |
2636 | | } |
2637 | 0 | "__wasm_set_stack_pointer" => { |
2638 | 0 | let ty = *ty.params().get(0)?; |
2639 | 0 | Some(Import::ContextSet { ty, slot: 0 }) |
2640 | | } |
2641 | | // TLS handling is slightly different than above to handle |
2642 | | // coop-threading-vs-not, so the exact resolution of this import is |
2643 | | // deferred to later. |
2644 | 0 | "__wasm_get_tls_base" => { |
2645 | 0 | let ty = *ty.results().get(0)?; |
2646 | 0 | Some(Import::TlsBaseGet { ty }) |
2647 | | } |
2648 | 0 | "__wasm_set_tls_base" => { |
2649 | 0 | let ty = *ty.params().get(0)?; |
2650 | 0 | Some(Import::TlsBaseSet { ty }) |
2651 | | } |
2652 | 0 | _ => None, |
2653 | | } |
2654 | 0 | } |
2655 | | } |
2656 | | |
2657 | | /// This function validates the following: |
2658 | | /// |
2659 | | /// * The `bytes` represent a valid core WebAssembly module. |
2660 | | /// * The module's imports are all satisfied by the given `imports` interfaces |
2661 | | /// or the `adapters` set. |
2662 | | /// * The given default and exported interfaces are satisfied by the module's |
2663 | | /// exports. |
2664 | | /// |
2665 | | /// The `ValidatedModule` return value contains the metadata which describes the |
2666 | | /// input module on success. This is then further used to generate a component |
2667 | | /// for this module. |
2668 | 593 | pub fn validate_module( |
2669 | 593 | encoder: &ComponentEncoder, |
2670 | 593 | bytes: &[u8], |
2671 | 593 | import_map: Option<&ModuleImportMap>, |
2672 | 593 | ) -> Result<ValidatedModule> { |
2673 | 593 | ValidatedModule::new( |
2674 | 593 | encoder, |
2675 | 593 | bytes, |
2676 | 593 | &encoder.main_module_exports, |
2677 | 593 | import_map, |
2678 | 593 | None, |
2679 | | ) |
2680 | 593 | } |
2681 | | |
2682 | | /// This function will validate the `bytes` provided as a wasm adapter module. |
2683 | | /// Notably this will validate the wasm module itself in addition to ensuring |
2684 | | /// that it has the "shape" of an adapter module. Current constraints are: |
2685 | | /// |
2686 | | /// * The adapter module can import only one memory |
2687 | | /// * The adapter module can only import from the name of `interface` specified, |
2688 | | /// and all function imports must match the `required` types which correspond |
2689 | | /// to the lowered types of the functions in `interface`. |
2690 | | /// |
2691 | | /// The wasm module passed into this function is the output of the GC pass of an |
2692 | | /// adapter module's original source. This means that the adapter module is |
2693 | | /// already minimized and this is a double-check that the minimization pass |
2694 | | /// didn't accidentally break the wasm module. |
2695 | | /// |
2696 | | /// If `is_library` is true, we waive some of the constraints described above, |
2697 | | /// allowing the module to import tables and globals, as well as import |
2698 | | /// functions at the world level, not just at the interface level. |
2699 | 0 | pub fn validate_adapter_module( |
2700 | 0 | encoder: &ComponentEncoder, |
2701 | 0 | bytes: &[u8], |
2702 | 0 | required_by_import: &IndexMap<String, FuncType>, |
2703 | 0 | exports: &IndexSet<WorldKey>, |
2704 | 0 | library_info: Option<&LibraryInfo>, |
2705 | 0 | ) -> Result<ValidatedModule> { |
2706 | 0 | let ret = ValidatedModule::new(encoder, bytes, exports, None, library_info)?; |
2707 | | |
2708 | 0 | for (name, required_ty) in required_by_import { |
2709 | 0 | let actual = match ret.exports.raw_exports.get(name) { |
2710 | 0 | Some(ty) => ty, |
2711 | 0 | None => return Err(AdapterModuleDidNotExport(name.clone()).into()), |
2712 | | }; |
2713 | 0 | validate_func_sig(name, required_ty, &actual)?; |
2714 | | } |
2715 | | |
2716 | 0 | Ok(ret) |
2717 | 0 | } |
2718 | | |
2719 | | /// An error that can be returned from adapting a core Wasm module into a |
2720 | | /// component using an adapter module. |
2721 | | /// |
2722 | | /// If the core Wasm module contained an import that it requires to be |
2723 | | /// satisfied by the adapter, and the adapter does not contain an export |
2724 | | /// with the same name, an instance of this error is returned. |
2725 | | #[derive(Debug, Clone)] |
2726 | | pub struct AdapterModuleDidNotExport(String); |
2727 | | |
2728 | | impl fmt::Display for AdapterModuleDidNotExport { |
2729 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
2730 | 0 | write!(f, "adapter module did not export `{}`", self.0) |
2731 | 0 | } |
2732 | | } |
2733 | | |
2734 | | impl std::error::Error for AdapterModuleDidNotExport {} |
2735 | | |
2736 | 361 | fn resource_test_for_interface<'a>( |
2737 | 361 | resolve: &'a Resolve, |
2738 | 361 | id: InterfaceId, |
2739 | 361 | ) -> impl Fn(&str) -> Option<TypeId> + 'a { |
2740 | 361 | let interface = &resolve.interfaces[id]; |
2741 | 361 | move |name: &str| { |
2742 | 361 | let ty = match interface.types.get(name) { |
2743 | 361 | Some(ty) => *ty, |
2744 | 0 | None => return None, |
2745 | | }; |
2746 | 361 | if matches!(resolve.types[ty].kind, TypeDefKind::Resource) { |
2747 | 361 | Some(ty) |
2748 | | } else { |
2749 | 0 | None |
2750 | | } |
2751 | 361 | } |
2752 | 361 | } |
2753 | | |
2754 | 28 | fn resource_test_for_world<'a>( |
2755 | 28 | resolve: &'a Resolve, |
2756 | 28 | id: WorldId, |
2757 | 28 | ) -> impl Fn(&str) -> Option<TypeId> + 'a { |
2758 | 28 | let world = &resolve.worlds[id]; |
2759 | 28 | move |name: &str| match world.imports.get(&WorldKey::Name(name.to_string()))? { |
2760 | 28 | WorldItem::Type { id, .. } => { |
2761 | 28 | if matches!(resolve.types[*id].kind, TypeDefKind::Resource) { |
2762 | 28 | Some(*id) |
2763 | | } else { |
2764 | 0 | None |
2765 | | } |
2766 | | } |
2767 | 0 | _ => None, |
2768 | 28 | } |
2769 | 28 | } |
2770 | | |
2771 | 2.80k | fn validate_func( |
2772 | 2.80k | resolve: &Resolve, |
2773 | 2.80k | ty: &wasmparser::FuncType, |
2774 | 2.80k | func: &Function, |
2775 | 2.80k | abi: AbiVariant, |
2776 | 2.80k | ) -> Result<()> { |
2777 | 2.80k | validate_func_sig( |
2778 | 2.80k | &func.name, |
2779 | 2.80k | &wasm_sig_to_func_type(resolve.wasm_signature(abi, func)), |
2780 | 2.80k | ty, |
2781 | | ) |
2782 | 2.80k | } |
2783 | | |
2784 | 1.44k | fn validate_post_return( |
2785 | 1.44k | resolve: &Resolve, |
2786 | 1.44k | ty: &wasmparser::FuncType, |
2787 | 1.44k | func: &Function, |
2788 | 1.44k | ) -> Result<()> { |
2789 | | // The expected signature of a post-return function is to take all the |
2790 | | // parameters that are returned by the guest function and then return no |
2791 | | // results. Model this by calculating the signature of `func` and then |
2792 | | // moving its results into the parameters list while emptying out the |
2793 | | // results. |
2794 | 1.44k | let mut sig = resolve.wasm_signature(AbiVariant::GuestExport, func); |
2795 | 1.44k | sig.params = mem::take(&mut sig.results); |
2796 | 1.44k | validate_func_sig( |
2797 | 1.44k | &format!("{} post-return", func.name), |
2798 | 1.44k | &wasm_sig_to_func_type(sig), |
2799 | 1.44k | ty, |
2800 | | ) |
2801 | 1.44k | } |
2802 | | |
2803 | 9.62k | fn validate_func_sig(name: &str, expected: &FuncType, ty: &wasmparser::FuncType) -> Result<()> { |
2804 | 9.62k | if ty != expected { |
2805 | 0 | bail!( |
2806 | | "type mismatch for function `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`", |
2807 | | name, |
2808 | 0 | expected.params(), |
2809 | 0 | expected.results(), |
2810 | 0 | ty.params(), |
2811 | 0 | ty.results() |
2812 | | ); |
2813 | 9.62k | } |
2814 | | |
2815 | 9.62k | Ok(()) |
2816 | 9.62k | } |
2817 | | |
2818 | | /// Matches `name` as `[${prefix}S]...`, and if found returns `("S", "...")` |
2819 | 4.98k | fn prefixed_intrinsic<'a>(name: &'a str, prefix: &str) -> Option<(&'a str, &'a str)> { |
2820 | 4.98k | assert!(prefix.starts_with("[")); |
2821 | 4.98k | assert!(prefix.ends_with("-")); |
2822 | 4.98k | let suffix = name.strip_prefix(prefix)?; |
2823 | 1.21k | let index = suffix.find(']')?; |
2824 | 1.21k | let rest = &suffix[index + 1..]; |
2825 | 1.21k | Some((&suffix[..index], rest)) |
2826 | 4.98k | } |
2827 | | |
2828 | | /// Parses a `[context-get-<N>]` / `[context-set-<N>]` style name, optionally |
2829 | | /// carrying a type width infix: `[context-get-i64-<N>]`. |
2830 | | /// |
2831 | | /// Returns the value type together with the numeric slot. Additional type |
2832 | | /// widths can be added here by extending the match below. |
2833 | 1.20k | fn parse_context_name(name: &str, prefix: &str) -> Option<(ValType, u32)> { |
2834 | 1.20k | let (suffix, rest) = prefixed_intrinsic(name, prefix)?; |
2835 | 332 | if !rest.is_empty() { |
2836 | 0 | return None; |
2837 | 332 | } |
2838 | 332 | let (ty, slot) = match suffix.split_once('-') { |
2839 | 0 | Some(("i64", slot)) => (ValType::I64, slot), |
2840 | 0 | Some(("i32", slot)) => (ValType::I32, slot), |
2841 | 332 | _ => (ValType::I32, suffix), |
2842 | | }; |
2843 | 332 | let slot = slot.parse().ok()?; |
2844 | 332 | Some((ty, slot)) |
2845 | 1.20k | } |
2846 | | |
2847 | 1.55k | fn get_function<'a>( |
2848 | 1.55k | resolve: &'a Resolve, |
2849 | 1.55k | world: &'a World, |
2850 | 1.55k | name: &str, |
2851 | 1.55k | interface: Option<InterfaceId>, |
2852 | 1.55k | imported: bool, |
2853 | 1.55k | ) -> Result<&'a Function> { |
2854 | 1.55k | let function = if let Some(id) = interface { |
2855 | 1.52k | return resolve.interfaces[id] |
2856 | 1.52k | .functions |
2857 | 1.52k | .get(name) |
2858 | 1.52k | .ok_or_else(|| anyhow!("no export `{name}` found")); |
2859 | 35 | } else if imported { |
2860 | 14 | world.imports.get(&WorldKey::Name(name.to_string())) |
2861 | | } else { |
2862 | 21 | world.exports.get(&WorldKey::Name(name.to_string())) |
2863 | | }; |
2864 | 35 | let Some(WorldItem::Function(function)) = function else { |
2865 | 0 | bail!("no export `{name}` found"); |
2866 | | }; |
2867 | 35 | Ok(function) |
2868 | 1.55k | } |