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