/src/wasm-tools/crates/wasmparser/src/validator/component.rs
Line | Count | Source |
1 | | //! State relating to validating a WebAssembly component. |
2 | | |
3 | | use super::{ |
4 | | check_max, |
5 | | component_types::{ |
6 | | Abi, AliasableResourceId, ComponentAnyTypeId, ComponentCoreInstanceTypeId, |
7 | | ComponentCoreModuleTypeId, ComponentCoreTypeId, ComponentDefinedType, |
8 | | ComponentDefinedTypeId, ComponentEntityType, ComponentFuncType, ComponentFuncTypeId, |
9 | | ComponentInstanceType, ComponentInstanceTypeId, ComponentItem, ComponentType, |
10 | | ComponentTypeId, ComponentValType, Context, CoreInstanceTypeKind, InstanceType, ModuleType, |
11 | | RecordType, Remap, Remapping, ResourceId, SubtypeCx, TupleType, VariantCase, VariantType, |
12 | | }, |
13 | | core::{InternRecGroup, Module}, |
14 | | types::{CoreTypeId, EntityType, TypeAlloc, TypeData, TypeInfo, TypeList}, |
15 | | }; |
16 | | use crate::collections::index_map::Entry; |
17 | | use crate::limits::*; |
18 | | use crate::prelude::*; |
19 | | use crate::validator::names::{ComponentName, ComponentNameKind, KebabStr, KebabString}; |
20 | | use crate::{ |
21 | | CanonicalFunction, CanonicalOption, ComponentExternName, ComponentExternalKind, |
22 | | ComponentOuterAliasKind, ComponentTypeRef, CompositeInnerType, Error, ExternalKind, FuncType, |
23 | | GlobalType, InstantiationArgKind, MemoryType, PackedIndex, RefType, Result, SubType, TableType, |
24 | | TypeBounds, ValType, WasmFeatures, require_feature, |
25 | | }; |
26 | | use core::mem; |
27 | | |
28 | 797k | fn to_kebab_string<'a>(s: &'a str, desc: &str, offset: usize) -> Result<KebabString> { |
29 | 797k | match KebabString::new(s) { |
30 | 797k | Some(s) => Ok(s), |
31 | | None => { |
32 | 0 | if s.is_empty() { |
33 | 0 | bail!(offset, "{desc} name cannot be empty"); |
34 | 0 | } |
35 | | |
36 | 0 | bail!(offset, "{desc} name `{s}` is not in kebab case"); |
37 | | } |
38 | | } |
39 | 797k | } |
40 | | |
41 | | pub(crate) struct ComponentState { |
42 | | /// Whether this state is a concrete component, an instance type, or a |
43 | | /// component type. |
44 | | kind: ComponentKind, |
45 | | features: WasmFeatures, |
46 | | |
47 | | // Core index spaces |
48 | | pub core_types: Vec<ComponentCoreTypeId>, |
49 | | pub core_funcs: Vec<CoreTypeId>, |
50 | | pub core_tags: Vec<CoreTypeId>, |
51 | | pub core_modules: Vec<ComponentCoreModuleTypeId>, |
52 | | pub core_instances: Vec<ComponentCoreInstanceTypeId>, |
53 | | pub core_memories: Vec<MemoryType>, |
54 | | pub core_tables: Vec<TableType>, |
55 | | pub core_globals: Vec<GlobalType>, |
56 | | |
57 | | // Component index spaces |
58 | | pub types: Vec<ComponentAnyTypeId>, |
59 | | pub funcs: Vec<ComponentFuncTypeId>, |
60 | | pub values: Vec<(ComponentValType, bool)>, |
61 | | pub instances: Vec<ComponentInstanceTypeId>, |
62 | | pub components: Vec<ComponentTypeId>, |
63 | | |
64 | | pub imports: IndexMap<String, ComponentItem>, |
65 | | pub import_names: IndexSet<ComponentName>, |
66 | | pub exports: IndexMap<String, ComponentItem>, |
67 | | pub export_names: IndexSet<ComponentName>, |
68 | | |
69 | | has_start: bool, |
70 | | type_info: TypeInfo, |
71 | | |
72 | | /// A mapping of imported resources in this component. |
73 | | /// |
74 | | /// This mapping represents all "type variables" imported into the |
75 | | /// component, or resources. This could be resources imported directly as |
76 | | /// a top-level type import or additionally transitively through other |
77 | | /// imported instances. |
78 | | /// |
79 | | /// The mapping element here is a "path" which is a list of indexes into |
80 | | /// the import map that will be generated for this component. Each index |
81 | | /// is an index into an `IndexMap`, and each list is guaranteed to have at |
82 | | /// least one element. |
83 | | /// |
84 | | /// An example of this map is: |
85 | | /// |
86 | | /// ```wasm |
87 | | /// (component |
88 | | /// ;; [0] - the first import |
89 | | /// (import "r" (type (sub resource))) |
90 | | /// |
91 | | /// ;; [1] - the second import |
92 | | /// (import "r2" (type (sub resource))) |
93 | | /// |
94 | | /// (import "i" (instance |
95 | | /// ;; [2, 0] - the third import, and the first export the instance |
96 | | /// (export "r3" (type (sub resource))) |
97 | | /// ;; [2, 1] - the third import, and the second export the instance |
98 | | /// (export "r4" (type (sub resource))) |
99 | | /// )) |
100 | | /// |
101 | | /// ;; ... |
102 | | /// ) |
103 | | /// ``` |
104 | | /// |
105 | | /// The `Vec<usize>` here can be thought of as `Vec<String>` but a |
106 | | /// (hopefully) more efficient representation. |
107 | | /// |
108 | | /// Finally note that this map is listed as an "append only" map because all |
109 | | /// insertions into it should always succeed. Any insertion which overlaps |
110 | | /// with a previous entry indicates a bug in the validator which needs to be |
111 | | /// corrected via other means. |
112 | | // |
113 | | // TODO: make these `SkolemResourceId` and then go fix all the compile |
114 | | // errors, don't add skolem things into the type area |
115 | | imported_resources: IndexMapAppendOnly<ResourceId, Vec<usize>>, |
116 | | |
117 | | /// A mapping of "defined" resources in this component, or those which |
118 | | /// are defined within the instantiation of this component. |
119 | | /// |
120 | | /// Defined resources, as the name implies, can sort of be thought of as |
121 | | /// "these are defined within the component". Note though that the means by |
122 | | /// which a local definition can occur are not simply those defined in the |
123 | | /// component but also in its transitively instantiated components |
124 | | /// internally. This means that this set closes over many transitive |
125 | | /// internal items in addition to those defined immediately in the component |
126 | | /// itself. |
127 | | /// |
128 | | /// The `Option<ValType>` in this mapping is whether or not the underlying |
129 | | /// representation of the resource is known to this component. Immediately |
130 | | /// defined resources, for example, will have `Some(I32)` here. Resources |
131 | | /// that come from transitively defined components, for example, will have |
132 | | /// `None`. In the type context all entries here are `None`. |
133 | | /// |
134 | | /// Note that like `imported_resources` all insertions into this map are |
135 | | /// expected to succeed to it's declared as append-only. |
136 | | defined_resources: IndexMapAppendOnly<ResourceId, Option<ValType>>, |
137 | | |
138 | | /// A mapping of explicitly exported resources from this component in |
139 | | /// addition to the path that they're exported at. |
140 | | /// |
141 | | /// For more information on the path here see the documentation for |
142 | | /// `imported_resources`. Note that the indexes here index into the |
143 | | /// list of exports of this component. |
144 | | explicit_resources: IndexMap<ResourceId, Vec<usize>>, |
145 | | |
146 | | /// The set of types which are considered "exported" from this component. |
147 | | /// |
148 | | /// This is added to whenever a type export is found, or an instance export |
149 | | /// which itself contains a type export. This additionally includes all |
150 | | /// imported types since those are suitable for export as well. |
151 | | /// |
152 | | /// This set is consulted whenever an exported item is added since all |
153 | | /// referenced types must be members of this set. |
154 | | exported_types: Set<ComponentAnyTypeId>, |
155 | | |
156 | | /// Same as `exported_types`, but for imports. |
157 | | imported_types: Set<ComponentAnyTypeId>, |
158 | | |
159 | | /// The set of top-level resource exports and their names. |
160 | | /// |
161 | | /// This context is used to validate method names such as `[method]foo.bar` |
162 | | /// to ensure that `foo` is an exported resource and that the type mentioned |
163 | | /// in a function type is actually named `foo`. |
164 | | /// |
165 | | /// Note that imports/exports have disjoint contexts to ensure that they're |
166 | | /// validated correctly. Namely you can't retroactively attach methods to an |
167 | | /// import, for example. |
168 | | toplevel_exported_resources: ComponentNameContext, |
169 | | |
170 | | /// Same as `toplevel_exported_resources`, but for imports. |
171 | | toplevel_imported_resources: ComponentNameContext, |
172 | | |
173 | | /// The type that's been assigned to slots of `context.{get,set}` by this |
174 | | /// component. This is `None` until the first intrinsic is seen. |
175 | | context_type: Option<ValType>, |
176 | | } |
177 | | |
178 | | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
179 | | pub enum ComponentKind { |
180 | | Component, |
181 | | InstanceType, |
182 | | ComponentType, |
183 | | } |
184 | | |
185 | | /// Helper context used to track information about resource names for method |
186 | | /// name validation. |
187 | | #[derive(Default)] |
188 | | struct ComponentNameContext { |
189 | | /// A map from a resource type id to an index in the `all_resource_names` |
190 | | /// set for the name of that resource. |
191 | | resource_name_map: Map<AliasableResourceId, usize>, |
192 | | |
193 | | /// All known resource names in this context, used to validate static method |
194 | | /// names to by ensuring that static methods' resource names are somewhere |
195 | | /// in this set. |
196 | | all_resource_names: IndexSet<String>, |
197 | | } |
198 | | |
199 | | #[derive(Debug, Copy, Clone)] |
200 | | pub enum ExternKind { |
201 | | Import, |
202 | | Export, |
203 | | } |
204 | | |
205 | | impl ExternKind { |
206 | 0 | pub fn desc(&self) -> &'static str { |
207 | 0 | match self { |
208 | 0 | ExternKind::Import => "import", |
209 | 0 | ExternKind::Export => "export", |
210 | | } |
211 | 0 | } |
212 | | } |
213 | | |
214 | | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] |
215 | | pub(crate) enum StringEncoding { |
216 | | #[default] |
217 | | Utf8, |
218 | | Utf16, |
219 | | CompactUtf16, |
220 | | } |
221 | | |
222 | | impl core::fmt::Display for StringEncoding { |
223 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
224 | 0 | f.write_str(match self { |
225 | 0 | Self::Utf8 => "utf8", |
226 | 0 | Self::Utf16 => "utf16", |
227 | 0 | Self::CompactUtf16 => "latin1-utf16", |
228 | | }) |
229 | 0 | } |
230 | | } |
231 | | |
232 | | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] |
233 | | pub(crate) enum Concurrency { |
234 | | /// Synchronous. |
235 | | #[default] |
236 | | Sync, |
237 | | |
238 | | /// Asynchronous. |
239 | | Async { |
240 | | /// When present, this is the function index of the async callback. When |
241 | | /// omitted, we are either using stack-switching based asynchrony or are |
242 | | /// in an operation that does not support the `callback` option (like |
243 | | /// lowering). |
244 | | callback: Option<u32>, |
245 | | }, |
246 | | } |
247 | | |
248 | | impl Concurrency { |
249 | 24.9k | pub(crate) fn is_sync(&self) -> bool { |
250 | 24.9k | matches!(self, Self::Sync) |
251 | 24.9k | } |
252 | | |
253 | 21.8k | pub(crate) fn is_async(&self) -> bool { |
254 | 21.8k | !self.is_sync() |
255 | 21.8k | } |
256 | | } |
257 | | |
258 | | #[derive(Clone, Copy)] |
259 | | pub(crate) enum PtrSize { |
260 | | Ptr32, |
261 | | Ptr64, |
262 | | } |
263 | | |
264 | | impl PtrSize { |
265 | 5.49k | pub(crate) fn core_type(&self) -> ValType { |
266 | 5.49k | match self { |
267 | 5.49k | PtrSize::Ptr32 => ValType::I32, |
268 | 0 | PtrSize::Ptr64 => ValType::I64, |
269 | | } |
270 | 5.49k | } |
271 | | } |
272 | | |
273 | | #[derive(Clone, Copy)] |
274 | | pub(crate) struct CanonicalOptions { |
275 | | pub(crate) string_encoding: StringEncoding, |
276 | | pub(crate) memory: Option<(u32, PtrSize)>, |
277 | | pub(crate) realloc: Option<u32>, |
278 | | pub(crate) post_return: Option<u32>, |
279 | | pub(crate) concurrency: Concurrency, |
280 | | pub(crate) core_type: Option<CoreTypeId>, |
281 | | pub(crate) gc: bool, |
282 | | } |
283 | | |
284 | | impl CanonicalOptions { |
285 | 2.82k | pub(crate) fn require_sync(&self, offset: usize, where_: &str) -> Result<&Self> { |
286 | 2.82k | if !self.concurrency.is_sync() { |
287 | 0 | bail!(offset, "cannot specify `async` option on `{where_}`") |
288 | 2.82k | } |
289 | 2.82k | Ok(self) |
290 | 2.82k | } |
291 | | |
292 | 1.63k | pub(crate) fn require_memory(&self, offset: usize) -> Result<&Self> { |
293 | 1.63k | if self.memory.is_none() { |
294 | 0 | bail!(offset, "canonical option `memory` is required"); |
295 | 1.63k | } |
296 | 1.63k | Ok(self) |
297 | 1.63k | } |
298 | | |
299 | 186 | pub(crate) fn require_realloc(&self, offset: usize) -> Result<&Self> { |
300 | | // Memory is always required when `realloc` is required. |
301 | 186 | self.require_memory(offset)?; |
302 | | |
303 | 186 | if self.realloc.is_none() { |
304 | 0 | bail!(offset, "canonical option `realloc` is required") |
305 | 186 | } |
306 | | |
307 | 186 | Ok(self) |
308 | 186 | } |
309 | | |
310 | 16.1k | pub(crate) fn require_memory_if( |
311 | 16.1k | &self, |
312 | 16.1k | offset: usize, |
313 | 16.1k | when: impl Fn() -> bool, |
314 | 16.1k | ) -> Result<&Self> { |
315 | 16.1k | if self.memory.is_none() && when() { |
316 | 0 | self.require_memory(offset)?; |
317 | 16.1k | } |
318 | 16.1k | Ok(self) |
319 | 16.1k | } <wasmparser::validator::component::CanonicalOptions>::require_memory_if::<<wasmparser::validator::component::ComponentState>::future_read::{closure#0}>Line | Count | Source | 310 | 69 | pub(crate) fn require_memory_if( | 311 | 69 | &self, | 312 | 69 | offset: usize, | 313 | 69 | when: impl Fn() -> bool, | 314 | 69 | ) -> Result<&Self> { | 315 | 69 | if self.memory.is_none() && when() { | 316 | 0 | self.require_memory(offset)?; | 317 | 69 | } | 318 | 69 | Ok(self) | 319 | 69 | } |
<wasmparser::validator::component::CanonicalOptions>::require_memory_if::<<wasmparser::validator::component::ComponentState>::stream_read::{closure#0}>Line | Count | Source | 310 | 60 | pub(crate) fn require_memory_if( | 311 | 60 | &self, | 312 | 60 | offset: usize, | 313 | 60 | when: impl Fn() -> bool, | 314 | 60 | ) -> Result<&Self> { | 315 | 60 | if self.memory.is_none() && when() { | 316 | 0 | self.require_memory(offset)?; | 317 | 60 | } | 318 | 60 | Ok(self) | 319 | 60 | } |
<wasmparser::validator::component::CanonicalOptions>::require_memory_if::<<wasmparser::validator::component::ComponentState>::future_write::{closure#0}>Line | Count | Source | 310 | 69 | pub(crate) fn require_memory_if( | 311 | 69 | &self, | 312 | 69 | offset: usize, | 313 | 69 | when: impl Fn() -> bool, | 314 | 69 | ) -> Result<&Self> { | 315 | 69 | if self.memory.is_none() && when() { | 316 | 0 | self.require_memory(offset)?; | 317 | 69 | } | 318 | 69 | Ok(self) | 319 | 69 | } |
<wasmparser::validator::component::CanonicalOptions>::require_memory_if::<<wasmparser::validator::component::ComponentState>::stream_write::{closure#0}>Line | Count | Source | 310 | 60 | pub(crate) fn require_memory_if( | 311 | 60 | &self, | 312 | 60 | offset: usize, | 313 | 60 | when: impl Fn() -> bool, | 314 | 60 | ) -> Result<&Self> { | 315 | 60 | if self.memory.is_none() && when() { | 316 | 0 | self.require_memory(offset)?; | 317 | 60 | } | 318 | 60 | Ok(self) | 319 | 60 | } |
<wasmparser::validator::component::CanonicalOptions>::require_memory_if::<<wasmparser::validator::component_types::ComponentFuncType>::lower::{closure#0}>Line | Count | Source | 310 | 13.3k | pub(crate) fn require_memory_if( | 311 | 13.3k | &self, | 312 | 13.3k | offset: usize, | 313 | 13.3k | when: impl Fn() -> bool, | 314 | 13.3k | ) -> Result<&Self> { | 315 | 13.3k | if self.memory.is_none() && when() { | 316 | 0 | self.require_memory(offset)?; | 317 | 13.3k | } | 318 | 13.3k | Ok(self) | 319 | 13.3k | } |
<wasmparser::validator::component::CanonicalOptions>::require_memory_if::<<wasmparser::validator::component_types::ComponentFuncType>::lower::{closure#3}>Line | Count | Source | 310 | 2.56k | pub(crate) fn require_memory_if( | 311 | 2.56k | &self, | 312 | 2.56k | offset: usize, | 313 | 2.56k | when: impl Fn() -> bool, | 314 | 2.56k | ) -> Result<&Self> { | 315 | 2.56k | if self.memory.is_none() && when() { | 316 | 0 | self.require_memory(offset)?; | 317 | 2.56k | } | 318 | 2.56k | Ok(self) | 319 | 2.56k | } |
|
320 | | |
321 | 29.6k | pub(crate) fn require_realloc_if( |
322 | 29.6k | &self, |
323 | 29.6k | offset: usize, |
324 | 29.6k | when: impl Fn() -> bool, |
325 | 29.6k | ) -> Result<&Self> { |
326 | 29.6k | if self.realloc.is_none() && when() { |
327 | 0 | self.require_realloc(offset)?; |
328 | 29.6k | } |
329 | 29.6k | Ok(self) |
330 | 29.6k | } <wasmparser::validator::component::CanonicalOptions>::require_realloc_if::<<wasmparser::validator::component::ComponentState>::future_read::{closure#1}>Line | Count | Source | 321 | 69 | pub(crate) fn require_realloc_if( | 322 | 69 | &self, | 323 | 69 | offset: usize, | 324 | 69 | when: impl Fn() -> bool, | 325 | 69 | ) -> Result<&Self> { | 326 | 69 | if self.realloc.is_none() && when() { | 327 | 0 | self.require_realloc(offset)?; | 328 | 69 | } | 329 | 69 | Ok(self) | 330 | 69 | } |
<wasmparser::validator::component::CanonicalOptions>::require_realloc_if::<<wasmparser::validator::component::ComponentState>::stream_read::{closure#1}>Line | Count | Source | 321 | 60 | pub(crate) fn require_realloc_if( | 322 | 60 | &self, | 323 | 60 | offset: usize, | 324 | 60 | when: impl Fn() -> bool, | 325 | 60 | ) -> Result<&Self> { | 326 | 60 | if self.realloc.is_none() && when() { | 327 | 0 | self.require_realloc(offset)?; | 328 | 60 | } | 329 | 60 | Ok(self) | 330 | 60 | } |
<wasmparser::validator::component::CanonicalOptions>::require_realloc_if::<<wasmparser::validator::component_types::ComponentFuncType>::lower::{closure#2}>Line | Count | Source | 321 | 12.1k | pub(crate) fn require_realloc_if( | 322 | 12.1k | &self, | 323 | 12.1k | offset: usize, | 324 | 12.1k | when: impl Fn() -> bool, | 325 | 12.1k | ) -> Result<&Self> { | 326 | 12.1k | if self.realloc.is_none() && when() { | 327 | 0 | self.require_realloc(offset)?; | 328 | 12.1k | } | 329 | 12.1k | Ok(self) | 330 | 12.1k | } |
<wasmparser::validator::component::CanonicalOptions>::require_realloc_if::<<wasmparser::validator::component_types::ComponentFuncType>::lower::{closure#1}>Line | Count | Source | 321 | 17.3k | pub(crate) fn require_realloc_if( | 322 | 17.3k | &self, | 323 | 17.3k | offset: usize, | 324 | 17.3k | when: impl Fn() -> bool, | 325 | 17.3k | ) -> Result<&Self> { | 326 | 17.3k | if self.realloc.is_none() && when() { | 327 | 0 | self.require_realloc(offset)?; | 328 | 17.3k | } | 329 | 17.3k | Ok(self) | 330 | 17.3k | } |
|
331 | | |
332 | 7.95k | pub(crate) fn check_lower(&self, offset: usize) -> Result<&Self> { |
333 | 7.95k | if self.post_return.is_some() { |
334 | 0 | bail!( |
335 | 0 | offset, |
336 | | "canonical option `post-return` cannot be specified for lowerings" |
337 | | ); |
338 | 7.95k | } |
339 | | |
340 | 459 | if let Concurrency::Async { callback: Some(_) } = self.concurrency { |
341 | 0 | bail!( |
342 | 0 | offset, |
343 | | "canonical option `callback` cannot be specified for lowerings" |
344 | | ); |
345 | 7.95k | } |
346 | | |
347 | 7.95k | if self.gc && self.core_type.is_none() { |
348 | 0 | bail!( |
349 | 0 | offset, |
350 | | "cannot specify `gc` without also specifying a `core-type` for lowerings" |
351 | | ) |
352 | 7.95k | } |
353 | | |
354 | 7.95k | Ok(self) |
355 | 7.95k | } |
356 | | |
357 | 9.12k | pub(crate) fn check_lift( |
358 | 9.12k | &mut self, |
359 | 9.12k | types: &TypeList, |
360 | 9.12k | state: &ComponentState, |
361 | 9.12k | core_ty_id: CoreTypeId, |
362 | 9.12k | offset: usize, |
363 | 9.12k | ) -> Result<&Self> { |
364 | 9.12k | debug_assert!(matches!( |
365 | 0 | types[core_ty_id].composite_type.inner, |
366 | | CompositeInnerType::Func(_) |
367 | | )); |
368 | | |
369 | 9.12k | if let Some(idx) = self.post_return { |
370 | 6.53k | let post_return_func_ty = types[state.core_function_at(idx, offset)?].unwrap_func(); |
371 | 6.53k | let core_ty = types[core_ty_id].unwrap_func(); |
372 | 6.53k | if post_return_func_ty.params() != core_ty.results() |
373 | 6.53k | || !post_return_func_ty.results().is_empty() |
374 | | { |
375 | 0 | bail!( |
376 | 0 | offset, |
377 | | "canonical option `post-return` uses a core function with an incorrect signature" |
378 | | ); |
379 | 6.53k | } |
380 | 2.59k | } |
381 | | |
382 | 2.59k | match self.concurrency { |
383 | 6.53k | Concurrency::Sync => {} |
384 | | |
385 | | Concurrency::Async { callback: None } => { |
386 | 66 | require_feature::cm_async_stackful( |
387 | 66 | state.features, |
388 | | "requires the component model async stackful feature", |
389 | 66 | offset, |
390 | 0 | )?; |
391 | | } |
392 | | |
393 | | Concurrency::Async { |
394 | 2.53k | callback: Some(idx), |
395 | | } => { |
396 | 2.53k | let func_ty = types[state.core_function_at(idx, offset)?].unwrap_func(); |
397 | 2.53k | if func_ty.params() != [ValType::I32; 3] && func_ty.params() != [ValType::I32] { |
398 | 0 | return Err(Error::new( |
399 | 0 | "canonical option `callback` uses a core function with an incorrect signature", |
400 | 0 | offset, |
401 | 0 | )); |
402 | 2.53k | } |
403 | | } |
404 | | } |
405 | | |
406 | 9.12k | if self.core_type.is_some() { |
407 | 0 | bail!( |
408 | 0 | offset, |
409 | | "canonical option `core-type` is not allowed in `canon lift`" |
410 | | ) |
411 | 9.12k | } |
412 | 9.12k | self.core_type = Some(core_ty_id); |
413 | | |
414 | 9.12k | Ok(self) |
415 | 9.12k | } |
416 | | |
417 | 14.0k | fn check_asyncness(&self, ty: &ComponentFuncType, offset: usize) -> Result<()> { |
418 | | // The `async` canonical ABI option is only allowed with `async`-typed |
419 | | // functions. |
420 | 14.0k | if self.concurrency.is_async() && !ty.async_ { |
421 | 0 | bail!( |
422 | 0 | offset, |
423 | | "the `async` canonical option requires an async function type", |
424 | | ); |
425 | 14.0k | } |
426 | 14.0k | Ok(()) |
427 | 14.0k | } |
428 | | |
429 | 258 | pub(crate) fn check_core_type( |
430 | 258 | &self, |
431 | 258 | types: &mut TypeAlloc, |
432 | 258 | actual: FuncType, |
433 | 258 | offset: usize, |
434 | 258 | ) -> Result<CoreTypeId> { |
435 | 258 | if let Some(declared_id) = self.core_type { |
436 | 0 | let declared = types[declared_id].unwrap_func(); |
437 | | |
438 | 0 | if actual.params() != declared.params() { |
439 | 0 | bail!( |
440 | 0 | offset, |
441 | | "declared core type has `{:?}` parameter types, but actual lowering has \ |
442 | | `{:?}` parameter types", |
443 | 0 | declared.params(), |
444 | 0 | actual.params(), |
445 | | ); |
446 | 0 | } |
447 | | |
448 | 0 | if actual.results() != declared.results() { |
449 | 0 | bail!( |
450 | 0 | offset, |
451 | | "declared core type has `{:?}` result types, but actual lowering has \ |
452 | | `{:?}` result types", |
453 | 0 | declared.results(), |
454 | 0 | actual.results(), |
455 | | ); |
456 | 0 | } |
457 | | |
458 | 0 | Ok(declared_id) |
459 | | } else { |
460 | 258 | Ok(types.intern_func_type(actual, offset)) |
461 | | } |
462 | 258 | } |
463 | | } |
464 | | |
465 | | impl ComponentState { |
466 | 139k | pub fn new(kind: ComponentKind, features: WasmFeatures) -> Self { |
467 | 139k | Self { |
468 | 139k | kind, |
469 | 139k | features, |
470 | 139k | core_types: Default::default(), |
471 | 139k | core_modules: Default::default(), |
472 | 139k | core_instances: Default::default(), |
473 | 139k | core_funcs: Default::default(), |
474 | 139k | core_memories: Default::default(), |
475 | 139k | core_tables: Default::default(), |
476 | 139k | core_globals: Default::default(), |
477 | 139k | core_tags: Default::default(), |
478 | 139k | types: Default::default(), |
479 | 139k | funcs: Default::default(), |
480 | 139k | values: Default::default(), |
481 | 139k | instances: Default::default(), |
482 | 139k | components: Default::default(), |
483 | 139k | imports: Default::default(), |
484 | 139k | exports: Default::default(), |
485 | 139k | import_names: Default::default(), |
486 | 139k | export_names: Default::default(), |
487 | 139k | has_start: Default::default(), |
488 | 139k | type_info: TypeInfo::new(), |
489 | 139k | imported_resources: Default::default(), |
490 | 139k | defined_resources: Default::default(), |
491 | 139k | explicit_resources: Default::default(), |
492 | 139k | exported_types: Default::default(), |
493 | 139k | imported_types: Default::default(), |
494 | 139k | toplevel_exported_resources: Default::default(), |
495 | 139k | toplevel_imported_resources: Default::default(), |
496 | 139k | context_type: None, |
497 | 139k | } |
498 | 139k | } |
499 | | |
500 | 328k | pub fn type_count(&self) -> usize { |
501 | 328k | self.core_types.len() + self.types.len() |
502 | 328k | } |
503 | | |
504 | 86.2k | pub fn instance_count(&self) -> usize { |
505 | 86.2k | self.core_instances.len() + self.instances.len() |
506 | 86.2k | } |
507 | | |
508 | 111k | pub fn function_count(&self) -> usize { |
509 | 111k | self.core_funcs.len() + self.funcs.len() |
510 | 111k | } |
511 | | |
512 | 0 | pub fn add_core_type( |
513 | 0 | components: &mut [Self], |
514 | 0 | ty: crate::CoreType, |
515 | 0 | types: &mut TypeAlloc, |
516 | 0 | offset: usize, |
517 | 0 | check_limit: bool, |
518 | 0 | ) -> Result<()> { |
519 | 0 | let current = components.last_mut().unwrap(); |
520 | 0 | if check_limit { |
521 | 0 | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; |
522 | 0 | } |
523 | 0 | match ty { |
524 | 0 | crate::CoreType::Rec(rec) => { |
525 | 0 | current.canonicalize_and_intern_rec_group(types, rec, offset)?; |
526 | | } |
527 | 0 | crate::CoreType::Module(decls) => { |
528 | 0 | let mod_ty = Self::create_module_type(components, decls.into_vec(), types, offset)?; |
529 | 0 | let id = ComponentCoreTypeId::Module(types.push_ty(mod_ty)); |
530 | 0 | components.last_mut().unwrap().core_types.push(id); |
531 | | } |
532 | | } |
533 | | |
534 | 0 | Ok(()) |
535 | 0 | } |
536 | | |
537 | 21.7k | pub fn add_core_module( |
538 | 21.7k | &mut self, |
539 | 21.7k | module: &Module, |
540 | 21.7k | types: &mut TypeAlloc, |
541 | 21.7k | offset: usize, |
542 | 21.7k | ) -> Result<()> { |
543 | 21.7k | let imports = module.imports_for_module_type(offset)?; |
544 | | |
545 | | // We have to clone the module's imports and exports here |
546 | | // because we cannot take the data out of the `MaybeOwned` |
547 | | // as it might be shared with a function validator. |
548 | 21.7k | let mod_ty = ModuleType { |
549 | 21.7k | info: TypeInfo::core(module.type_size), |
550 | 21.7k | imports, |
551 | 21.7k | exports: module.exports.clone(), |
552 | 21.7k | }; |
553 | | |
554 | 21.7k | let mod_id = types.push_ty(mod_ty); |
555 | 21.7k | self.core_modules.push(mod_id); |
556 | | |
557 | 21.7k | Ok(()) |
558 | 21.7k | } |
559 | | |
560 | 38.5k | pub fn add_core_instance( |
561 | 38.5k | &mut self, |
562 | 38.5k | instance: crate::Instance, |
563 | 38.5k | types: &mut TypeAlloc, |
564 | 38.5k | offset: usize, |
565 | 38.5k | ) -> Result<()> { |
566 | 38.5k | let instance = match instance { |
567 | 21.7k | crate::Instance::Instantiate { module_index, args } => { |
568 | 21.7k | self.instantiate_core_module(module_index, args.into_vec(), types, offset)? |
569 | | } |
570 | 16.7k | crate::Instance::FromExports(exports) => { |
571 | 16.7k | self.instantiate_core_exports(exports.into_vec(), types, offset)? |
572 | | } |
573 | | }; |
574 | | |
575 | 38.5k | self.core_instances.push(instance); |
576 | | |
577 | 38.5k | Ok(()) |
578 | 38.5k | } |
579 | | |
580 | 379k | pub fn add_type( |
581 | 379k | components: &mut Vec<Self>, |
582 | 379k | ty: crate::ComponentType, |
583 | 379k | types: &mut TypeAlloc, |
584 | 379k | offset: usize, |
585 | 379k | check_limit: bool, |
586 | 379k | ) -> Result<()> { |
587 | 379k | assert!(!components.is_empty()); |
588 | | |
589 | 644k | fn current(components: &mut Vec<ComponentState>) -> &mut ComponentState { |
590 | 644k | components.last_mut().unwrap() |
591 | 644k | } |
592 | | |
593 | 379k | let id = match ty { |
594 | 227k | crate::ComponentType::Defined(ty) => { |
595 | 227k | let ty = current(components).create_defined_type(ty, types, offset)?; |
596 | 227k | let depth = ty.type_info(types).depth(); |
597 | 227k | if depth > MAX_WASM_COMPONENT_TYPE_DEPTH { |
598 | 0 | bail!(offset, "type nesting is too deep"); |
599 | 227k | } |
600 | 227k | types.push(ty).into() |
601 | | } |
602 | 37.2k | crate::ComponentType::Func(ty) => { |
603 | 37.2k | let ty = current(components).create_function_type(ty, types, offset)?; |
604 | 37.2k | types.push(ty).into() |
605 | | } |
606 | 63.1k | crate::ComponentType::Component(decls) => { |
607 | 63.1k | let ty = Self::create_component_type(components, decls.into_vec(), types, offset)?; |
608 | 63.0k | types.push(ty).into() |
609 | | } |
610 | 51.3k | crate::ComponentType::Instance(decls) => { |
611 | 51.3k | let ty = Self::create_instance_type(components, decls.into_vec(), types, offset)?; |
612 | 51.3k | types.push(ty).into() |
613 | | } |
614 | 636 | crate::ComponentType::Resource { rep, dtor } => { |
615 | 636 | let component = current(components); |
616 | | |
617 | | // Resource types cannot be declared in a type context, only |
618 | | // within a component context. |
619 | 636 | if component.kind != ComponentKind::Component { |
620 | 0 | bail!( |
621 | 0 | offset, |
622 | | "resources can only be defined within a concrete component" |
623 | | ); |
624 | 636 | } |
625 | | |
626 | | // Current MVP restriction of the component model. |
627 | 636 | if rep == ValType::I64 { |
628 | 0 | require_feature::cm64( |
629 | 0 | component.features, |
630 | | "resources with `i64` require the `cm64` feature to be enabled", |
631 | 0 | offset, |
632 | 0 | )?; |
633 | 636 | } |
634 | 636 | if rep != ValType::I32 && rep != ValType::I64 { |
635 | 0 | bail!( |
636 | 0 | offset, |
637 | | "resources can only be represented by `i32` or `i64`" |
638 | | ); |
639 | 636 | } |
640 | | |
641 | | // If specified validate that the destructor is both a valid |
642 | | // function and has the correct signature. |
643 | 636 | if let Some(dtor) = dtor { |
644 | 636 | let ty = component.core_function_at(dtor, offset)?; |
645 | 636 | let ty = types[ty].composite_type.unwrap_func(); |
646 | 636 | if ty.params() != [rep] || ty.results() != [] { |
647 | 0 | bail!( |
648 | 0 | offset, |
649 | | "core function {dtor} has wrong signature for a destructor" |
650 | | ); |
651 | 636 | } |
652 | 0 | } |
653 | | |
654 | | // As this is the introduction of a resource create a fresh new |
655 | | // identifier for the resource. This is then added into the |
656 | | // list of defined resources for this component, notably with a |
657 | | // rep listed to enable getting access to various intrinsics |
658 | | // such as `resource.rep`. |
659 | 636 | let id = types.alloc_resource_id(); |
660 | 636 | component.defined_resources.insert(id.resource(), Some(rep)); |
661 | 636 | id.into() |
662 | | } |
663 | | }; |
664 | | |
665 | 379k | let current = current(components); |
666 | 379k | if check_limit { |
667 | 222k | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; |
668 | 156k | } |
669 | 379k | current.types.push(id); |
670 | | |
671 | 379k | Ok(()) |
672 | 379k | } |
673 | | |
674 | 33.8k | pub fn add_import( |
675 | 33.8k | &mut self, |
676 | 33.8k | import: crate::ComponentImport<'_>, |
677 | 33.8k | types: &mut TypeAlloc, |
678 | 33.8k | offset: usize, |
679 | 33.8k | ) -> Result<()> { |
680 | 33.8k | let mut entity = self.check_type_ref(&import.ty, types, offset)?; |
681 | 33.8k | self.add_entity( |
682 | 33.8k | &mut entity, |
683 | 33.8k | Some((import.name.name, ExternKind::Import)), |
684 | 33.8k | types, |
685 | 33.8k | offset, |
686 | 0 | )?; |
687 | 33.8k | self.toplevel_imported_resources.validate_extern( |
688 | 33.8k | &import.name, |
689 | 33.8k | ExternKind::Import, |
690 | 33.8k | &entity, |
691 | 33.8k | types, |
692 | 33.8k | offset, |
693 | 33.8k | &mut self.import_names, |
694 | 33.8k | &mut self.imports, |
695 | 33.8k | &mut self.type_info, |
696 | 33.8k | &self.features, |
697 | 0 | )?; |
698 | 33.8k | Ok(()) |
699 | 33.8k | } |
700 | | |
701 | 306k | fn add_entity( |
702 | 306k | &mut self, |
703 | 306k | ty: &mut ComponentEntityType, |
704 | 306k | name_and_kind: Option<(&str, ExternKind)>, |
705 | 306k | types: &mut TypeAlloc, |
706 | 306k | offset: usize, |
707 | 306k | ) -> Result<()> { |
708 | 306k | let kind = name_and_kind.map(|(_, k)| k); |
709 | 306k | let (len, max, desc) = match ty { |
710 | 0 | ComponentEntityType::Module(id) => { |
711 | 0 | self.core_modules.push(*id); |
712 | 0 | (self.core_modules.len(), MAX_WASM_MODULES, "modules") |
713 | | } |
714 | 13.6k | ComponentEntityType::Component(id) => { |
715 | 13.6k | self.components.push(*id); |
716 | 13.6k | (self.components.len(), MAX_WASM_COMPONENTS, "components") |
717 | | } |
718 | 56.5k | ComponentEntityType::Instance(id) => { |
719 | 56.5k | match kind { |
720 | 8.31k | Some(ExternKind::Import) => self.prepare_instance_import(id, types), |
721 | 48.2k | Some(ExternKind::Export) => self.prepare_instance_export(id, types), |
722 | 0 | None => {} |
723 | | } |
724 | 56.5k | self.instances.push(*id); |
725 | 56.5k | (self.instance_count(), MAX_WASM_INSTANCES, "instances") |
726 | | } |
727 | 56.0k | ComponentEntityType::Func(id) => { |
728 | 56.0k | self.funcs.push(*id); |
729 | 56.0k | (self.function_count(), MAX_WASM_FUNCTIONS, "functions") |
730 | | } |
731 | 0 | ComponentEntityType::Value(ty) => { |
732 | 0 | self.check_value_support(offset)?; |
733 | 0 | let value_used = match kind { |
734 | 0 | Some(ExternKind::Import) | None => false, |
735 | 0 | Some(ExternKind::Export) => true, |
736 | | }; |
737 | 0 | self.values.push((*ty, value_used)); |
738 | 0 | (self.values.len(), MAX_WASM_VALUES, "values") |
739 | | } |
740 | | ComponentEntityType::Type { |
741 | 180k | created, |
742 | 180k | referenced, |
743 | | } => { |
744 | 180k | self.types.push(*created); |
745 | | |
746 | | // Extra logic here for resources being imported and exported. |
747 | | // Note that if `created` is the same as `referenced` then this |
748 | | // is the original introduction of the resource which is where |
749 | | // `self.{imported,defined}_resources` are updated. |
750 | 180k | if let ComponentAnyTypeId::Resource(id) = *created { |
751 | 5.54k | match kind { |
752 | | Some(ExternKind::Import) => { |
753 | | // A fresh new resource is being imported into a |
754 | | // component. This arises from the import section of |
755 | | // a component or from the import declaration in a |
756 | | // component type. In both cases a new imported |
757 | | // resource is injected with a fresh new identifier |
758 | | // into our state. |
759 | 1.53k | if created == referenced { |
760 | 1.42k | self.imported_resources |
761 | 1.42k | .insert(id.resource(), vec![self.imports.len()]); |
762 | 1.42k | } |
763 | | } |
764 | | |
765 | | Some(ExternKind::Export) => { |
766 | | // A fresh resource is being exported from this |
767 | | // component. This arises as part of the |
768 | | // declaration of a component type, for example. In |
769 | | // this situation brand new resource identifier is |
770 | | // allocated and a definition is added, unlike the |
771 | | // import case where an imported resource is added. |
772 | | // Notably the representation of this new resource |
773 | | // is unknown so it's listed as `None`. |
774 | 4.01k | if created == referenced { |
775 | 2.69k | self.defined_resources.insert(id.resource(), None); |
776 | 2.69k | } |
777 | | |
778 | | // If this is a type export of a resource type then |
779 | | // update the `explicit_resources` list. A new |
780 | | // export path is about to be created for this |
781 | | // resource and this keeps track of that. |
782 | 4.01k | self.explicit_resources |
783 | 4.01k | .insert(id.resource(), vec![self.exports.len()]); |
784 | | } |
785 | | |
786 | 766 | None => {} |
787 | | } |
788 | 174k | } |
789 | 180k | (self.types.len(), MAX_WASM_TYPES, "types") |
790 | | } |
791 | | }; |
792 | | |
793 | 306k | check_max(len, 0, max, desc, offset)?; |
794 | | |
795 | | // Before returning perform the final validation of the type of the item |
796 | | // being imported/exported. This will ensure that everything is |
797 | | // appropriately named with respect to type definitions, resources, etc. |
798 | 306k | if let Some((name, kind)) = name_and_kind { |
799 | 295k | if !self.validate_and_register_named_types(Some(name), kind, ty, types) { |
800 | 0 | bail!( |
801 | 0 | offset, |
802 | | "{} not valid to be used as {}", |
803 | 0 | ty.desc(), |
804 | 0 | kind.desc() |
805 | | ); |
806 | 295k | } |
807 | 11.3k | } |
808 | 306k | Ok(()) |
809 | 306k | } |
810 | | |
811 | | /// Validates that the `ty` referenced only refers to named types internally |
812 | | /// and then inserts anything necessary, if applicable, to the defined sets |
813 | | /// within this component. |
814 | | /// |
815 | | /// This function will validate that `ty` only refers to named types. For |
816 | | /// example if it's a record then all of its fields must refer to named |
817 | | /// types. This consults either `self.imported_types` or |
818 | | /// `self.exported_types` as specified by `kind`. Note that this is not |
819 | | /// inherently recursive itself but it ends up being recursive since if |
820 | | /// recursive members were named then all their components must also be |
821 | | /// named. Consequently this check stops at the "one layer deep" position, |
822 | | /// or more accurately the position where types must be named (e.g. tuples |
823 | | /// aren't required to be named). |
824 | 444k | fn validate_and_register_named_types( |
825 | 444k | &mut self, |
826 | 444k | toplevel_name: Option<&str>, |
827 | 444k | kind: ExternKind, |
828 | 444k | ty: &ComponentEntityType, |
829 | 444k | types: &TypeAlloc, |
830 | 444k | ) -> bool { |
831 | 444k | if let ComponentEntityType::Type { created, .. } = ty { |
832 | | // If this is a top-level resource then register it in the |
833 | | // appropriate context so later validation of method-like-names |
834 | | // works out. |
835 | 283k | if let Some(name) = toplevel_name { |
836 | 172k | if let ComponentAnyTypeId::Resource(id) = *created { |
837 | 5.54k | let cx = match kind { |
838 | 1.53k | ExternKind::Import => &mut self.toplevel_imported_resources, |
839 | 4.01k | ExternKind::Export => &mut self.toplevel_exported_resources, |
840 | | }; |
841 | 5.54k | cx.register(name, id); |
842 | 166k | } |
843 | 110k | } |
844 | 161k | } |
845 | | |
846 | 444k | match self.kind { |
847 | 328k | ComponentKind::Component | ComponentKind::ComponentType => {} |
848 | 115k | ComponentKind::InstanceType => return true, |
849 | | } |
850 | 328k | let set = match kind { |
851 | 78.1k | ExternKind::Import => &self.imported_types, |
852 | 250k | ExternKind::Export => &self.exported_types, |
853 | | }; |
854 | 328k | match ty { |
855 | | // When a type is imported or exported than any recursive type |
856 | | // referred to by that import/export must additionally be exported |
857 | | // or imported. Here this walks the "first layer" of the type which |
858 | | // delegates to `TypeAlloc::type_named_type_id` to determine whether |
859 | | // the components of the type being named here are indeed all they |
860 | | // themselves named. |
861 | | ComponentEntityType::Type { |
862 | 196k | created, |
863 | 196k | referenced, |
864 | | } => { |
865 | 196k | if !self.all_valtypes_named(types, *referenced, set) { |
866 | 0 | return false; |
867 | 196k | } |
868 | 196k | match kind { |
869 | | // Imported types are both valid for import and valid for |
870 | | // export. |
871 | 49.4k | ExternKind::Import => { |
872 | 49.4k | self.imported_types.insert(*created); |
873 | 49.4k | self.exported_types.insert(*created); |
874 | 49.4k | } |
875 | 147k | ExternKind::Export => { |
876 | 147k | self.exported_types.insert(*created); |
877 | 147k | } |
878 | | } |
879 | | |
880 | 196k | true |
881 | | } |
882 | | |
883 | | // Instances are slightly nuanced here. The general idea is that if |
884 | | // an instance is imported, then any type exported by the instance |
885 | | // is then also exported. Additionally for exports. To get this to |
886 | | // work out this arm will recursively call |
887 | | // `validate_and_register_named_types` which means that types are |
888 | | // inserted into `self.{imported,exported}_types` as-we-go rather |
889 | | // than all at once. |
890 | | // |
891 | | // This then recursively validates that all items in the instance |
892 | | // itself are valid to import/export, recursive instances are |
893 | | // captured, and everything is appropriately added to the right |
894 | | // imported/exported set. |
895 | 148k | ComponentEntityType::Instance(i) => types[*i].exports.iter().all(|(_name, ty)| { |
896 | 148k | self.validate_and_register_named_types(None, kind, &ty.ty, types) |
897 | 148k | }), |
898 | | |
899 | | // All types referred to by a function must be named. |
900 | 61.5k | ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set), |
901 | | |
902 | 0 | ComponentEntityType::Value(ty) => types.type_named_valtype(ty, set), |
903 | | |
904 | | // Components/modules are always "closed" or "standalone" and don't |
905 | | // need validation with respect to their named types. |
906 | 13.6k | ComponentEntityType::Component(_) | ComponentEntityType::Module(_) => true, |
907 | | } |
908 | 444k | } |
909 | | |
910 | 196k | fn all_valtypes_named( |
911 | 196k | &self, |
912 | 196k | types: &TypeAlloc, |
913 | 196k | id: ComponentAnyTypeId, |
914 | 196k | set: &Set<ComponentAnyTypeId>, |
915 | 196k | ) -> bool { |
916 | 196k | match id { |
917 | | // Resource types, in isolation, are always valid to import or |
918 | | // export since they're either attached to an import or being |
919 | | // exported. |
920 | | // |
921 | | // Note that further validation of this happens in `finish`, too. |
922 | 6.19k | ComponentAnyTypeId::Resource(_) => true, |
923 | | |
924 | | // Component types are validated as they are constructed, |
925 | | // so all component types are valid to export if they've |
926 | | // already been constructed. |
927 | 49.4k | ComponentAnyTypeId::Component(_) => true, |
928 | | |
929 | 141k | ComponentAnyTypeId::Defined(id) => self.all_valtypes_named_in_defined(types, id, set), |
930 | 0 | ComponentAnyTypeId::Func(id) => self.all_valtypes_named_in_func(types, id, set), |
931 | 0 | ComponentAnyTypeId::Instance(id) => self.all_valtypes_named_in_instance(types, id, set), |
932 | | } |
933 | 196k | } |
934 | | |
935 | 0 | fn all_valtypes_named_in_instance( |
936 | 0 | &self, |
937 | 0 | types: &TypeAlloc, |
938 | 0 | id: ComponentInstanceTypeId, |
939 | 0 | set: &Set<ComponentAnyTypeId>, |
940 | 0 | ) -> bool { |
941 | | // Instances must recursively have all referenced types named. |
942 | 0 | let ty = &types[id]; |
943 | 0 | ty.exports.values().all(|ty| match ty.ty { |
944 | 0 | ComponentEntityType::Module(_) => true, |
945 | 0 | ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, id, set), |
946 | 0 | ComponentEntityType::Type { created: id, .. } => { |
947 | 0 | self.all_valtypes_named(types, id, set) |
948 | | } |
949 | 0 | ComponentEntityType::Value(ComponentValType::Type(id)) => { |
950 | 0 | self.all_valtypes_named_in_defined(types, id, set) |
951 | | } |
952 | 0 | ComponentEntityType::Instance(id) => { |
953 | 0 | self.all_valtypes_named_in_instance(types, id, set) |
954 | | } |
955 | | ComponentEntityType::Component(_) |
956 | 0 | | ComponentEntityType::Value(ComponentValType::Primitive(_)) => return true, |
957 | 0 | }) |
958 | 0 | } |
959 | | |
960 | 141k | fn all_valtypes_named_in_defined( |
961 | 141k | &self, |
962 | 141k | types: &TypeAlloc, |
963 | 141k | id: ComponentDefinedTypeId, |
964 | 141k | set: &Set<ComponentAnyTypeId>, |
965 | 141k | ) -> bool { |
966 | 141k | let ty = &types[id]; |
967 | 141k | match ty { |
968 | | // These types do not contain anything which must be |
969 | | // named. |
970 | | ComponentDefinedType::Primitive(_) |
971 | | | ComponentDefinedType::Flags(_) |
972 | 100k | | ComponentDefinedType::Enum(_) => true, |
973 | | |
974 | | // Referenced types of all these aggregates must all be |
975 | | // named. |
976 | 15.8k | ComponentDefinedType::Record(r) => { |
977 | 62.4k | r.fields.values().all(|t| types.type_named_valtype(t, set)) |
978 | | } |
979 | 589 | ComponentDefinedType::Tuple(r) => { |
980 | 1.07k | r.types.iter().all(|t| types.type_named_valtype(t, set)) |
981 | | } |
982 | 23.1k | ComponentDefinedType::Variant(r) => r |
983 | 23.1k | .cases |
984 | 23.1k | .values() |
985 | 85.9k | .filter_map(|t| t.ty.as_ref()) |
986 | 69.9k | .all(|t| types.type_named_valtype(t, set)), |
987 | 231 | ComponentDefinedType::Result { ok, err, .. } => { |
988 | 231 | ok.as_ref() |
989 | 231 | .map(|t| types.type_named_valtype(t, set)) |
990 | 231 | .unwrap_or(true) |
991 | 231 | && err |
992 | 231 | .as_ref() |
993 | 231 | .map(|t| types.type_named_valtype(t, set)) |
994 | 231 | .unwrap_or(true) |
995 | | } |
996 | 189 | ComponentDefinedType::List { element: ty, .. } |
997 | 23 | | ComponentDefinedType::FixedLengthList { element: ty, .. } |
998 | 902 | | ComponentDefinedType::Option { ty, .. } => types.type_named_valtype(ty, set), |
999 | 0 | ComponentDefinedType::Map { key, value, .. } => { |
1000 | 0 | types.type_named_valtype(key, set) && types.type_named_valtype(value, set) |
1001 | | } |
1002 | | |
1003 | | // The resource referred to by own/borrow must be named. |
1004 | 0 | ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => { |
1005 | 0 | set.contains(&ComponentAnyTypeId::from(*id)) |
1006 | | } |
1007 | | |
1008 | 49 | ComponentDefinedType::Future { ty, .. } | ComponentDefinedType::Stream { ty, .. } => ty |
1009 | 49 | .as_ref() |
1010 | 49 | .map(|ty| types.type_named_valtype(ty, set)) |
1011 | 49 | .unwrap_or(true), |
1012 | | } |
1013 | 141k | } |
1014 | | |
1015 | 61.5k | fn all_valtypes_named_in_func( |
1016 | 61.5k | &self, |
1017 | 61.5k | types: &TypeAlloc, |
1018 | 61.5k | id: ComponentFuncTypeId, |
1019 | 61.5k | set: &Set<ComponentAnyTypeId>, |
1020 | 61.5k | ) -> bool { |
1021 | 61.5k | let ty = &types[id]; |
1022 | | // Function types must have all their parameters/results named. |
1023 | 61.5k | ty.params |
1024 | 61.5k | .iter() |
1025 | 61.5k | .map(|(_, ty)| ty) |
1026 | 61.5k | .chain(&ty.result) |
1027 | 181k | .all(|ty| types.type_named_valtype(ty, set)) |
1028 | 61.5k | } |
1029 | | |
1030 | | /// Updates the type `id` specified, an identifier for a component instance |
1031 | | /// type, to be imported into this component. |
1032 | | /// |
1033 | | /// Importing an instance type into a component specially handles the |
1034 | | /// defined resources registered in the instance type. Notably all |
1035 | | /// defined resources are "freshened" into brand new type variables and |
1036 | | /// these new variables are substituted within the type. This is what |
1037 | | /// creates a new `TypeId` and may update the `id` specified. |
1038 | | /// |
1039 | | /// One side effect of this operation, for example, is that if an instance |
1040 | | /// type is used twice to import two different instances then the instances |
1041 | | /// do not share resource types despite sharing the same original instance |
1042 | | /// type. |
1043 | 8.31k | fn prepare_instance_import(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) { |
1044 | 8.31k | let ty = &types[*id]; |
1045 | | |
1046 | | // No special treatment for imports of instances which themselves have |
1047 | | // no defined resources |
1048 | 8.31k | if ty.defined_resources.is_empty() { |
1049 | 7.42k | return; |
1050 | 886 | } |
1051 | | |
1052 | 886 | let mut new_ty = ComponentInstanceType { |
1053 | 886 | // Copied from the input verbatim |
1054 | 886 | info: ty.info, |
1055 | 886 | |
1056 | 886 | // Copied over as temporary storage for now, and both of these are |
1057 | 886 | // filled out and expanded below. |
1058 | 886 | exports: ty.exports.clone(), |
1059 | 886 | explicit_resources: ty.explicit_resources.clone(), |
1060 | 886 | |
1061 | 886 | // Explicitly discard this field since the |
1062 | 886 | // defined resources are lifted into `self` |
1063 | 886 | defined_resources: Default::default(), |
1064 | 886 | }; |
1065 | | |
1066 | | // Create brand new resources for all defined ones in the instance. |
1067 | 886 | let resources = (0..ty.defined_resources.len()) |
1068 | 1.07k | .map(|_| types.alloc_resource_id()) |
1069 | 886 | .collect::<IndexSet<_>>(); |
1070 | | |
1071 | | // Build a map from the defined resources in `ty` to those in `new_ty`. |
1072 | | // |
1073 | | // As part of this same loop the new resources, which were previously |
1074 | | // defined in `ty`, now become imported variables in `self`. Their |
1075 | | // path for where they're imported is updated as well with |
1076 | | // `self.next_import_index` as the import-to-be soon. |
1077 | 886 | let mut mapping = Remapping::default(); |
1078 | 886 | let ty = &types[*id]; |
1079 | 1.07k | for (old, new) in ty.defined_resources.iter().zip(&resources) { |
1080 | 1.07k | let prev = mapping.resources.insert(*old, new.resource()); |
1081 | 1.07k | assert!(prev.is_none()); |
1082 | | |
1083 | 1.07k | let mut base = vec![self.imports.len()]; |
1084 | 1.07k | base.extend(ty.explicit_resources[old].iter().copied()); |
1085 | 1.07k | self.imported_resources.insert(new.resource(), base); |
1086 | | } |
1087 | | |
1088 | | // Using the old-to-new resource mapping perform a substitution on |
1089 | | // the `exports` and `explicit_resources` fields of `new_ty` |
1090 | 5.74k | for ty in new_ty.exports.values_mut() { |
1091 | 5.74k | types.remap_component_entity(&mut ty.ty, &mut mapping); |
1092 | 5.74k | } |
1093 | 1.11k | for (id, path) in mem::take(&mut new_ty.explicit_resources) { |
1094 | 1.11k | let id = *mapping.resources.get(&id).unwrap_or(&id); |
1095 | 1.11k | new_ty.explicit_resources.insert(id, path); |
1096 | 1.11k | } |
1097 | | |
1098 | | // Now that `new_ty` is complete finish its registration and then |
1099 | | // update `id` on the way out. |
1100 | 886 | *id = types.push_ty(new_ty); |
1101 | 8.31k | } |
1102 | | |
1103 | | /// Prepares an instance type, pointed to `id`, for being exported as a |
1104 | | /// concrete instance from `self`. |
1105 | | /// |
1106 | | /// This will internally perform any resource "freshening" as required and |
1107 | | /// then additionally update metadata within `self` about resources being |
1108 | | /// exported or defined. |
1109 | 48.2k | fn prepare_instance_export(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) { |
1110 | | // Exports of an instance mean that the enclosing context |
1111 | | // is inheriting the resources that the instance |
1112 | | // encapsulates. This means that the instance type |
1113 | | // recorded for this export will itself have no |
1114 | | // defined resources. |
1115 | 48.2k | let ty = &types[*id]; |
1116 | | |
1117 | | // Check to see if `defined_resources` is non-empty, and if so then |
1118 | | // "freshen" all the resources and inherit them to our own defined |
1119 | | // resources, updating `id` in the process. |
1120 | | // |
1121 | | // Note though that this specifically is not rewriting the resources of |
1122 | | // exported instances. The `defined_resources` set on instance types is |
1123 | | // a little subtle (see its documentation for more info), but the |
1124 | | // general idea is that for a concrete instance it's always empty. Only |
1125 | | // for instance type definitions does it ever have elements in it. |
1126 | | // |
1127 | | // That means that if this set is non-empty then what's happening is |
1128 | | // that we're in a type context an exporting an instance of a previously |
1129 | | // specified type. In this case all resources are required to be |
1130 | | // "freshened" to ensure that multiple exports of the same type all |
1131 | | // export different types of resources. |
1132 | | // |
1133 | | // And finally note that this operation empties out the |
1134 | | // `defined_resources` set of the type that is registered for the |
1135 | | // instance, as this export is modeled as producing a concrete instance. |
1136 | 48.2k | if !ty.defined_resources.is_empty() { |
1137 | 1.43k | let mut new_ty = ty.clone(); |
1138 | 1.43k | let mut mapping = Remapping::default(); |
1139 | 1.61k | for old in mem::take(&mut new_ty.defined_resources) { |
1140 | 1.61k | let new = types.alloc_resource_id(); |
1141 | 1.61k | mapping.resources.insert(old, new.resource()); |
1142 | 1.61k | self.defined_resources.insert(new.resource(), None); |
1143 | 1.61k | } |
1144 | 12.0k | for ty in new_ty.exports.values_mut() { |
1145 | 12.0k | types.remap_component_entity(&mut ty.ty, &mut mapping); |
1146 | 12.0k | } |
1147 | 1.65k | for (id, path) in mem::take(&mut new_ty.explicit_resources) { |
1148 | 1.65k | let id = mapping.resources.get(&id).copied().unwrap_or(id); |
1149 | 1.65k | new_ty.explicit_resources.insert(id, path); |
1150 | 1.65k | } |
1151 | 1.43k | *id = types.push_ty(new_ty); |
1152 | 46.7k | } |
1153 | | |
1154 | | // Any explicit resources in the instance are now additionally explicit |
1155 | | // in this component since it's exported. |
1156 | | // |
1157 | | // The path to each explicit resources gets one element prepended which |
1158 | | // is `self.next_export_index`, the index of the export about to be |
1159 | | // generated. |
1160 | 48.2k | let ty = &types[*id]; |
1161 | 48.2k | for (id, path) in ty.explicit_resources.iter() { |
1162 | 2.51k | let mut new_path = vec![self.exports.len()]; |
1163 | 2.51k | new_path.extend(path); |
1164 | 2.51k | self.explicit_resources.insert(*id, new_path); |
1165 | 2.51k | } |
1166 | 48.2k | } |
1167 | | |
1168 | 261k | pub fn add_export( |
1169 | 261k | &mut self, |
1170 | 261k | name: ComponentExternName<'_>, |
1171 | 261k | mut ty: ComponentEntityType, |
1172 | 261k | types: &mut TypeAlloc, |
1173 | 261k | offset: usize, |
1174 | 261k | check_limit: bool, |
1175 | 261k | ) -> Result<()> { |
1176 | 261k | if check_limit { |
1177 | 173k | check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?; |
1178 | 88.4k | } |
1179 | 261k | self.add_entity( |
1180 | 261k | &mut ty, |
1181 | 261k | Some((name.name, ExternKind::Export)), |
1182 | 261k | types, |
1183 | 261k | offset, |
1184 | 0 | )?; |
1185 | 261k | self.toplevel_exported_resources.validate_extern( |
1186 | 261k | &name, |
1187 | 261k | ExternKind::Export, |
1188 | 261k | &ty, |
1189 | 261k | types, |
1190 | 261k | offset, |
1191 | 261k | &mut self.export_names, |
1192 | 261k | &mut self.exports, |
1193 | 261k | &mut self.type_info, |
1194 | 261k | &self.features, |
1195 | 0 | )?; |
1196 | 261k | Ok(()) |
1197 | 261k | } |
1198 | | |
1199 | 42.9k | pub fn canonical_function( |
1200 | 42.9k | &mut self, |
1201 | 42.9k | func: CanonicalFunction, |
1202 | 42.9k | types: &mut TypeAlloc, |
1203 | 42.9k | offset: usize, |
1204 | 42.9k | ) -> Result<()> { |
1205 | 42.9k | match func { |
1206 | | CanonicalFunction::Lift { |
1207 | 9.12k | core_func_index, |
1208 | 9.12k | type_index, |
1209 | 9.12k | options, |
1210 | 9.12k | } => self.lift_function(core_func_index, type_index, &options, types, offset), |
1211 | | CanonicalFunction::Lower { |
1212 | 4.94k | func_index, |
1213 | 4.94k | options, |
1214 | 4.94k | } => self.lower_function(func_index, &options, types, offset), |
1215 | 636 | CanonicalFunction::ResourceNew { resource } => { |
1216 | 636 | self.resource_new(resource, types, offset) |
1217 | | } |
1218 | 1.23k | CanonicalFunction::ResourceDrop { resource } => { |
1219 | 1.23k | self.resource_drop(resource, types, offset) |
1220 | | } |
1221 | 636 | CanonicalFunction::ResourceRep { resource } => { |
1222 | 636 | self.resource_rep(resource, types, offset) |
1223 | | } |
1224 | 0 | CanonicalFunction::ThreadSpawnRef { func_ty_index } => { |
1225 | 0 | self.thread_spawn_ref(func_ty_index, types, offset) |
1226 | | } |
1227 | | CanonicalFunction::ThreadSpawnIndirect { |
1228 | 0 | func_ty_index, |
1229 | 0 | table_index, |
1230 | 0 | } => self.thread_spawn_indirect(func_ty_index, table_index, types, offset), |
1231 | | CanonicalFunction::ThreadAvailableParallelism => { |
1232 | 0 | self.thread_available_parallelism(types, offset) |
1233 | | } |
1234 | 1.54k | CanonicalFunction::BackpressureInc => self.backpressure_inc(types, offset), |
1235 | 1.54k | CanonicalFunction::BackpressureDec => self.backpressure_dec(types, offset), |
1236 | 2.82k | CanonicalFunction::TaskReturn { result, options } => { |
1237 | 2.82k | self.task_return(&result, &options, types, offset) |
1238 | | } |
1239 | 1.54k | CanonicalFunction::TaskCancel => self.task_cancel(types, offset), |
1240 | 1.54k | CanonicalFunction::ContextGet { ty, slot } => self.context_get(ty, slot, types, offset), |
1241 | 1.54k | CanonicalFunction::ContextSet { ty, slot } => self.context_set(ty, slot, types, offset), |
1242 | 1.54k | CanonicalFunction::SubtaskDrop => self.subtask_drop(types, offset), |
1243 | 1.54k | CanonicalFunction::SubtaskCancel { async_ } => { |
1244 | 1.54k | self.subtask_cancel(async_, types, offset) |
1245 | | } |
1246 | 60 | CanonicalFunction::StreamNew { ty } => self.stream_new(ty, types, offset), |
1247 | 60 | CanonicalFunction::StreamRead { ty, options } => { |
1248 | 60 | self.stream_read(ty, &options, types, offset) |
1249 | | } |
1250 | 60 | CanonicalFunction::StreamWrite { ty, options } => { |
1251 | 60 | self.stream_write(ty, &options, types, offset) |
1252 | | } |
1253 | 60 | CanonicalFunction::StreamCancelRead { ty, async_ } => { |
1254 | 60 | self.stream_cancel_read(ty, async_, types, offset) |
1255 | | } |
1256 | 60 | CanonicalFunction::StreamCancelWrite { ty, async_ } => { |
1257 | 60 | self.stream_cancel_write(ty, async_, types, offset) |
1258 | | } |
1259 | 60 | CanonicalFunction::StreamDropReadable { ty } => { |
1260 | 60 | self.stream_drop_readable(ty, types, offset) |
1261 | | } |
1262 | 60 | CanonicalFunction::StreamDropWritable { ty } => { |
1263 | 60 | self.stream_drop_writable(ty, types, offset) |
1264 | | } |
1265 | 582 | CanonicalFunction::FutureNew { ty } => self.future_new(ty, types, offset), |
1266 | 69 | CanonicalFunction::FutureRead { ty, options } => { |
1267 | 69 | self.future_read(ty, &options, types, offset) |
1268 | | } |
1269 | 69 | CanonicalFunction::FutureWrite { ty, options } => { |
1270 | 69 | self.future_write(ty, &options, types, offset) |
1271 | | } |
1272 | 582 | CanonicalFunction::FutureCancelRead { ty, async_ } => { |
1273 | 582 | self.future_cancel_read(ty, async_, types, offset) |
1274 | | } |
1275 | 582 | CanonicalFunction::FutureCancelWrite { ty, async_ } => { |
1276 | 582 | self.future_cancel_write(ty, async_, types, offset) |
1277 | | } |
1278 | 582 | CanonicalFunction::FutureDropReadable { ty } => { |
1279 | 582 | self.future_drop_readable(ty, types, offset) |
1280 | | } |
1281 | 582 | CanonicalFunction::FutureDropWritable { ty } => { |
1282 | 582 | self.future_drop_writable(ty, types, offset) |
1283 | | } |
1284 | 0 | CanonicalFunction::ErrorContextNew { options } => { |
1285 | 0 | self.error_context_new(options.into_vec(), types, offset) |
1286 | | } |
1287 | 0 | CanonicalFunction::ErrorContextDebugMessage { options } => { |
1288 | 0 | self.error_context_debug_message(options.into_vec(), types, offset) |
1289 | | } |
1290 | 0 | CanonicalFunction::ErrorContextDrop => self.error_context_drop(types, offset), |
1291 | 1.54k | CanonicalFunction::WaitableSetNew => self.waitable_set_new(types, offset), |
1292 | | CanonicalFunction::WaitableSetWait { |
1293 | | cancellable: _, |
1294 | 1.54k | memory, |
1295 | 1.54k | } => self.waitable_set_wait(memory, types, offset), |
1296 | | CanonicalFunction::WaitableSetPoll { |
1297 | | cancellable: _, |
1298 | 1.54k | memory, |
1299 | 1.54k | } => self.waitable_set_poll(memory, types, offset), |
1300 | 1.54k | CanonicalFunction::WaitableSetDrop => self.waitable_set_drop(types, offset), |
1301 | 1.54k | CanonicalFunction::WaitableJoin => self.waitable_join(types, offset), |
1302 | 0 | CanonicalFunction::ThreadIndex => self.thread_index(types, offset), |
1303 | | CanonicalFunction::ThreadNewIndirect { |
1304 | 0 | func_ty_index, |
1305 | 0 | table_index, |
1306 | 0 | } => self.thread_new_indirect(func_ty_index, table_index, types, offset), |
1307 | 0 | CanonicalFunction::ThreadResumeLater => self.thread_resume_later(types, offset), |
1308 | 0 | CanonicalFunction::ThreadSuspend { cancellable } => { |
1309 | 0 | self.thread_suspend(cancellable, types, offset) |
1310 | | } |
1311 | 1.54k | CanonicalFunction::ThreadYield { cancellable: _ } => self.thread_yield(types, offset), |
1312 | 0 | CanonicalFunction::ThreadSuspendThenResume { cancellable } => { |
1313 | 0 | self.thread_suspend_then_resume(cancellable, types, offset) |
1314 | | } |
1315 | 0 | CanonicalFunction::ThreadYieldThenResume { cancellable } => { |
1316 | 0 | self.thread_yield_then_resume(cancellable, types, offset) |
1317 | | } |
1318 | 0 | CanonicalFunction::ThreadSuspendThenPromote { cancellable } => { |
1319 | 0 | self.thread_suspend_then_promote(cancellable, types, offset) |
1320 | | } |
1321 | 0 | CanonicalFunction::ThreadYieldThenPromote { cancellable } => { |
1322 | 0 | self.thread_yield_then_promote(cancellable, types, offset) |
1323 | | } |
1324 | | } |
1325 | 42.9k | } |
1326 | | |
1327 | 9.12k | fn lift_function( |
1328 | 9.12k | &mut self, |
1329 | 9.12k | core_func_index: u32, |
1330 | 9.12k | type_index: u32, |
1331 | 9.12k | options: &[CanonicalOption], |
1332 | 9.12k | types: &mut TypeAlloc, |
1333 | 9.12k | offset: usize, |
1334 | 9.12k | ) -> Result<()> { |
1335 | 9.12k | let ty = self.function_type_at(type_index, types, offset)?; |
1336 | 9.12k | let core_ty_id = self.core_function_at(core_func_index, offset)?; |
1337 | | |
1338 | | // Lifting a function is for an export, so match the expected canonical ABI |
1339 | | // export signature |
1340 | 9.12k | let mut options = self.check_options(types, options, offset)?; |
1341 | 9.12k | options.check_lift(types, self, core_ty_id, offset)?; |
1342 | 9.12k | options.check_asyncness(ty, offset)?; |
1343 | 9.12k | let func_ty = ty.lower(types, &options, Abi::Lift, offset)?; |
1344 | 9.12k | let lowered_core_ty_id = func_ty.intern(types, offset); |
1345 | | |
1346 | 9.12k | if core_ty_id == lowered_core_ty_id { |
1347 | 9.12k | self.funcs |
1348 | 9.12k | .push(self.types[type_index as usize].unwrap_func()); |
1349 | 9.12k | return Ok(()); |
1350 | 0 | } |
1351 | | |
1352 | 0 | let ty = types[core_ty_id].unwrap_func(); |
1353 | 0 | let lowered_ty = types[lowered_core_ty_id].unwrap_func(); |
1354 | | |
1355 | 0 | if lowered_ty.params() != ty.params() { |
1356 | 0 | bail!( |
1357 | 0 | offset, |
1358 | | "lowered parameter types `{:?}` do not match parameter types `{:?}` of \ |
1359 | | core function {core_func_index}", |
1360 | 0 | lowered_ty.params(), |
1361 | 0 | ty.params() |
1362 | | ); |
1363 | 0 | } |
1364 | | |
1365 | 0 | if lowered_ty.results() != ty.results() { |
1366 | 0 | bail!( |
1367 | 0 | offset, |
1368 | | "lowered result types `{:?}` do not match result types `{:?}` of \ |
1369 | | core function {core_func_index}", |
1370 | 0 | lowered_ty.results(), |
1371 | 0 | ty.results() |
1372 | | ); |
1373 | 0 | } |
1374 | | |
1375 | | // Otherwise, must be different rec groups or subtyping (which isn't |
1376 | | // supported yet) or something. |
1377 | 0 | bail!( |
1378 | 0 | offset, |
1379 | | "lowered function type `{:?}` does not match type `{:?}` of \ |
1380 | | core function {core_func_index}", |
1381 | 0 | types[lowered_core_ty_id], |
1382 | 0 | types[core_ty_id], |
1383 | | ); |
1384 | 9.12k | } |
1385 | | |
1386 | 4.94k | fn lower_function( |
1387 | 4.94k | &mut self, |
1388 | 4.94k | func_index: u32, |
1389 | 4.94k | options: &[CanonicalOption], |
1390 | 4.94k | types: &mut TypeAlloc, |
1391 | 4.94k | offset: usize, |
1392 | 4.94k | ) -> Result<()> { |
1393 | 4.94k | let ty = &types[self.function_at(func_index, offset)?]; |
1394 | | |
1395 | | // Lowering a function is for an import, so use a function type that matches |
1396 | | // the expected canonical ABI import signature. |
1397 | 4.94k | let options = self.check_options(types, options, offset)?; |
1398 | 4.94k | options.check_lower(offset)?; |
1399 | 4.94k | options.check_asyncness(ty, offset)?; |
1400 | | |
1401 | 4.94k | let func_ty = ty.lower(types, &options, Abi::Lower, offset)?; |
1402 | 4.94k | let ty_id = func_ty.intern(types, offset); |
1403 | | |
1404 | 4.94k | self.core_funcs.push(ty_id); |
1405 | 4.94k | Ok(()) |
1406 | 4.94k | } |
1407 | | |
1408 | 636 | fn resource_new(&mut self, resource: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1409 | 636 | let rep = self.check_local_resource(resource, types, offset)?; |
1410 | 636 | let id = types.intern_func_type(FuncType::new([rep], [ValType::I32]), offset); |
1411 | 636 | self.core_funcs.push(id); |
1412 | 636 | Ok(()) |
1413 | 636 | } |
1414 | | |
1415 | 1.23k | fn resource_drop(&mut self, resource: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1416 | 1.23k | self.resource_at(resource, types, offset)?; |
1417 | 1.23k | let id = types.intern_func_type(FuncType::new([ValType::I32], []), offset); |
1418 | 1.23k | self.core_funcs.push(id); |
1419 | 1.23k | Ok(()) |
1420 | 1.23k | } |
1421 | | |
1422 | 636 | fn resource_rep(&mut self, resource: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1423 | 636 | let rep = self.check_local_resource(resource, types, offset)?; |
1424 | 636 | let id = types.intern_func_type(FuncType::new([ValType::I32], [rep]), offset); |
1425 | 636 | self.core_funcs.push(id); |
1426 | 636 | Ok(()) |
1427 | 636 | } |
1428 | | |
1429 | 1.54k | fn backpressure_inc(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1430 | 1.54k | require_feature::cm_async( |
1431 | 1.54k | self.features, |
1432 | | "`backpressure.inc` requires the component model async feature", |
1433 | 1.54k | offset, |
1434 | 0 | )?; |
1435 | | |
1436 | 1.54k | self.core_funcs |
1437 | 1.54k | .push(types.intern_func_type(FuncType::new([], []), offset)); |
1438 | 1.54k | Ok(()) |
1439 | 1.54k | } |
1440 | | |
1441 | 1.54k | fn backpressure_dec(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1442 | 1.54k | require_feature::cm_async( |
1443 | 1.54k | self.features, |
1444 | | "`backpressure.dec` requires the component model async feature", |
1445 | 1.54k | offset, |
1446 | 0 | )?; |
1447 | | |
1448 | 1.54k | self.core_funcs |
1449 | 1.54k | .push(types.intern_func_type(FuncType::new([], []), offset)); |
1450 | 1.54k | Ok(()) |
1451 | 1.54k | } |
1452 | | |
1453 | 2.82k | fn task_return( |
1454 | 2.82k | &mut self, |
1455 | 2.82k | result: &Option<crate::ComponentValType>, |
1456 | 2.82k | options: &[CanonicalOption], |
1457 | 2.82k | types: &mut TypeAlloc, |
1458 | 2.82k | offset: usize, |
1459 | 2.82k | ) -> Result<()> { |
1460 | 2.82k | require_feature::cm_async( |
1461 | 2.82k | self.features, |
1462 | | "`task.return` requires the component model async feature", |
1463 | 2.82k | offset, |
1464 | 0 | )?; |
1465 | | |
1466 | 2.82k | let func_ty = ComponentFuncType { |
1467 | | async_: false, |
1468 | 2.82k | info: TypeInfo::new(), |
1469 | 2.82k | params: result |
1470 | 2.82k | .iter() |
1471 | 2.82k | .map(|ty| { |
1472 | | Ok(( |
1473 | 2.73k | KebabString::new("v").unwrap(), |
1474 | 2.73k | match ty { |
1475 | 2.72k | crate::ComponentValType::Primitive(ty) => { |
1476 | 2.72k | ComponentValType::Primitive(*ty) |
1477 | | } |
1478 | 12 | crate::ComponentValType::Type(index) => { |
1479 | 12 | ComponentValType::Type(self.defined_type_at(*index, offset)?) |
1480 | | } |
1481 | | }, |
1482 | | )) |
1483 | 2.73k | }) |
1484 | 2.82k | .collect::<Result<_>>()?, |
1485 | 2.82k | result: None, |
1486 | | }; |
1487 | | |
1488 | 2.82k | let options = self.check_options(types, options, offset)?; |
1489 | 2.82k | if options.realloc.is_some() { |
1490 | 0 | bail!(offset, "cannot specify `realloc` option on `task.return`") |
1491 | 2.82k | } |
1492 | 2.82k | if options.post_return.is_some() { |
1493 | 0 | bail!( |
1494 | 0 | offset, |
1495 | | "cannot specify `post-return` option on `task.return`" |
1496 | | ) |
1497 | 2.82k | } |
1498 | 2.82k | options.check_lower(offset)?; |
1499 | 2.82k | options.require_sync(offset, "task.return")?; |
1500 | | |
1501 | 2.82k | let func_ty = func_ty.lower(types, &options, Abi::Lower, offset)?; |
1502 | 2.82k | let ty_id = func_ty.intern(types, offset); |
1503 | | |
1504 | 2.82k | self.core_funcs.push(ty_id); |
1505 | 2.82k | Ok(()) |
1506 | 2.82k | } |
1507 | | |
1508 | 1.54k | fn task_cancel(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1509 | 1.54k | require_feature::cm_async( |
1510 | 1.54k | self.features, |
1511 | | "`task.cancel` requires the component model async feature", |
1512 | 1.54k | offset, |
1513 | 0 | )?; |
1514 | | |
1515 | 1.54k | self.core_funcs |
1516 | 1.54k | .push(types.intern_func_type(FuncType::new([], []), offset)); |
1517 | 1.54k | Ok(()) |
1518 | 1.54k | } |
1519 | | |
1520 | 3.09k | fn validate_context_immediate( |
1521 | 3.09k | &self, |
1522 | 3.09k | immediate: u32, |
1523 | 3.09k | operation: &str, |
1524 | 3.09k | offset: usize, |
1525 | 3.09k | ) -> Result<()> { |
1526 | 3.09k | if immediate > 0 { |
1527 | 0 | require_feature::cm_threading( |
1528 | 0 | self.features, |
1529 | 0 | format_args!("`{operation}` immediate must be zero: {immediate}"), |
1530 | 0 | offset, |
1531 | 0 | )?; |
1532 | 0 | if immediate > 1 { |
1533 | 0 | bail!( |
1534 | 0 | offset, |
1535 | | "`{operation}` immediate must be zero or one: {immediate}" |
1536 | | ) |
1537 | 0 | } |
1538 | 3.09k | } |
1539 | 3.09k | Ok(()) |
1540 | 3.09k | } |
1541 | | |
1542 | 1.54k | fn context_get( |
1543 | 1.54k | &mut self, |
1544 | 1.54k | ty: ValType, |
1545 | 1.54k | i: u32, |
1546 | 1.54k | types: &mut TypeAlloc, |
1547 | 1.54k | offset: usize, |
1548 | 1.54k | ) -> Result<()> { |
1549 | 1.54k | require_feature::cm_async( |
1550 | 1.54k | self.features, |
1551 | | "`context.get` requires the component model async feature", |
1552 | 1.54k | offset, |
1553 | 0 | )?; |
1554 | 1.54k | self.validate_context_type(ty, "context.get", offset)?; |
1555 | 1.54k | self.validate_context_immediate(i, "context.get", offset)?; |
1556 | | |
1557 | 1.54k | self.core_funcs |
1558 | 1.54k | .push(types.intern_func_type(FuncType::new([], [ty]), offset)); |
1559 | 1.54k | Ok(()) |
1560 | 1.54k | } |
1561 | | |
1562 | 1.54k | fn context_set( |
1563 | 1.54k | &mut self, |
1564 | 1.54k | ty: ValType, |
1565 | 1.54k | i: u32, |
1566 | 1.54k | types: &mut TypeAlloc, |
1567 | 1.54k | offset: usize, |
1568 | 1.54k | ) -> Result<()> { |
1569 | 1.54k | require_feature::cm_async( |
1570 | 1.54k | self.features, |
1571 | | "`context.set` requires the component model async feature", |
1572 | 1.54k | offset, |
1573 | 0 | )?; |
1574 | 1.54k | self.validate_context_type(ty, "context.set", offset)?; |
1575 | 1.54k | self.validate_context_immediate(i, "context.set", offset)?; |
1576 | | |
1577 | 1.54k | self.core_funcs |
1578 | 1.54k | .push(types.intern_func_type(FuncType::new([ty], []), offset)); |
1579 | 1.54k | Ok(()) |
1580 | 1.54k | } |
1581 | | |
1582 | 3.09k | fn validate_context_type(&mut self, ty: ValType, intrinsic: &str, offset: usize) -> Result<()> { |
1583 | 3.09k | match ty { |
1584 | 3.09k | ValType::I32 => {} |
1585 | | ValType::I64 => { |
1586 | 0 | require_feature::cm64( |
1587 | 0 | self.features, |
1588 | 0 | format_args!( |
1589 | | "64-bit `{intrinsic}` requires the component model 64-bit feature" |
1590 | | ), |
1591 | 0 | offset, |
1592 | 0 | )?; |
1593 | 0 | {} |
1594 | | } |
1595 | 0 | _ => bail!(offset, "`{intrinsic}` only supports `i32` or `i64`"), |
1596 | | } |
1597 | | |
1598 | 3.09k | match self.context_type { |
1599 | 1.54k | None => self.context_type = Some(ty), |
1600 | 1.54k | Some(other) => { |
1601 | 1.54k | if other != ty { |
1602 | 0 | bail!( |
1603 | 0 | offset, |
1604 | | "`{intrinsic}` type must match previous context type" |
1605 | | ) |
1606 | 1.54k | } |
1607 | | } |
1608 | | } |
1609 | 3.09k | Ok(()) |
1610 | 3.09k | } |
1611 | | |
1612 | 1.54k | fn subtask_drop(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1613 | 1.54k | require_feature::cm_async( |
1614 | 1.54k | self.features, |
1615 | | "`subtask.drop` requires the component model async feature", |
1616 | 1.54k | offset, |
1617 | 0 | )?; |
1618 | | |
1619 | 1.54k | self.core_funcs |
1620 | 1.54k | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1621 | 1.54k | Ok(()) |
1622 | 1.54k | } |
1623 | | |
1624 | 1.54k | fn subtask_cancel(&mut self, async_: bool, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1625 | 1.54k | require_feature::cm_async( |
1626 | 1.54k | self.features, |
1627 | | "`subtask.cancel` requires the component model async feature", |
1628 | 1.54k | offset, |
1629 | 0 | )?; |
1630 | 1.54k | if async_ { |
1631 | 0 | require_feature::cm_more_async_builtins( |
1632 | 0 | self.features, |
1633 | | "async `subtask.cancel` requires the component model more async builtins feature", |
1634 | 0 | offset, |
1635 | 0 | )?; |
1636 | 1.54k | } |
1637 | | |
1638 | 1.54k | self.core_funcs |
1639 | 1.54k | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1640 | 1.54k | Ok(()) |
1641 | 1.54k | } |
1642 | | |
1643 | 60 | fn stream_new(&mut self, ty: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1644 | 60 | require_feature::cm_async( |
1645 | 60 | self.features, |
1646 | | "`stream.new` requires the component model async feature", |
1647 | 60 | offset, |
1648 | 0 | )?; |
1649 | | |
1650 | 60 | let ty = self.defined_type_at(ty, offset)?; |
1651 | 60 | let ComponentDefinedType::Stream { .. } = &types[ty] else { |
1652 | 0 | bail!(offset, "`stream.new` requires a stream type") |
1653 | | }; |
1654 | | |
1655 | 60 | self.core_funcs |
1656 | 60 | .push(types.intern_func_type(FuncType::new([], [ValType::I64]), offset)); |
1657 | 60 | Ok(()) |
1658 | 60 | } |
1659 | | |
1660 | 60 | fn stream_read( |
1661 | 60 | &mut self, |
1662 | 60 | ty: u32, |
1663 | 60 | options: &[CanonicalOption], |
1664 | 60 | types: &mut TypeAlloc, |
1665 | 60 | offset: usize, |
1666 | 60 | ) -> Result<()> { |
1667 | 60 | require_feature::cm_async( |
1668 | 60 | self.features, |
1669 | | "`stream.read` requires the component model async feature", |
1670 | 60 | offset, |
1671 | 0 | )?; |
1672 | | |
1673 | 60 | let ty = self.defined_type_at(ty, offset)?; |
1674 | 60 | let ComponentDefinedType::Stream { ty: elem_ty, .. } = &types[ty] else { |
1675 | 0 | bail!(offset, "`stream.read` requires a stream type") |
1676 | | }; |
1677 | | |
1678 | 60 | let options = self.check_options(types, options, offset)?; |
1679 | 60 | if options.concurrency.is_sync() { |
1680 | 0 | require_feature::cm_more_async_builtins( |
1681 | 0 | self.features, |
1682 | | "synchronous `stream.read` requires the component model more async builtins feature", |
1683 | 0 | offset, |
1684 | 0 | )?; |
1685 | 60 | } |
1686 | 60 | let ty_id = options |
1687 | 60 | .require_memory_if(offset, || elem_ty.is_some())? |
1688 | 60 | .require_realloc_if(offset, || elem_ty.is_some_and(|ty| ty.contains_ptr(types)))? |
1689 | 60 | .check_lower(offset)? |
1690 | 60 | .check_core_type( |
1691 | 60 | types, |
1692 | 60 | FuncType::new([ValType::I32; 3], [ValType::I32]), |
1693 | 60 | offset, |
1694 | 0 | )?; |
1695 | | |
1696 | 60 | self.core_funcs.push(ty_id); |
1697 | 60 | Ok(()) |
1698 | 60 | } |
1699 | | |
1700 | 60 | fn stream_write( |
1701 | 60 | &mut self, |
1702 | 60 | ty: u32, |
1703 | 60 | options: &[CanonicalOption], |
1704 | 60 | types: &mut TypeAlloc, |
1705 | 60 | offset: usize, |
1706 | 60 | ) -> Result<()> { |
1707 | 60 | require_feature::cm_async( |
1708 | 60 | self.features, |
1709 | | "`stream.write` requires the component model async feature", |
1710 | 60 | offset, |
1711 | 0 | )?; |
1712 | | |
1713 | 60 | let ty = self.defined_type_at(ty, offset)?; |
1714 | 60 | let ComponentDefinedType::Stream { ty: elem_ty, .. } = &types[ty] else { |
1715 | 0 | bail!(offset, "`stream.write` requires a stream type") |
1716 | | }; |
1717 | | |
1718 | 60 | let options = self.check_options(types, options, offset)?; |
1719 | 60 | if options.concurrency.is_sync() { |
1720 | 0 | require_feature::cm_more_async_builtins( |
1721 | 0 | self.features, |
1722 | | "synchronous `stream.write` requires the component model more async builtins feature", |
1723 | 0 | offset, |
1724 | 0 | )?; |
1725 | 60 | } |
1726 | 60 | let ty_id = options |
1727 | 60 | .require_memory_if(offset, || elem_ty.is_some())? |
1728 | 60 | .check_lower(offset)? |
1729 | 60 | .check_core_type( |
1730 | 60 | types, |
1731 | 60 | FuncType::new([ValType::I32; 3], [ValType::I32]), |
1732 | 60 | offset, |
1733 | 0 | )?; |
1734 | | |
1735 | 60 | self.core_funcs.push(ty_id); |
1736 | 60 | Ok(()) |
1737 | 60 | } |
1738 | | |
1739 | 60 | fn stream_cancel_read( |
1740 | 60 | &mut self, |
1741 | 60 | ty: u32, |
1742 | 60 | cancellable: bool, |
1743 | 60 | types: &mut TypeAlloc, |
1744 | 60 | offset: usize, |
1745 | 60 | ) -> Result<()> { |
1746 | 60 | require_feature::cm_async( |
1747 | 60 | self.features, |
1748 | | "`stream.cancel-read` requires the component model async feature", |
1749 | 60 | offset, |
1750 | 0 | )?; |
1751 | 60 | if cancellable { |
1752 | 0 | require_feature::cm_more_async_builtins( |
1753 | 0 | self.features, |
1754 | | "async `stream.cancel-read` requires the component model more async builtins feature", |
1755 | 0 | offset, |
1756 | 0 | )?; |
1757 | 60 | } |
1758 | | |
1759 | 60 | let ty = self.defined_type_at(ty, offset)?; |
1760 | 60 | let ComponentDefinedType::Stream { .. } = &types[ty] else { |
1761 | 0 | bail!(offset, "`stream.cancel-read` requires a stream type") |
1762 | | }; |
1763 | | |
1764 | 60 | self.core_funcs |
1765 | 60 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1766 | 60 | Ok(()) |
1767 | 60 | } |
1768 | | |
1769 | 60 | fn stream_cancel_write( |
1770 | 60 | &mut self, |
1771 | 60 | ty: u32, |
1772 | 60 | cancellable: bool, |
1773 | 60 | types: &mut TypeAlloc, |
1774 | 60 | offset: usize, |
1775 | 60 | ) -> Result<()> { |
1776 | 60 | require_feature::cm_async( |
1777 | 60 | self.features, |
1778 | | "`stream.cancel-write` requires the component model async feature", |
1779 | 60 | offset, |
1780 | 0 | )?; |
1781 | 60 | if cancellable { |
1782 | 0 | require_feature::cm_more_async_builtins( |
1783 | 0 | self.features, |
1784 | | "async `stream.cancel-write` requires the component model more async builtins feature", |
1785 | 0 | offset, |
1786 | 0 | )?; |
1787 | 60 | } |
1788 | | |
1789 | 60 | let ty = self.defined_type_at(ty, offset)?; |
1790 | 60 | let ComponentDefinedType::Stream { .. } = &types[ty] else { |
1791 | 0 | bail!(offset, "`stream.cancel-write` requires a stream type") |
1792 | | }; |
1793 | | |
1794 | 60 | self.core_funcs |
1795 | 60 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1796 | 60 | Ok(()) |
1797 | 60 | } |
1798 | | |
1799 | 60 | fn stream_drop_readable( |
1800 | 60 | &mut self, |
1801 | 60 | ty: u32, |
1802 | 60 | types: &mut TypeAlloc, |
1803 | 60 | offset: usize, |
1804 | 60 | ) -> Result<()> { |
1805 | 60 | require_feature::cm_async( |
1806 | 60 | self.features, |
1807 | | "`stream.drop-readable` requires the component model async feature", |
1808 | 60 | offset, |
1809 | 0 | )?; |
1810 | | |
1811 | 60 | let ty = self.defined_type_at(ty, offset)?; |
1812 | 60 | let ComponentDefinedType::Stream { .. } = &types[ty] else { |
1813 | 0 | bail!(offset, "`stream.drop-readable` requires a stream type") |
1814 | | }; |
1815 | | |
1816 | 60 | self.core_funcs |
1817 | 60 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1818 | 60 | Ok(()) |
1819 | 60 | } |
1820 | | |
1821 | 60 | fn stream_drop_writable( |
1822 | 60 | &mut self, |
1823 | 60 | ty: u32, |
1824 | 60 | types: &mut TypeAlloc, |
1825 | 60 | offset: usize, |
1826 | 60 | ) -> Result<()> { |
1827 | 60 | require_feature::cm_async( |
1828 | 60 | self.features, |
1829 | | "`stream.drop-writable` requires the component model async feature", |
1830 | 60 | offset, |
1831 | 0 | )?; |
1832 | | |
1833 | 60 | let ty = self.defined_type_at(ty, offset)?; |
1834 | 60 | let ComponentDefinedType::Stream { .. } = &types[ty] else { |
1835 | 0 | bail!(offset, "`stream.drop-writable` requires a stream type") |
1836 | | }; |
1837 | | |
1838 | 60 | self.core_funcs |
1839 | 60 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1840 | 60 | Ok(()) |
1841 | 60 | } |
1842 | | |
1843 | 582 | fn future_new(&mut self, ty: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
1844 | 582 | require_feature::cm_async( |
1845 | 582 | self.features, |
1846 | | "`future.new` requires the component model async feature", |
1847 | 582 | offset, |
1848 | 0 | )?; |
1849 | | |
1850 | 582 | let ty = self.defined_type_at(ty, offset)?; |
1851 | 582 | let ComponentDefinedType::Future { .. } = &types[ty] else { |
1852 | 0 | bail!(offset, "`future.new` requires a future type") |
1853 | | }; |
1854 | | |
1855 | 582 | self.core_funcs |
1856 | 582 | .push(types.intern_func_type(FuncType::new([], [ValType::I64]), offset)); |
1857 | 582 | Ok(()) |
1858 | 582 | } |
1859 | | |
1860 | 69 | fn future_read( |
1861 | 69 | &mut self, |
1862 | 69 | ty: u32, |
1863 | 69 | options: &[CanonicalOption], |
1864 | 69 | types: &mut TypeAlloc, |
1865 | 69 | offset: usize, |
1866 | 69 | ) -> Result<()> { |
1867 | 69 | require_feature::cm_async( |
1868 | 69 | self.features, |
1869 | | "`future.read` requires the component model async feature", |
1870 | 69 | offset, |
1871 | 0 | )?; |
1872 | | |
1873 | 69 | let ty = self.defined_type_at(ty, offset)?; |
1874 | 69 | let ComponentDefinedType::Future { ty: elem_ty, .. } = &types[ty] else { |
1875 | 0 | bail!(offset, "`future.read` requires a future type") |
1876 | | }; |
1877 | | |
1878 | 69 | let options = self.check_options(types, options, offset)?; |
1879 | 69 | if options.concurrency.is_sync() { |
1880 | 0 | require_feature::cm_more_async_builtins( |
1881 | 0 | self.features, |
1882 | | "synchronous `future.read` requires the component model more async builtins feature", |
1883 | 0 | offset, |
1884 | 0 | )?; |
1885 | 69 | } |
1886 | 69 | let ty_id = options |
1887 | 69 | .require_memory_if(offset, || elem_ty.is_some())? |
1888 | 69 | .require_realloc_if(offset, || elem_ty.is_some_and(|ty| ty.contains_ptr(types)))? |
1889 | 69 | .check_lower(offset)? |
1890 | 69 | .check_core_type( |
1891 | 69 | types, |
1892 | 69 | FuncType::new([ValType::I32; 2], [ValType::I32]), |
1893 | 69 | offset, |
1894 | 0 | )?; |
1895 | | |
1896 | 69 | self.core_funcs.push(ty_id); |
1897 | 69 | Ok(()) |
1898 | 69 | } |
1899 | | |
1900 | 69 | fn future_write( |
1901 | 69 | &mut self, |
1902 | 69 | ty: u32, |
1903 | 69 | options: &[CanonicalOption], |
1904 | 69 | types: &mut TypeAlloc, |
1905 | 69 | offset: usize, |
1906 | 69 | ) -> Result<()> { |
1907 | 69 | require_feature::cm_async( |
1908 | 69 | self.features, |
1909 | | "`future.write` requires the component model async feature", |
1910 | 69 | offset, |
1911 | 0 | )?; |
1912 | | |
1913 | 69 | let ty = self.defined_type_at(ty, offset)?; |
1914 | 69 | let ComponentDefinedType::Future { ty: elem_ty, .. } = &types[ty] else { |
1915 | 0 | bail!(offset, "`future.write` requires a future type") |
1916 | | }; |
1917 | | |
1918 | 69 | let options = self.check_options(types, &options, offset)?; |
1919 | 69 | if options.concurrency.is_sync() { |
1920 | 0 | require_feature::cm_more_async_builtins( |
1921 | 0 | self.features, |
1922 | | "synchronous `future.write` requires the component model more async builtins feature", |
1923 | 0 | offset, |
1924 | 0 | )?; |
1925 | 69 | } |
1926 | 69 | let ty_id = options |
1927 | 69 | .require_memory_if(offset, || elem_ty.is_some())? |
1928 | 69 | .check_core_type( |
1929 | 69 | types, |
1930 | 69 | FuncType::new([ValType::I32; 2], [ValType::I32]), |
1931 | 69 | offset, |
1932 | 0 | )?; |
1933 | | |
1934 | 69 | self.core_funcs.push(ty_id); |
1935 | 69 | Ok(()) |
1936 | 69 | } |
1937 | | |
1938 | 582 | fn future_cancel_read( |
1939 | 582 | &mut self, |
1940 | 582 | ty: u32, |
1941 | 582 | cancellable: bool, |
1942 | 582 | types: &mut TypeAlloc, |
1943 | 582 | offset: usize, |
1944 | 582 | ) -> Result<()> { |
1945 | 582 | require_feature::cm_async( |
1946 | 582 | self.features, |
1947 | | "`future.cancel-read` requires the component model async feature", |
1948 | 582 | offset, |
1949 | 0 | )?; |
1950 | 582 | if cancellable { |
1951 | 0 | require_feature::cm_more_async_builtins( |
1952 | 0 | self.features, |
1953 | | "async `future.cancel-read` requires the component model more async builtins feature", |
1954 | 0 | offset, |
1955 | 0 | )?; |
1956 | 582 | } |
1957 | | |
1958 | 582 | let ty = self.defined_type_at(ty, offset)?; |
1959 | 582 | let ComponentDefinedType::Future { .. } = &types[ty] else { |
1960 | 0 | bail!(offset, "`future.cancel-read` requires a future type") |
1961 | | }; |
1962 | | |
1963 | 582 | self.core_funcs |
1964 | 582 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1965 | 582 | Ok(()) |
1966 | 582 | } |
1967 | | |
1968 | 582 | fn future_cancel_write( |
1969 | 582 | &mut self, |
1970 | 582 | ty: u32, |
1971 | 582 | cancellable: bool, |
1972 | 582 | types: &mut TypeAlloc, |
1973 | 582 | offset: usize, |
1974 | 582 | ) -> Result<()> { |
1975 | 582 | require_feature::cm_async( |
1976 | 582 | self.features, |
1977 | | "`future.cancel-write` requires the component model async feature", |
1978 | 582 | offset, |
1979 | 0 | )?; |
1980 | 582 | if cancellable { |
1981 | 0 | require_feature::cm_more_async_builtins( |
1982 | 0 | self.features, |
1983 | | "async `future.cancel-write` requires the component model more async builtins feature", |
1984 | 0 | offset, |
1985 | 0 | )?; |
1986 | 582 | } |
1987 | | |
1988 | 582 | let ty = self.defined_type_at(ty, offset)?; |
1989 | 582 | let ComponentDefinedType::Future { .. } = &types[ty] else { |
1990 | 0 | bail!(offset, "`future.cancel-write` requires a future type") |
1991 | | }; |
1992 | | |
1993 | 582 | self.core_funcs |
1994 | 582 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1995 | 582 | Ok(()) |
1996 | 582 | } |
1997 | | |
1998 | 582 | fn future_drop_readable( |
1999 | 582 | &mut self, |
2000 | 582 | ty: u32, |
2001 | 582 | types: &mut TypeAlloc, |
2002 | 582 | offset: usize, |
2003 | 582 | ) -> Result<()> { |
2004 | 582 | require_feature::cm_async( |
2005 | 582 | self.features, |
2006 | | "`future.drop-readable` requires the component model async feature", |
2007 | 582 | offset, |
2008 | 0 | )?; |
2009 | | |
2010 | 582 | let ty = self.defined_type_at(ty, offset)?; |
2011 | 582 | let ComponentDefinedType::Future { .. } = &types[ty] else { |
2012 | 0 | bail!(offset, "`future.drop-readable` requires a future type") |
2013 | | }; |
2014 | | |
2015 | 582 | self.core_funcs |
2016 | 582 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
2017 | 582 | Ok(()) |
2018 | 582 | } |
2019 | | |
2020 | 582 | fn future_drop_writable( |
2021 | 582 | &mut self, |
2022 | 582 | ty: u32, |
2023 | 582 | types: &mut TypeAlloc, |
2024 | 582 | offset: usize, |
2025 | 582 | ) -> Result<()> { |
2026 | 582 | require_feature::cm_async( |
2027 | 582 | self.features, |
2028 | | "`future.drop-writable` requires the component model async feature", |
2029 | 582 | offset, |
2030 | 0 | )?; |
2031 | | |
2032 | 582 | let ty = self.defined_type_at(ty, offset)?; |
2033 | 582 | let ComponentDefinedType::Future { .. } = &types[ty] else { |
2034 | 0 | bail!(offset, "`future.drop-writable` requires a future type") |
2035 | | }; |
2036 | | |
2037 | 582 | self.core_funcs |
2038 | 582 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
2039 | 582 | Ok(()) |
2040 | 582 | } |
2041 | | |
2042 | 0 | fn error_context_new( |
2043 | 0 | &mut self, |
2044 | 0 | options: Vec<CanonicalOption>, |
2045 | 0 | types: &mut TypeAlloc, |
2046 | 0 | offset: usize, |
2047 | 0 | ) -> Result<()> { |
2048 | 0 | require_feature::cm_error_context( |
2049 | 0 | self.features, |
2050 | | "`error-context.new` requires the component model error-context feature", |
2051 | 0 | offset, |
2052 | 0 | )?; |
2053 | | |
2054 | 0 | let ty_id = self |
2055 | 0 | .check_options(types, &options, offset)? |
2056 | 0 | .require_memory(offset)? |
2057 | 0 | .require_sync(offset, "error-context.new")? |
2058 | 0 | .check_lower(offset)? |
2059 | 0 | .check_core_type( |
2060 | 0 | types, |
2061 | 0 | FuncType::new([ValType::I32; 2], [ValType::I32]), |
2062 | 0 | offset, |
2063 | 0 | )?; |
2064 | | |
2065 | 0 | self.core_funcs.push(ty_id); |
2066 | 0 | Ok(()) |
2067 | 0 | } |
2068 | | |
2069 | 0 | fn error_context_debug_message( |
2070 | 0 | &mut self, |
2071 | 0 | options: Vec<CanonicalOption>, |
2072 | 0 | types: &mut TypeAlloc, |
2073 | 0 | offset: usize, |
2074 | 0 | ) -> Result<()> { |
2075 | 0 | require_feature::cm_error_context( |
2076 | 0 | self.features, |
2077 | | "`error-context.debug-message` requires the component model error-context feature", |
2078 | 0 | offset, |
2079 | 0 | )?; |
2080 | | |
2081 | 0 | let ty_id = self |
2082 | 0 | .check_options(types, &options, offset)? |
2083 | 0 | .require_memory(offset)? |
2084 | 0 | .require_realloc(offset)? |
2085 | 0 | .require_sync(offset, "error-context.debug-message")? |
2086 | 0 | .check_lower(offset)? |
2087 | 0 | .check_core_type(types, FuncType::new([ValType::I32; 2], []), offset)?; |
2088 | | |
2089 | 0 | self.core_funcs.push(ty_id); |
2090 | 0 | Ok(()) |
2091 | 0 | } |
2092 | | |
2093 | 0 | fn error_context_drop(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
2094 | 0 | require_feature::cm_error_context( |
2095 | 0 | self.features, |
2096 | | "`error-context.drop` requires the component model error-context feature", |
2097 | 0 | offset, |
2098 | 0 | )?; |
2099 | | |
2100 | 0 | self.core_funcs |
2101 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
2102 | 0 | Ok(()) |
2103 | 0 | } |
2104 | | |
2105 | 1.54k | fn waitable_set_new(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
2106 | 1.54k | require_feature::cm_async( |
2107 | 1.54k | self.features, |
2108 | | "`waitable-set.new` requires the component model async feature", |
2109 | 1.54k | offset, |
2110 | 0 | )?; |
2111 | | |
2112 | 1.54k | self.core_funcs |
2113 | 1.54k | .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); |
2114 | 1.54k | Ok(()) |
2115 | 1.54k | } |
2116 | | |
2117 | 1.54k | fn waitable_set_wait( |
2118 | 1.54k | &mut self, |
2119 | 1.54k | memory: u32, |
2120 | 1.54k | types: &mut TypeAlloc, |
2121 | 1.54k | offset: usize, |
2122 | 1.54k | ) -> Result<()> { |
2123 | 1.54k | require_feature::cm_async( |
2124 | 1.54k | self.features, |
2125 | | "`waitable-set.wait` requires the component model async feature", |
2126 | 1.54k | offset, |
2127 | 0 | )?; |
2128 | | |
2129 | 1.54k | self.cabi_memory_at(memory, offset)?; |
2130 | 1.54k | let memory64 = self.memory_at(memory, offset)?.memory64; |
2131 | 1.54k | let ty = if memory64 { ValType::I64 } else { ValType::I32 }; |
2132 | 1.54k | self.core_funcs.push( |
2133 | 1.54k | types.intern_func_type(FuncType::new([ValType::I32, ty], [ValType::I32]), offset), |
2134 | | ); |
2135 | 1.54k | Ok(()) |
2136 | 1.54k | } |
2137 | | |
2138 | 1.54k | fn waitable_set_poll( |
2139 | 1.54k | &mut self, |
2140 | 1.54k | memory: u32, |
2141 | 1.54k | types: &mut TypeAlloc, |
2142 | 1.54k | offset: usize, |
2143 | 1.54k | ) -> Result<()> { |
2144 | 1.54k | require_feature::cm_async( |
2145 | 1.54k | self.features, |
2146 | | "`waitable-set.poll` requires the component model async feature", |
2147 | 1.54k | offset, |
2148 | 0 | )?; |
2149 | | |
2150 | 1.54k | self.cabi_memory_at(memory, offset)?; |
2151 | 1.54k | let memory64 = self.memory_at(memory, offset)?.memory64; |
2152 | 1.54k | let ty = if memory64 { ValType::I64 } else { ValType::I32 }; |
2153 | 1.54k | self.core_funcs.push( |
2154 | 1.54k | types.intern_func_type(FuncType::new([ValType::I32, ty], [ValType::I32]), offset), |
2155 | | ); |
2156 | 1.54k | Ok(()) |
2157 | 1.54k | } |
2158 | | |
2159 | 1.54k | fn waitable_set_drop(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
2160 | 1.54k | require_feature::cm_async( |
2161 | 1.54k | self.features, |
2162 | | "`waitable-set.drop` requires the component model async feature", |
2163 | 1.54k | offset, |
2164 | 0 | )?; |
2165 | | |
2166 | 1.54k | self.core_funcs |
2167 | 1.54k | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
2168 | 1.54k | Ok(()) |
2169 | 1.54k | } |
2170 | | |
2171 | 1.54k | fn waitable_join(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
2172 | 1.54k | require_feature::cm_async( |
2173 | 1.54k | self.features, |
2174 | | "`waitable.join` requires the component model async feature", |
2175 | 1.54k | offset, |
2176 | 0 | )?; |
2177 | | |
2178 | 1.54k | self.core_funcs |
2179 | 1.54k | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset)); |
2180 | 1.54k | Ok(()) |
2181 | 1.54k | } |
2182 | | |
2183 | 0 | fn thread_index(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
2184 | 0 | require_feature::cm_threading( |
2185 | 0 | self.features, |
2186 | | "`thread.index` requires the component model threading feature", |
2187 | 0 | offset, |
2188 | 0 | )?; |
2189 | | |
2190 | 0 | self.core_funcs |
2191 | 0 | .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); |
2192 | 0 | Ok(()) |
2193 | 0 | } |
2194 | | |
2195 | 0 | fn thread_new_indirect( |
2196 | 0 | &mut self, |
2197 | 0 | func_ty_index: u32, |
2198 | 0 | table_index: u32, |
2199 | 0 | types: &mut TypeAlloc, |
2200 | 0 | offset: usize, |
2201 | 0 | ) -> Result<()> { |
2202 | 0 | require_feature::cm_threading( |
2203 | 0 | self.features, |
2204 | | "`thread.new-indirect` requires the component model threading feature", |
2205 | 0 | offset, |
2206 | 0 | )?; |
2207 | | |
2208 | 0 | let core_type_id = match self.core_type_at(func_ty_index, offset)? { |
2209 | 0 | ComponentCoreTypeId::Sub(c) => c, |
2210 | 0 | ComponentCoreTypeId::Module(_) => bail!(offset, "expected a core function type"), |
2211 | | }; |
2212 | 0 | let sub_ty = &types[core_type_id]; |
2213 | 0 | match &sub_ty.composite_type.inner { |
2214 | 0 | CompositeInnerType::Func(func_ty) => { |
2215 | 0 | if func_ty.params() != [ValType::I32] { |
2216 | 0 | bail!( |
2217 | 0 | offset, |
2218 | | "start function must take a single `i32` argument (currently)" |
2219 | | ); |
2220 | 0 | } |
2221 | 0 | if func_ty.results() != [] { |
2222 | 0 | bail!(offset, "start function must not return any values"); |
2223 | 0 | } |
2224 | | } |
2225 | 0 | _ => bail!(offset, "start type must be a function"), |
2226 | | } |
2227 | | |
2228 | 0 | let table = self.table_at(table_index, offset)?; |
2229 | | |
2230 | 0 | SubtypeCx::table_type( |
2231 | 0 | table, |
2232 | 0 | &TableType { |
2233 | 0 | initial: 0, |
2234 | 0 | maximum: None, |
2235 | 0 | table64: false, |
2236 | 0 | shared: false, |
2237 | 0 | element_type: RefType::FUNCREF, |
2238 | 0 | }, |
2239 | 0 | offset, |
2240 | | ) |
2241 | 0 | .map_err(|mut e| { |
2242 | 0 | e.add_context("table is not a 32-bit table of (ref null (func))".into()); |
2243 | 0 | e |
2244 | 0 | })?; |
2245 | | |
2246 | 0 | self.core_funcs.push(types.intern_func_type( |
2247 | 0 | FuncType::new([ValType::I32, ValType::I32], [ValType::I32]), |
2248 | 0 | offset, |
2249 | 0 | )); |
2250 | 0 | Ok(()) |
2251 | 0 | } |
2252 | | |
2253 | 0 | fn thread_resume_later(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
2254 | 0 | require_feature::cm_threading( |
2255 | 0 | self.features, |
2256 | | "`thread.resume-later` requires the component model threading feature", |
2257 | 0 | offset, |
2258 | 0 | )?; |
2259 | 0 | self.core_funcs |
2260 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
2261 | 0 | Ok(()) |
2262 | 0 | } |
2263 | | |
2264 | 0 | fn thread_suspend( |
2265 | 0 | &mut self, |
2266 | 0 | _cancellable: bool, |
2267 | 0 | types: &mut TypeAlloc, |
2268 | 0 | offset: usize, |
2269 | 0 | ) -> Result<()> { |
2270 | 0 | require_feature::cm_threading( |
2271 | 0 | self.features, |
2272 | | "`thread.suspend` requires the component model threading feature", |
2273 | 0 | offset, |
2274 | 0 | )?; |
2275 | 0 | self.core_funcs |
2276 | 0 | .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); |
2277 | 0 | Ok(()) |
2278 | 0 | } |
2279 | | |
2280 | 1.54k | fn thread_yield(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
2281 | 1.54k | require_feature::cm_async( |
2282 | 1.54k | self.features, |
2283 | | "`thread.yield` requires the component model async feature", |
2284 | 1.54k | offset, |
2285 | 0 | )?; |
2286 | | |
2287 | 1.54k | self.core_funcs |
2288 | 1.54k | .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); |
2289 | 1.54k | Ok(()) |
2290 | 1.54k | } |
2291 | | |
2292 | 0 | fn thread_suspend_then_resume( |
2293 | 0 | &mut self, |
2294 | 0 | _cancellable: bool, |
2295 | 0 | types: &mut TypeAlloc, |
2296 | 0 | offset: usize, |
2297 | 0 | ) -> Result<()> { |
2298 | 0 | require_feature::cm_threading( |
2299 | 0 | self.features, |
2300 | | "`thread.suspend-then-resume` requires the component model threading feature", |
2301 | 0 | offset, |
2302 | 0 | )?; |
2303 | | |
2304 | 0 | self.core_funcs |
2305 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
2306 | 0 | Ok(()) |
2307 | 0 | } |
2308 | | |
2309 | 0 | fn thread_yield_then_resume( |
2310 | 0 | &mut self, |
2311 | 0 | _cancellable: bool, |
2312 | 0 | types: &mut TypeAlloc, |
2313 | 0 | offset: usize, |
2314 | 0 | ) -> Result<()> { |
2315 | 0 | require_feature::cm_threading( |
2316 | 0 | self.features, |
2317 | | "`thread.yield-then-resume` requires the component model threading feature", |
2318 | 0 | offset, |
2319 | 0 | )?; |
2320 | 0 | self.core_funcs |
2321 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
2322 | 0 | Ok(()) |
2323 | 0 | } |
2324 | | |
2325 | 0 | fn thread_suspend_then_promote( |
2326 | 0 | &mut self, |
2327 | 0 | _cancellable: bool, |
2328 | 0 | types: &mut TypeAlloc, |
2329 | 0 | offset: usize, |
2330 | 0 | ) -> Result<()> { |
2331 | 0 | require_feature::cm_threading( |
2332 | 0 | self.features, |
2333 | | "`thread.suspend-then-promote` requires the component model threading feature", |
2334 | 0 | offset, |
2335 | 0 | )?; |
2336 | 0 | self.core_funcs |
2337 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
2338 | 0 | Ok(()) |
2339 | 0 | } |
2340 | | |
2341 | 0 | fn thread_yield_then_promote( |
2342 | 0 | &mut self, |
2343 | 0 | _cancellable: bool, |
2344 | 0 | types: &mut TypeAlloc, |
2345 | 0 | offset: usize, |
2346 | 0 | ) -> Result<()> { |
2347 | 0 | require_feature::cm_threading( |
2348 | 0 | self.features, |
2349 | | "`thread.yield-then-promote` requires the component model threading feature", |
2350 | 0 | offset, |
2351 | 0 | )?; |
2352 | 0 | self.core_funcs |
2353 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
2354 | 0 | Ok(()) |
2355 | 0 | } |
2356 | | |
2357 | 1.27k | fn check_local_resource(&self, idx: u32, types: &TypeList, offset: usize) -> Result<ValType> { |
2358 | 1.27k | let resource = self.resource_at(idx, types, offset)?; |
2359 | 1.27k | match self |
2360 | 1.27k | .defined_resources |
2361 | 1.27k | .get(&resource.resource()) |
2362 | 1.27k | .and_then(|rep| *rep) |
2363 | | { |
2364 | 1.27k | Some(ty) => Ok(ty), |
2365 | 0 | None => bail!(offset, "type {idx} is not a local resource"), |
2366 | | } |
2367 | 1.27k | } |
2368 | | |
2369 | 6.53k | fn resource_at<'a>( |
2370 | 6.53k | &self, |
2371 | 6.53k | idx: u32, |
2372 | 6.53k | _types: &'a TypeList, |
2373 | 6.53k | offset: usize, |
2374 | 6.53k | ) -> Result<AliasableResourceId> { |
2375 | 6.53k | if let ComponentAnyTypeId::Resource(id) = self.component_type_at(idx, offset)? { |
2376 | 6.53k | return Ok(id); |
2377 | 0 | } |
2378 | 0 | bail!(offset, "type index {} is not a resource type", idx) |
2379 | 6.53k | } |
2380 | | |
2381 | 0 | fn thread_spawn_ref( |
2382 | 0 | &mut self, |
2383 | 0 | func_ty_index: u32, |
2384 | 0 | types: &mut TypeAlloc, |
2385 | 0 | offset: usize, |
2386 | 0 | ) -> Result<()> { |
2387 | 0 | require_feature::shared_everything_threads( |
2388 | 0 | self.features, |
2389 | | "`thread.spawn-ref` requires the shared-everything-threads proposal", |
2390 | 0 | offset, |
2391 | 0 | )?; |
2392 | 0 | let core_type_id = self.validate_spawn_type(func_ty_index, types, offset)?; |
2393 | | |
2394 | | // Insert the core function. |
2395 | 0 | let packed_index = PackedIndex::from_id(core_type_id).ok_or_else(|| { |
2396 | 0 | format_err!(offset, "implementation limit: too many types in `TypeList`") |
2397 | 0 | })?; |
2398 | 0 | let start_func_ref = RefType::concrete(true, packed_index); |
2399 | 0 | let func_ty = FuncType::new([ValType::Ref(start_func_ref), ValType::I32], [ValType::I32]); |
2400 | 0 | let core_ty = SubType::func(func_ty, true); |
2401 | 0 | let id = types.intern_sub_type(core_ty, offset); |
2402 | 0 | self.core_funcs.push(id); |
2403 | | |
2404 | 0 | Ok(()) |
2405 | 0 | } |
2406 | | |
2407 | 0 | fn thread_spawn_indirect( |
2408 | 0 | &mut self, |
2409 | 0 | func_ty_index: u32, |
2410 | 0 | table_index: u32, |
2411 | 0 | types: &mut TypeAlloc, |
2412 | 0 | offset: usize, |
2413 | 0 | ) -> Result<()> { |
2414 | 0 | require_feature::shared_everything_threads( |
2415 | 0 | self.features, |
2416 | | "`thread.spawn-indirect` requires the shared-everything-threads proposal", |
2417 | 0 | offset, |
2418 | 0 | )?; |
2419 | 0 | let _ = self.validate_spawn_type(func_ty_index, types, offset)?; |
2420 | | |
2421 | | // Check this much like `call_indirect` (see |
2422 | | // `OperatorValidatorTemp::check_call_indirect_ty`), but loosen the |
2423 | | // table type restrictions to just a `funcref`. See the component model |
2424 | | // for more details: |
2425 | | // https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#-canon-threadspawn-indirect. |
2426 | 0 | let table = self.table_at(table_index, offset)?; |
2427 | | |
2428 | 0 | SubtypeCx::table_type( |
2429 | 0 | table, |
2430 | 0 | &TableType { |
2431 | 0 | initial: 0, |
2432 | 0 | maximum: None, |
2433 | 0 | table64: false, |
2434 | 0 | shared: true, |
2435 | 0 | element_type: RefType::FUNCREF |
2436 | 0 | .shared() |
2437 | 0 | .expect("a funcref can always be shared"), |
2438 | 0 | }, |
2439 | 0 | offset, |
2440 | | ) |
2441 | 0 | .map_err(|mut e| { |
2442 | 0 | e.add_context("table is not a 32-bit shared table of (ref null (shared func))".into()); |
2443 | 0 | e |
2444 | 0 | })?; |
2445 | | |
2446 | | // Insert the core function. |
2447 | 0 | let func_ty = FuncType::new([ValType::I32, ValType::I32], [ValType::I32]); |
2448 | 0 | let core_ty = SubType::func(func_ty, true); |
2449 | 0 | let id = types.intern_sub_type(core_ty, offset); |
2450 | 0 | self.core_funcs.push(id); |
2451 | | |
2452 | 0 | Ok(()) |
2453 | 0 | } |
2454 | | |
2455 | | /// Validates the type of a `thread.spawn*` instruction. |
2456 | | /// |
2457 | | /// This is currently limited to shared functions with the signature `[i32] |
2458 | | /// -> []`. See component model [explanation] for more details. |
2459 | | /// |
2460 | | /// [explanation]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#-canon-threadspawn-ref |
2461 | 0 | fn validate_spawn_type( |
2462 | 0 | &self, |
2463 | 0 | func_ty_index: u32, |
2464 | 0 | types: &TypeAlloc, |
2465 | 0 | offset: usize, |
2466 | 0 | ) -> Result<CoreTypeId> { |
2467 | 0 | let core_type_id = match self.core_type_at(func_ty_index, offset)? { |
2468 | 0 | ComponentCoreTypeId::Sub(c) => c, |
2469 | 0 | ComponentCoreTypeId::Module(_) => bail!(offset, "expected a core function type"), |
2470 | | }; |
2471 | 0 | let sub_ty = &types[core_type_id]; |
2472 | 0 | if !sub_ty.composite_type.shared { |
2473 | 0 | bail!(offset, "spawn type must be shared"); |
2474 | 0 | } |
2475 | 0 | match &sub_ty.composite_type.inner { |
2476 | 0 | CompositeInnerType::Func(func_ty) => { |
2477 | 0 | if func_ty.params() != [ValType::I32] { |
2478 | 0 | bail!( |
2479 | 0 | offset, |
2480 | | "spawn function must take a single `i32` argument (currently)" |
2481 | | ); |
2482 | 0 | } |
2483 | 0 | if func_ty.results() != [] { |
2484 | 0 | bail!(offset, "spawn function must not return any values"); |
2485 | 0 | } |
2486 | | } |
2487 | 0 | _ => bail!(offset, "spawn type must be a function"), |
2488 | | } |
2489 | 0 | Ok(core_type_id) |
2490 | 0 | } |
2491 | | |
2492 | 0 | fn thread_available_parallelism(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { |
2493 | 0 | require_feature::shared_everything_threads( |
2494 | 0 | self.features, |
2495 | | "`thread.available_parallelism` requires the shared-everything-threads proposal", |
2496 | 0 | offset, |
2497 | 0 | )?; |
2498 | | |
2499 | 0 | let func_ty = FuncType::new([], [ValType::I32]); |
2500 | 0 | let core_ty = SubType::func(func_ty, true); |
2501 | 0 | let id = types.intern_sub_type(core_ty, offset); |
2502 | 0 | self.core_funcs.push(id); |
2503 | | |
2504 | 0 | Ok(()) |
2505 | 0 | } |
2506 | | |
2507 | 5.16k | pub fn add_component(&mut self, component: ComponentType, types: &mut TypeAlloc) -> Result<()> { |
2508 | 5.16k | let id = types.push_ty(component); |
2509 | 5.16k | self.components.push(id); |
2510 | 5.16k | Ok(()) |
2511 | 5.16k | } |
2512 | | |
2513 | 5.16k | pub fn add_instance( |
2514 | 5.16k | &mut self, |
2515 | 5.16k | instance: crate::ComponentInstance, |
2516 | 5.16k | types: &mut TypeAlloc, |
2517 | 5.16k | offset: usize, |
2518 | 5.16k | ) -> Result<()> { |
2519 | 5.16k | let instance = match instance { |
2520 | | crate::ComponentInstance::Instantiate { |
2521 | 5.16k | component_index, |
2522 | 5.16k | args, |
2523 | 5.16k | } => self.instantiate_component(component_index, args.into_vec(), types, offset)?, |
2524 | 0 | crate::ComponentInstance::FromExports(exports) => { |
2525 | 0 | self.instantiate_component_exports(exports.into_vec(), types, offset)? |
2526 | | } |
2527 | | }; |
2528 | | |
2529 | 5.16k | self.instances.push(instance); |
2530 | | |
2531 | 5.16k | Ok(()) |
2532 | 5.16k | } |
2533 | | |
2534 | 66.6k | pub fn add_alias( |
2535 | 66.6k | components: &mut [Self], |
2536 | 66.6k | alias: crate::ComponentAlias, |
2537 | 66.6k | types: &mut TypeAlloc, |
2538 | 66.6k | offset: usize, |
2539 | 66.6k | ) -> Result<()> { |
2540 | 66.6k | let component = components.last_mut().unwrap(); |
2541 | | |
2542 | 66.6k | match component.kind { |
2543 | | // For a component itself all alias kind are allowed. |
2544 | 48.7k | ComponentKind::Component => {} |
2545 | | |
2546 | | // For instance/component types only aliases to `type` or `instance` |
2547 | | // items are allowed, and all other aliases are rejected. |
2548 | 7.67k | ComponentKind::InstanceType | ComponentKind::ComponentType => match &alias { |
2549 | | crate::ComponentAlias::InstanceExport { |
2550 | | kind: ComponentExternalKind::Type | ComponentExternalKind::Instance, |
2551 | | .. |
2552 | | } |
2553 | | | crate::ComponentAlias::Outer { |
2554 | | kind: ComponentOuterAliasKind::Type | ComponentOuterAliasKind::CoreType, |
2555 | | .. |
2556 | 17.8k | } => {} |
2557 | | |
2558 | 0 | _ => bail!( |
2559 | 0 | offset, |
2560 | | "aliases in a component or instance type may only refer to types or instances" |
2561 | | ), |
2562 | | }, |
2563 | | } |
2564 | | |
2565 | 66.6k | match alias { |
2566 | | crate::ComponentAlias::InstanceExport { |
2567 | 11.3k | instance_index, |
2568 | 11.3k | kind, |
2569 | 11.3k | name, |
2570 | 11.3k | } => component.alias_instance_export(instance_index, kind, name, types, offset), |
2571 | | crate::ComponentAlias::CoreInstanceExport { |
2572 | 45.1k | instance_index, |
2573 | 45.1k | kind, |
2574 | 45.1k | name, |
2575 | 45.1k | } => component.alias_core_instance_export(instance_index, kind, name, types, offset), |
2576 | 10.1k | crate::ComponentAlias::Outer { kind, count, index } => match kind { |
2577 | | ComponentOuterAliasKind::CoreModule => { |
2578 | 0 | Self::alias_module(components, count, index, offset) |
2579 | | } |
2580 | | ComponentOuterAliasKind::CoreType => { |
2581 | 0 | Self::alias_core_type(components, count, index, offset) |
2582 | | } |
2583 | | ComponentOuterAliasKind::Type => { |
2584 | 10.1k | Self::alias_type(components, count, index, types, offset) |
2585 | | } |
2586 | | ComponentOuterAliasKind::Component => { |
2587 | 0 | Self::alias_component(components, count, index, offset) |
2588 | | } |
2589 | | }, |
2590 | | } |
2591 | 66.6k | } |
2592 | | |
2593 | 0 | pub fn add_start( |
2594 | 0 | &mut self, |
2595 | 0 | func_index: u32, |
2596 | 0 | args: &[u32], |
2597 | 0 | results: u32, |
2598 | 0 | types: &mut TypeList, |
2599 | 0 | offset: usize, |
2600 | 0 | ) -> Result<()> { |
2601 | 0 | require_feature::cm_values( |
2602 | 0 | self.features, |
2603 | | "support for component model `value`s is not enabled", |
2604 | 0 | offset, |
2605 | 0 | )?; |
2606 | 0 | if self.has_start { |
2607 | 0 | return Err(Error::new( |
2608 | 0 | "component cannot have more than one start function", |
2609 | 0 | offset, |
2610 | 0 | )); |
2611 | 0 | } |
2612 | | |
2613 | 0 | let ft = &types[self.function_at(func_index, offset)?]; |
2614 | | |
2615 | 0 | if ft.params.len() != args.len() { |
2616 | 0 | bail!( |
2617 | 0 | offset, |
2618 | | "component start function requires {} arguments but was given {}", |
2619 | 0 | ft.params.len(), |
2620 | 0 | args.len() |
2621 | | ); |
2622 | 0 | } |
2623 | | |
2624 | 0 | if u32::from(ft.result.is_some()) != results { |
2625 | 0 | bail!( |
2626 | 0 | offset, |
2627 | | "component start function has a result count of {results} \ |
2628 | | but the function type has a result count of {type_results}", |
2629 | 0 | type_results = u32::from(ft.result.is_some()), |
2630 | | ); |
2631 | 0 | } |
2632 | | |
2633 | 0 | let cx = SubtypeCx::new(types, types); |
2634 | 0 | for (i, ((_, ty), arg)) in ft.params.iter().zip(args).enumerate() { |
2635 | | // Ensure the value's type is a subtype of the parameter type |
2636 | 0 | cx.component_val_type(self.value_at(*arg, offset)?, ty, offset) |
2637 | 0 | .with_context(|| { |
2638 | 0 | format!("value type mismatch for component start function argument {i}") |
2639 | 0 | })?; |
2640 | | } |
2641 | | |
2642 | 0 | if let Some(ty) = ft.result { |
2643 | 0 | self.values.push((ty, false)); |
2644 | 0 | } |
2645 | | |
2646 | 0 | self.has_start = true; |
2647 | | |
2648 | 0 | Ok(()) |
2649 | 0 | } |
2650 | | |
2651 | 17.1k | fn check_options( |
2652 | 17.1k | &self, |
2653 | 17.1k | types: &TypeList, |
2654 | 17.1k | options: &[CanonicalOption], |
2655 | 17.1k | offset: usize, |
2656 | 17.1k | ) -> Result<CanonicalOptions> { |
2657 | 0 | fn display(option: CanonicalOption) -> &'static str { |
2658 | 0 | match option { |
2659 | 0 | CanonicalOption::UTF8 => "utf8", |
2660 | 0 | CanonicalOption::UTF16 => "utf16", |
2661 | 0 | CanonicalOption::CompactUTF16 => "latin1-utf16", |
2662 | 0 | CanonicalOption::Memory(_) => "memory", |
2663 | 0 | CanonicalOption::Realloc(_) => "realloc", |
2664 | 0 | CanonicalOption::PostReturn(_) => "post-return", |
2665 | 0 | CanonicalOption::Async => "async", |
2666 | 0 | CanonicalOption::Callback(_) => "callback", |
2667 | 0 | CanonicalOption::CoreType(_) => "core-type", |
2668 | 0 | CanonicalOption::Gc => "gc", |
2669 | | } |
2670 | 0 | } |
2671 | | |
2672 | 17.1k | let mut encoding = None; |
2673 | 17.1k | let mut memory = None; |
2674 | 17.1k | let mut realloc = None; |
2675 | 17.1k | let mut post_return = None; |
2676 | 17.1k | let mut is_async = false; |
2677 | 17.1k | let mut callback = None; |
2678 | 17.1k | let mut core_type = None; |
2679 | 17.1k | let mut gc = false; |
2680 | | |
2681 | 17.1k | for option in options { |
2682 | 16.3k | match option { |
2683 | | CanonicalOption::UTF8 | CanonicalOption::UTF16 | CanonicalOption::CompactUTF16 => { |
2684 | 972 | match encoding { |
2685 | 0 | Some(existing) => { |
2686 | 0 | bail!( |
2687 | 0 | offset, |
2688 | | "canonical encoding option `{existing}` conflicts with option `{}`", |
2689 | 0 | display(*option), |
2690 | | ) |
2691 | | } |
2692 | | None => { |
2693 | 972 | encoding = Some(match option { |
2694 | 972 | CanonicalOption::UTF8 => StringEncoding::Utf8, |
2695 | 0 | CanonicalOption::UTF16 => StringEncoding::Utf16, |
2696 | 0 | CanonicalOption::CompactUTF16 => StringEncoding::CompactUtf16, |
2697 | 0 | _ => unreachable!(), |
2698 | | }); |
2699 | | } |
2700 | | } |
2701 | | } |
2702 | 2.25k | CanonicalOption::Memory(idx) => { |
2703 | 2.25k | memory = match memory { |
2704 | | None => { |
2705 | 2.25k | let ptr_size = self.cabi_memory_at(*idx, offset)?; |
2706 | 2.25k | Some((*idx, ptr_size)) |
2707 | | } |
2708 | | Some(_) => { |
2709 | 0 | return Err(Error::new( |
2710 | 0 | "canonical option `memory` is specified more than once", |
2711 | 0 | offset, |
2712 | 0 | )); |
2713 | | } |
2714 | | } |
2715 | | } |
2716 | 975 | CanonicalOption::Realloc(idx) => { |
2717 | 975 | realloc = match realloc { |
2718 | | None => { |
2719 | | // Validation deferred because it may depend on the memory option. |
2720 | 975 | Some(*idx) |
2721 | | } |
2722 | | Some(_) => { |
2723 | 0 | return Err(Error::new( |
2724 | 0 | "canonical option `realloc` is specified more than once", |
2725 | 0 | offset, |
2726 | 0 | )); |
2727 | | } |
2728 | | } |
2729 | | } |
2730 | 6.53k | CanonicalOption::PostReturn(idx) => { |
2731 | 6.53k | post_return = match post_return { |
2732 | 6.53k | None => Some(*idx), |
2733 | | Some(_) => { |
2734 | 0 | return Err(Error::new( |
2735 | 0 | "canonical option `post-return` is specified more than once", |
2736 | 0 | offset, |
2737 | 0 | )); |
2738 | | } |
2739 | | } |
2740 | | } |
2741 | | CanonicalOption::Async => { |
2742 | 3.12k | if is_async { |
2743 | 0 | return Err(Error::new( |
2744 | 0 | "canonical option `async` is specified more than once", |
2745 | 0 | offset, |
2746 | 0 | )); |
2747 | | } else { |
2748 | 3.12k | require_feature::cm_async( |
2749 | 3.12k | self.features, |
2750 | | "canonical option `async` requires the component model async feature", |
2751 | 3.12k | offset, |
2752 | 0 | )?; |
2753 | | |
2754 | 3.12k | is_async = true; |
2755 | | } |
2756 | | } |
2757 | 2.53k | CanonicalOption::Callback(idx) => { |
2758 | 2.53k | callback = match callback { |
2759 | 2.53k | None => Some(*idx), |
2760 | | Some(_) => { |
2761 | 0 | return Err(Error::new( |
2762 | 0 | "canonical option `callback` is specified more than once", |
2763 | 0 | offset, |
2764 | 0 | )); |
2765 | | } |
2766 | | } |
2767 | | } |
2768 | 0 | CanonicalOption::CoreType(idx) => { |
2769 | 0 | core_type = match core_type { |
2770 | | None => { |
2771 | 0 | require_feature::cm_gc( |
2772 | 0 | self.features, |
2773 | | "canonical option `core type` requires the component model gc feature", |
2774 | 0 | offset, |
2775 | 0 | )?; |
2776 | 0 | let ty = match self.core_type_at(*idx, offset)? { |
2777 | 0 | ComponentCoreTypeId::Sub(ty) => ty, |
2778 | | ComponentCoreTypeId::Module(_) => { |
2779 | 0 | return Err(Error::new( |
2780 | 0 | "canonical option `core type` must reference a core function \ |
2781 | 0 | type", |
2782 | 0 | offset, |
2783 | 0 | )); |
2784 | | } |
2785 | | }; |
2786 | 0 | match &types[ty].composite_type.inner { |
2787 | 0 | CompositeInnerType::Func(_) => {} |
2788 | | CompositeInnerType::Array(_) |
2789 | | | CompositeInnerType::Struct(_) |
2790 | | | CompositeInnerType::Cont(_) => { |
2791 | 0 | return Err(Error::new( |
2792 | 0 | "canonical option `core type` must reference a core function \ |
2793 | 0 | type", |
2794 | 0 | offset, |
2795 | 0 | )); |
2796 | | } |
2797 | | } |
2798 | 0 | Some(ty) |
2799 | | } |
2800 | | Some(_) => { |
2801 | 0 | return Err(Error::new( |
2802 | 0 | "canonical option `core type` is specified more than once", |
2803 | 0 | offset, |
2804 | 0 | )); |
2805 | | } |
2806 | | }; |
2807 | | } |
2808 | | CanonicalOption::Gc => { |
2809 | 0 | if gc { |
2810 | 0 | return Err(Error::new( |
2811 | 0 | "canonical option `gc` is specified more than once", |
2812 | 0 | offset, |
2813 | 0 | )); |
2814 | 0 | } |
2815 | 0 | require_feature::cm_gc( |
2816 | 0 | self.features, |
2817 | | "canonical option `gc` requires the `cm-gc` feature", |
2818 | 0 | offset, |
2819 | 0 | )?; |
2820 | 0 | gc = true; |
2821 | | } |
2822 | | } |
2823 | | } |
2824 | | |
2825 | 17.1k | let string_encoding = encoding.unwrap_or_default(); |
2826 | | |
2827 | 17.1k | let concurrency = match (is_async, callback, post_return.is_some()) { |
2828 | | (false, Some(_), _) => { |
2829 | 0 | bail!(offset, "cannot specify callback without async") |
2830 | | } |
2831 | | (true, _, true) => { |
2832 | 0 | bail!(offset, "cannot specify post-return function in async") |
2833 | | } |
2834 | 14.0k | (false, None, _) => Concurrency::Sync, |
2835 | 3.12k | (true, callback, false) => Concurrency::Async { callback }, |
2836 | | }; |
2837 | | |
2838 | 17.1k | if !gc && core_type.is_some() { |
2839 | 0 | bail!(offset, "cannot specify `core-type` without `gc`") |
2840 | 17.1k | } |
2841 | | |
2842 | | // Validate `realloc` |
2843 | 17.1k | if let Some(realloc_idx) = realloc { |
2844 | 975 | let addr_type = match memory { |
2845 | 975 | Some((_, ptr_size)) => ptr_size.core_type(), |
2846 | | None => { |
2847 | 0 | return Err(Error::new( |
2848 | 0 | "canonical option `realloc` requires `memory` to also be specified", |
2849 | 0 | offset, |
2850 | 0 | )); |
2851 | | } |
2852 | | }; |
2853 | 975 | let ty_id = self.core_function_at(realloc_idx, offset)?; |
2854 | 975 | let func_ty = types[ty_id].unwrap_func(); |
2855 | 975 | if func_ty.params() != [addr_type, addr_type, addr_type, addr_type] |
2856 | 975 | || func_ty.results() != [addr_type] |
2857 | | { |
2858 | 0 | return Err(Error::new( |
2859 | 0 | "canonical option `realloc` uses a core function with an incorrect signature", |
2860 | 0 | offset, |
2861 | 0 | )); |
2862 | 975 | } |
2863 | 16.1k | } |
2864 | | |
2865 | 17.1k | Ok(CanonicalOptions { |
2866 | 17.1k | string_encoding, |
2867 | 17.1k | memory, |
2868 | 17.1k | realloc, |
2869 | 17.1k | post_return, |
2870 | 17.1k | concurrency, |
2871 | 17.1k | core_type, |
2872 | 17.1k | gc, |
2873 | 17.1k | }) |
2874 | 17.1k | } |
2875 | | |
2876 | 215k | fn check_type_ref( |
2877 | 215k | &mut self, |
2878 | 215k | ty: &ComponentTypeRef, |
2879 | 215k | types: &mut TypeAlloc, |
2880 | 215k | offset: usize, |
2881 | 215k | ) -> Result<ComponentEntityType> { |
2882 | 98.2k | Ok(match ty { |
2883 | 0 | ComponentTypeRef::Module(index) => { |
2884 | 0 | let id = self.core_type_at(*index, offset)?; |
2885 | 0 | match id { |
2886 | | ComponentCoreTypeId::Sub(_) => { |
2887 | 0 | bail!(offset, "core type index {index} is not a module type") |
2888 | | } |
2889 | 0 | ComponentCoreTypeId::Module(id) => ComponentEntityType::Module(id), |
2890 | | } |
2891 | | } |
2892 | 52.3k | ComponentTypeRef::Func(index) => { |
2893 | 52.3k | let id = self.component_type_at(*index, offset)?; |
2894 | 52.3k | match id { |
2895 | 52.3k | ComponentAnyTypeId::Func(id) => ComponentEntityType::Func(id), |
2896 | 0 | _ => bail!(offset, "type index {index} is not a function type"), |
2897 | | } |
2898 | | } |
2899 | 0 | ComponentTypeRef::Value(ty) => { |
2900 | 0 | self.check_value_support(offset)?; |
2901 | 0 | let ty = match ty { |
2902 | 0 | crate::ComponentValType::Primitive(ty) => ComponentValType::Primitive(*ty), |
2903 | 0 | crate::ComponentValType::Type(index) => { |
2904 | 0 | ComponentValType::Type(self.defined_type_at(*index, offset)?) |
2905 | | } |
2906 | | }; |
2907 | 0 | ComponentEntityType::Value(ty) |
2908 | | } |
2909 | 94.1k | ComponentTypeRef::Type(TypeBounds::Eq(index)) => { |
2910 | 94.1k | let referenced = self.component_type_at(*index, offset)?; |
2911 | 94.1k | let created = types.with_unique(referenced); |
2912 | 94.1k | ComponentEntityType::Type { |
2913 | 94.1k | referenced, |
2914 | 94.1k | created, |
2915 | 94.1k | } |
2916 | | } |
2917 | | ComponentTypeRef::Type(TypeBounds::SubResource) => { |
2918 | 4.12k | let id = types.alloc_resource_id(); |
2919 | 4.12k | ComponentEntityType::Type { |
2920 | 4.12k | referenced: id.into(), |
2921 | 4.12k | created: id.into(), |
2922 | 4.12k | } |
2923 | | } |
2924 | 51.3k | ComponentTypeRef::Instance(index) => { |
2925 | 51.3k | let id = self.component_type_at(*index, offset)?; |
2926 | 51.3k | match id { |
2927 | 51.3k | ComponentAnyTypeId::Instance(id) => ComponentEntityType::Instance(id), |
2928 | 0 | _ => bail!(offset, "type index {index} is not an instance type"), |
2929 | | } |
2930 | | } |
2931 | 13.6k | ComponentTypeRef::Component(index) => { |
2932 | 13.6k | let id = self.component_type_at(*index, offset)?; |
2933 | 13.6k | match id { |
2934 | 13.6k | ComponentAnyTypeId::Component(id) => ComponentEntityType::Component(id), |
2935 | 0 | _ => bail!(offset, "type index {index} is not a component type"), |
2936 | | } |
2937 | | } |
2938 | | }) |
2939 | 215k | } |
2940 | | |
2941 | 88.4k | pub fn export_to_entity_type( |
2942 | 88.4k | &mut self, |
2943 | 88.4k | export: &crate::ComponentExport, |
2944 | 88.4k | types: &mut TypeAlloc, |
2945 | 88.4k | offset: usize, |
2946 | 88.4k | ) -> Result<ComponentEntityType> { |
2947 | 88.4k | let actual = match export.kind { |
2948 | | ComponentExternalKind::Module => { |
2949 | 0 | ComponentEntityType::Module(self.module_at(export.index, offset)?) |
2950 | | } |
2951 | | ComponentExternalKind::Func => { |
2952 | 9.12k | ComponentEntityType::Func(self.function_at(export.index, offset)?) |
2953 | | } |
2954 | | ComponentExternalKind::Value => { |
2955 | 0 | self.check_value_support(offset)?; |
2956 | 0 | ComponentEntityType::Value(*self.value_at(export.index, offset)?) |
2957 | | } |
2958 | | ComponentExternalKind::Type => { |
2959 | 74.1k | let referenced = self.component_type_at(export.index, offset)?; |
2960 | 74.1k | let created = types.with_unique(referenced); |
2961 | 74.1k | ComponentEntityType::Type { |
2962 | 74.1k | referenced, |
2963 | 74.1k | created, |
2964 | 74.1k | } |
2965 | | } |
2966 | | ComponentExternalKind::Instance => { |
2967 | 5.16k | ComponentEntityType::Instance(self.instance_at(export.index, offset)?) |
2968 | | } |
2969 | | ComponentExternalKind::Component => { |
2970 | 0 | ComponentEntityType::Component(self.component_at(export.index, offset)?) |
2971 | | } |
2972 | | }; |
2973 | | |
2974 | 88.4k | let ascribed = match &export.ty { |
2975 | 8.44k | Some(ty) => self.check_type_ref(ty, types, offset)?, |
2976 | 79.9k | None => return Ok(actual), |
2977 | | }; |
2978 | | |
2979 | 8.44k | SubtypeCx::new(types, types) |
2980 | 8.44k | .component_entity_type(&actual, &ascribed, offset) |
2981 | 8.44k | .with_context(|| "ascribed type of export is not compatible with item's type")?; |
2982 | | |
2983 | 8.44k | Ok(ascribed) |
2984 | 88.4k | } |
2985 | | |
2986 | 0 | fn create_module_type( |
2987 | 0 | components: &[Self], |
2988 | 0 | decls: Vec<crate::ModuleTypeDeclaration>, |
2989 | 0 | types: &mut TypeAlloc, |
2990 | 0 | offset: usize, |
2991 | 0 | ) -> Result<ModuleType> { |
2992 | 0 | let mut state = Module::new(components[0].features); |
2993 | | |
2994 | 0 | for decl in decls { |
2995 | 0 | match decl { |
2996 | 0 | crate::ModuleTypeDeclaration::Type(rec) => { |
2997 | 0 | state.add_types(rec, types, offset, true)?; |
2998 | | } |
2999 | 0 | crate::ModuleTypeDeclaration::Export { name, mut ty } => { |
3000 | 0 | let ty = state.check_type_ref(&mut ty, types, offset)?; |
3001 | 0 | state.add_export(name, ty, offset, true, types)?; |
3002 | | } |
3003 | 0 | crate::ModuleTypeDeclaration::OuterAlias { kind, count, index } => { |
3004 | 0 | match kind { |
3005 | | crate::OuterAliasKind::Type => { |
3006 | 0 | let ty = if count == 0 { |
3007 | | // Local alias, check the local module state |
3008 | 0 | ComponentCoreTypeId::Sub(state.type_id_at(index, offset)?) |
3009 | | } else { |
3010 | | // Otherwise, check the enclosing component state |
3011 | 0 | let component = |
3012 | 0 | Self::check_alias_count(components, count - 1, offset)?; |
3013 | 0 | component.core_type_at(index, offset)? |
3014 | | }; |
3015 | | |
3016 | 0 | check_max(state.types.len(), 1, MAX_WASM_TYPES, "types", offset)?; |
3017 | | |
3018 | 0 | match ty { |
3019 | 0 | ComponentCoreTypeId::Sub(ty) => state.types.push(ty), |
3020 | | // TODO https://github.com/WebAssembly/component-model/issues/265 |
3021 | 0 | ComponentCoreTypeId::Module(_) => bail!( |
3022 | 0 | offset, |
3023 | | "not implemented: aliasing core module types into a core \ |
3024 | | module's types index space" |
3025 | | ), |
3026 | | } |
3027 | | } |
3028 | | } |
3029 | | } |
3030 | 0 | crate::ModuleTypeDeclaration::Import(import) => { |
3031 | 0 | state.add_import(import, types, offset)?; |
3032 | | } |
3033 | | } |
3034 | | } |
3035 | | |
3036 | 0 | let imports = state.imports_for_module_type(offset)?; |
3037 | | |
3038 | 0 | Ok(ModuleType { |
3039 | 0 | info: TypeInfo::core(state.type_size), |
3040 | 0 | imports, |
3041 | 0 | exports: state.exports, |
3042 | 0 | }) |
3043 | 0 | } |
3044 | | |
3045 | 63.1k | fn create_component_type( |
3046 | 63.1k | components: &mut Vec<Self>, |
3047 | 63.1k | decls: Vec<crate::ComponentTypeDeclaration>, |
3048 | 63.1k | types: &mut TypeAlloc, |
3049 | 63.1k | offset: usize, |
3050 | 63.1k | ) -> Result<ComponentType> { |
3051 | 63.1k | let features = components[0].features; |
3052 | 63.1k | components.push(ComponentState::new(ComponentKind::ComponentType, features)); |
3053 | | |
3054 | 169k | for decl in decls { |
3055 | 169k | match decl { |
3056 | 0 | crate::ComponentTypeDeclaration::CoreType(ty) => { |
3057 | 0 | Self::add_core_type(components, ty, types, offset, true)?; |
3058 | | } |
3059 | 86.7k | crate::ComponentTypeDeclaration::Type(ty) => { |
3060 | 86.7k | Self::add_type(components, ty, types, offset, true)?; |
3061 | | } |
3062 | 57.6k | crate::ComponentTypeDeclaration::Export { name, ty } => { |
3063 | 57.6k | let current = components.last_mut().unwrap(); |
3064 | 57.6k | let ty = current.check_type_ref(&ty, types, offset)?; |
3065 | 57.6k | current.add_export(name, ty, types, offset, true)?; |
3066 | | } |
3067 | 16.9k | crate::ComponentTypeDeclaration::Import(import) => { |
3068 | 16.9k | components |
3069 | 16.9k | .last_mut() |
3070 | 16.9k | .unwrap() |
3071 | 16.9k | .add_import(import, types, offset)?; |
3072 | | } |
3073 | 7.67k | crate::ComponentTypeDeclaration::Alias(alias) => { |
3074 | 7.67k | Self::add_alias(components, alias, types, offset)?; |
3075 | | } |
3076 | | }; |
3077 | | } |
3078 | | |
3079 | 63.0k | components.pop().unwrap().finish(types, offset) |
3080 | 63.1k | } |
3081 | | |
3082 | 51.3k | fn create_instance_type( |
3083 | 51.3k | components: &mut Vec<Self>, |
3084 | 51.3k | decls: Vec<crate::InstanceTypeDeclaration>, |
3085 | 51.3k | types: &mut TypeAlloc, |
3086 | 51.3k | offset: usize, |
3087 | 51.3k | ) -> Result<ComponentInstanceType> { |
3088 | 51.3k | let features = components[0].features; |
3089 | 51.3k | components.push(ComponentState::new(ComponentKind::InstanceType, features)); |
3090 | | |
3091 | 261k | for decl in decls { |
3092 | 261k | match decl { |
3093 | 0 | crate::InstanceTypeDeclaration::CoreType(ty) => { |
3094 | 0 | Self::add_core_type(components, ty, types, offset, true)?; |
3095 | | } |
3096 | 135k | crate::InstanceTypeDeclaration::Type(ty) => { |
3097 | 135k | Self::add_type(components, ty, types, offset, true)?; |
3098 | | } |
3099 | 115k | crate::InstanceTypeDeclaration::Export { name, ty } => { |
3100 | 115k | let current = components.last_mut().unwrap(); |
3101 | 115k | let ty = current.check_type_ref(&ty, types, offset)?; |
3102 | 115k | current.add_export(name, ty, types, offset, true)?; |
3103 | | } |
3104 | 10.1k | crate::InstanceTypeDeclaration::Alias(alias) => { |
3105 | 10.1k | Self::add_alias(components, alias, types, offset)?; |
3106 | | } |
3107 | | }; |
3108 | | } |
3109 | | |
3110 | 51.3k | let mut state = components.pop().unwrap(); |
3111 | | |
3112 | 51.3k | assert!(state.imported_resources.is_empty()); |
3113 | | |
3114 | | Ok(ComponentInstanceType { |
3115 | 51.3k | info: state.type_info, |
3116 | | |
3117 | | // The defined resources for this instance type are those listed on |
3118 | | // the component state. The path to each defined resource is |
3119 | | // guaranteed to live within the `explicit_resources` map since, |
3120 | | // when in the type context, the introduction of any defined |
3121 | | // resource must have been done with `(export "x" (type (sub |
3122 | | // resource)))` which, in a sense, "fuses" the introduction of the |
3123 | | // variable with the export. This means that all defined resources, |
3124 | | // if any, should be guaranteed to have an `explicit_resources` path |
3125 | | // listed. |
3126 | 51.3k | defined_resources: mem::take(&mut state.defined_resources) |
3127 | 51.3k | .into_iter() |
3128 | 51.3k | .map(|(id, rep)| { |
3129 | 2.68k | assert!(rep.is_none()); |
3130 | 2.68k | id |
3131 | 2.68k | }) |
3132 | 51.3k | .collect(), |
3133 | | |
3134 | | // The map of what resources are explicitly exported and where |
3135 | | // they're exported is plumbed through as-is. |
3136 | 51.3k | explicit_resources: mem::take(&mut state.explicit_resources), |
3137 | | |
3138 | 51.3k | exports: mem::take(&mut state.exports), |
3139 | | }) |
3140 | 51.3k | } |
3141 | | |
3142 | 37.2k | fn create_function_type( |
3143 | 37.2k | &self, |
3144 | 37.2k | ty: crate::ComponentFuncType, |
3145 | 37.2k | types: &TypeList, |
3146 | 37.2k | offset: usize, |
3147 | 37.2k | ) -> Result<ComponentFuncType> { |
3148 | 37.2k | let mut info = TypeInfo::new(); |
3149 | | |
3150 | 37.2k | if ty.async_ { |
3151 | 26.1k | require_feature::cm_async( |
3152 | 26.1k | self.features, |
3153 | | "async component functions require the component model async feature", |
3154 | 26.1k | offset, |
3155 | 0 | )?; |
3156 | 11.0k | } |
3157 | | |
3158 | 37.2k | let mut set = Set::default(); |
3159 | 37.2k | set.reserve(core::cmp::max( |
3160 | 37.2k | ty.params.len(), |
3161 | 37.2k | usize::from(ty.result.is_some()), |
3162 | | )); |
3163 | | |
3164 | 37.2k | let params = ty |
3165 | 37.2k | .params |
3166 | 37.2k | .iter() |
3167 | 92.1k | .map(|(name, ty)| { |
3168 | 92.1k | let name = to_kebab_string(name, "function parameter", offset)?; |
3169 | 92.1k | if !set.insert(name.clone()) { |
3170 | 0 | bail!( |
3171 | 0 | offset, |
3172 | | "function parameter name `{name}` conflicts with previous parameter name `{prev}`", |
3173 | 0 | prev = set.get(&name).unwrap(), |
3174 | | ); |
3175 | 92.1k | } |
3176 | | |
3177 | 92.1k | let ty = self.create_component_val_type(*ty, offset)?; |
3178 | 92.1k | info.combine(ty.info(types), offset)?; |
3179 | 92.1k | Ok((name, ty)) |
3180 | 92.1k | }) |
3181 | 37.2k | .collect::<Result<_>>()?; |
3182 | | |
3183 | 37.2k | set.clear(); |
3184 | | |
3185 | 37.2k | let result = ty |
3186 | 37.2k | .result |
3187 | 37.2k | .map(|ty| { |
3188 | 30.3k | let ty = self.create_component_val_type(ty, offset)?; |
3189 | 30.3k | let ty_info = ty.info(types); |
3190 | 30.3k | if ty_info.contains_borrow() { |
3191 | 0 | bail!(offset, "function result cannot contain a `borrow` type"); |
3192 | 30.3k | } |
3193 | 30.3k | info.combine(ty.info(types), offset)?; |
3194 | 30.3k | Ok(ty) |
3195 | 30.3k | }) |
3196 | 37.2k | .transpose()?; |
3197 | | |
3198 | 37.2k | Ok(ComponentFuncType { |
3199 | 37.2k | async_: ty.async_, |
3200 | 37.2k | info, |
3201 | 37.2k | params, |
3202 | 37.2k | result, |
3203 | 37.2k | }) |
3204 | 37.2k | } |
3205 | | |
3206 | 21.7k | fn instantiate_core_module( |
3207 | 21.7k | &self, |
3208 | 21.7k | module_index: u32, |
3209 | 21.7k | module_args: Vec<crate::InstantiationArg>, |
3210 | 21.7k | types: &mut TypeAlloc, |
3211 | 21.7k | offset: usize, |
3212 | 21.7k | ) -> Result<ComponentCoreInstanceTypeId> { |
3213 | 16.7k | fn insert_arg<'a>( |
3214 | 16.7k | name: &'a str, |
3215 | 16.7k | arg: &'a InstanceType, |
3216 | 16.7k | args: &mut IndexMap<&'a str, &'a InstanceType>, |
3217 | 16.7k | offset: usize, |
3218 | 16.7k | ) -> Result<()> { |
3219 | 16.7k | if args.insert(name, arg).is_some() { |
3220 | 0 | bail!( |
3221 | 0 | offset, |
3222 | | "duplicate module instantiation argument named `{name}`" |
3223 | | ); |
3224 | 16.7k | } |
3225 | | |
3226 | 16.7k | Ok(()) |
3227 | 16.7k | } |
3228 | | |
3229 | 21.7k | let module_type_id = self.module_at(module_index, offset)?; |
3230 | 21.7k | let mut args = IndexMap::default(); |
3231 | | |
3232 | | // Populate the arguments |
3233 | 21.7k | for module_arg in module_args { |
3234 | 16.7k | match module_arg.kind { |
3235 | | InstantiationArgKind::Instance => { |
3236 | 16.7k | let instance_type = &types[self.core_instance_at(module_arg.index, offset)?]; |
3237 | 16.7k | insert_arg(module_arg.name, instance_type, &mut args, offset)?; |
3238 | | } |
3239 | | } |
3240 | | } |
3241 | | |
3242 | | // Validate the arguments |
3243 | 21.7k | let module_type = &types[module_type_id]; |
3244 | 21.7k | let cx = SubtypeCx::new(types, types); |
3245 | 50.7k | for ((module, name), expected) in module_type.imports.iter() { |
3246 | 50.7k | let instance = args.get(module.as_str()).ok_or_else(|| { |
3247 | 0 | format_err!( |
3248 | 0 | offset, |
3249 | | "missing module instantiation argument named `{module}`" |
3250 | | ) |
3251 | 0 | })?; |
3252 | | |
3253 | 50.7k | let arg = instance |
3254 | 50.7k | .internal_exports(types) |
3255 | 50.7k | .get(name.as_str()) |
3256 | 50.7k | .ok_or_else(|| { |
3257 | 0 | format_err!( |
3258 | 0 | offset, |
3259 | | "module instantiation argument `{module}` does not \ |
3260 | | export an item named `{name}`", |
3261 | | ) |
3262 | 0 | })?; |
3263 | | |
3264 | 50.7k | cx.entity_type(arg, expected, offset).with_context(|| { |
3265 | 0 | format!( |
3266 | | "type mismatch for export `{name}` of module \ |
3267 | | instantiation argument `{module}`" |
3268 | | ) |
3269 | 0 | })?; |
3270 | | } |
3271 | | |
3272 | 21.7k | let mut info = TypeInfo::new(); |
3273 | 52.3k | for (_, ty) in module_type.exports.iter() { |
3274 | 52.3k | info.combine(ty.info(types), offset)?; |
3275 | | } |
3276 | | |
3277 | 21.7k | Ok(types.push_ty(InstanceType { |
3278 | 21.7k | info, |
3279 | 21.7k | kind: CoreInstanceTypeKind::Instantiated(module_type_id), |
3280 | 21.7k | })) |
3281 | 21.7k | } |
3282 | | |
3283 | 5.16k | fn instantiate_component( |
3284 | 5.16k | &mut self, |
3285 | 5.16k | component_index: u32, |
3286 | 5.16k | component_args: Vec<crate::ComponentInstantiationArg>, |
3287 | 5.16k | types: &mut TypeAlloc, |
3288 | 5.16k | offset: usize, |
3289 | 5.16k | ) -> Result<ComponentInstanceTypeId> { |
3290 | 5.16k | let component_type_id = self.component_at(component_index, offset)?; |
3291 | 5.16k | let mut args = IndexMap::default(); |
3292 | | |
3293 | | // Populate the arguments |
3294 | 9.39k | for component_arg in component_args { |
3295 | 9.39k | let ty = match component_arg.kind { |
3296 | | ComponentExternalKind::Module => { |
3297 | 0 | ComponentEntityType::Module(self.module_at(component_arg.index, offset)?) |
3298 | | } |
3299 | | ComponentExternalKind::Component => { |
3300 | 0 | ComponentEntityType::Component(self.component_at(component_arg.index, offset)?) |
3301 | | } |
3302 | | ComponentExternalKind::Instance => { |
3303 | 0 | ComponentEntityType::Instance(self.instance_at(component_arg.index, offset)?) |
3304 | | } |
3305 | | ComponentExternalKind::Func => { |
3306 | 8.44k | ComponentEntityType::Func(self.function_at(component_arg.index, offset)?) |
3307 | | } |
3308 | | ComponentExternalKind::Value => { |
3309 | 0 | self.check_value_support(offset)?; |
3310 | 0 | ComponentEntityType::Value(*self.value_at(component_arg.index, offset)?) |
3311 | | } |
3312 | | ComponentExternalKind::Type => { |
3313 | 954 | let ty = self.component_type_at(component_arg.index, offset)?; |
3314 | 954 | ComponentEntityType::Type { |
3315 | 954 | referenced: ty, |
3316 | 954 | created: ty, |
3317 | 954 | } |
3318 | | } |
3319 | | }; |
3320 | 9.39k | match args.entry(component_arg.name.to_string()) { |
3321 | 0 | Entry::Occupied(e) => { |
3322 | 0 | bail!( |
3323 | 0 | offset, |
3324 | | "instantiation argument `{name}` conflicts with previous argument `{prev}`", |
3325 | 0 | prev = e.key(), |
3326 | | name = component_arg.name |
3327 | | ); |
3328 | | } |
3329 | 9.39k | Entry::Vacant(e) => { |
3330 | 9.39k | e.insert(ty); |
3331 | 9.39k | } |
3332 | | } |
3333 | | } |
3334 | | |
3335 | | // Here comes the fun part of the component model, we're instantiating |
3336 | | // the component with type `component_type_id` with the `args` |
3337 | | // specified. Easy enough! |
3338 | | // |
3339 | | // This operation, however, is one of the lynchpins of safety in the |
3340 | | // component model. Additionally what this ends up implementing ranges |
3341 | | // from "well just check the types are equal" to "let's have a |
3342 | | // full-blown ML-style module type system in the component model". There |
3343 | | // are primarily two major tricky pieces to the component model which |
3344 | | // make this operation, instantiating components, hard: |
3345 | | // |
3346 | | // 1. Components can import and exports other components. This means |
3347 | | // that arguments to instantiation are along the lines of functions |
3348 | | // being passed to functions or similar. Effectively this means that |
3349 | | // the term "variance" comes into play with either contravariance |
3350 | | // or covariance depending on where you are in typechecking. This is |
3351 | | // one of the main rationales, however, that this check below is a |
3352 | | // check for subtyping as opposed to exact type equivalence. For |
3353 | | // example an instance that exports something is a subtype of an |
3354 | | // instance that exports nothing. Components get a bit trick since |
3355 | | // they both have imports and exports. My way of thinking about it |
3356 | | // is "who's asking for what". If you're asking for imports then |
3357 | | // I need to at least supply those imports, but I can possibly |
3358 | | // supply more. If you're asking for a thing which you'll give a set |
3359 | | // of imports, then I can give you something which takes less imports |
3360 | | // because what you give still suffices. (things like that). The |
3361 | | // real complication with components, however, comes with... |
3362 | | // |
3363 | | // 2. Resources. Resources in the component model are akin to "abstract |
3364 | | // types". They're not abstract in the sense that they have no |
3365 | | // representation, they're always backed by a 32-bit integer right |
3366 | | // now. Instead they're abstract in the sense that some components |
3367 | | // aren't allowed to understand the representation of a resource. |
3368 | | // For example if you import a resource you can't get the underlying |
3369 | | // internals of it. Furthermore the resource is strictly tracked |
3370 | | // within the component with `own` and `borrow` runtime semantics. |
3371 | | // The hardest part about resources, though, is handling them as |
3372 | | // part of instantiation and subtyping. |
3373 | | // |
3374 | | // For example one major aspect of resources is that if a component |
3375 | | // exports a resource then each instantiation of the component |
3376 | | // produces a fresh resource type. This means that the type recorded |
3377 | | // for the instantiation here can't simply be "I instantiated |
3378 | | // component X" since in such a situation the type of all |
3379 | | // instantiations would be the same, which they aren't. |
3380 | | // |
3381 | | // This sort of subtlety comes up quite frequently for resources. |
3382 | | // This file contains references to `imported_resources` and |
3383 | | // `defined_resources` for example which refer to the formal |
3384 | | // nature of components and their abstract variables. Specifically |
3385 | | // for instantiation though we're eventually faced with the problem |
3386 | | // of subtype checks where resource subtyping is defined as "does |
3387 | | // your id equal mine". Naively implemented that means anything with |
3388 | | // resources isn't subtypes of anything else since resource ids are |
3389 | | // unique between components. Instead what actually needs to happen |
3390 | | // is types need to be substituted. |
3391 | | // |
3392 | | // Much of the complexity here is not actually apparent here in this |
3393 | | // literal one function. Instead it's spread out across validation |
3394 | | // in this file and type-checking in the `types.rs` module. Note that |
3395 | | // the "spread out" nature isn't because we're bad maintainers |
3396 | | // (hopefully), but rather it's quite infectious how many parts need |
3397 | | // to handle resources and account for defined/imported variables. |
3398 | | // |
3399 | | // For example only one subtyping method is called here where `args` is |
3400 | | // passed in. This method is quite recursive in its nature though and |
3401 | | // will internally touch all the fields that this file maintains to |
3402 | | // end up putting into various bits and pieces of type information. |
3403 | | // |
3404 | | // Unfortunately there's probably not really a succinct way to read |
3405 | | // this method and understand everything. If you've written ML module |
3406 | | // type systems this will probably look quite familiar, but otherwise |
3407 | | // the whole system is not really easily approachable at this time. It's |
3408 | | // hoped in the future that there's a formalism to refer to which will |
3409 | | // make things more clear as the code would be able to reference this |
3410 | | // hypothetical formalism. Until that's the case, though, these |
3411 | | // comments are hopefully enough when augmented with communication with |
3412 | | // the authors. |
3413 | | |
3414 | 5.16k | let component_type = &types[component_type_id]; |
3415 | 5.16k | let mut exports = component_type.exports.clone(); |
3416 | 5.16k | let mut info = TypeInfo::new(); |
3417 | 33.0k | for (_, ty) in component_type.exports.iter() { |
3418 | 33.0k | info.combine(ty.ty.info(types), offset)?; |
3419 | | } |
3420 | | |
3421 | | // Perform the subtype check that `args` matches the imports of |
3422 | | // `component_type_id`. The result of this subtype check is the |
3423 | | // production of a mapping of resource types from the imports to the |
3424 | | // arguments provided. This is a substitution map which is then used |
3425 | | // below to perform a substitution into the exports of the instance |
3426 | | // since the types of the exports are now in terms of whatever was |
3427 | | // supplied as imports. |
3428 | 5.16k | let mut mapping = SubtypeCx::new(types, types).open_instance_type( |
3429 | 5.16k | &args, |
3430 | 5.16k | component_type_id, |
3431 | 5.16k | ExternKind::Import, |
3432 | 5.16k | offset, |
3433 | 0 | )?; |
3434 | | |
3435 | | // Part of the instantiation of a component is that all of its |
3436 | | // defined resources become "fresh" on each instantiation. This |
3437 | | // means that each instantiation of a component gets brand new type |
3438 | | // variables representing its defined resources, modeling that each |
3439 | | // instantiation produces distinct types. The freshening is performed |
3440 | | // here by allocating new ids and inserting them into `mapping`. |
3441 | | // |
3442 | | // Note that technically the `mapping` from subtyping should be applied |
3443 | | // first and then the mapping for freshening should be applied |
3444 | | // afterwards. The keys of the map from subtyping are the imported |
3445 | | // resources from this component which are disjoint from its defined |
3446 | | // resources. That means it should be possible to place everything |
3447 | | // into one large map which maps from: |
3448 | | // |
3449 | | // * the component's imported resources go to whatever was explicitly |
3450 | | // supplied in the import map |
3451 | | // * the component's defined resources go to fresh new resources |
3452 | | // |
3453 | | // These two remapping operations can then get folded into one by |
3454 | | // placing everything in the same `mapping` and using that for a remap |
3455 | | // only once. |
3456 | 5.16k | let fresh_defined_resources = (0..component_type.defined_resources.len()) |
3457 | 5.16k | .map(|_| types.alloc_resource_id().resource()) |
3458 | 5.16k | .collect::<IndexSet<_>>(); |
3459 | 5.16k | let component_type = &types[component_type_id]; |
3460 | 5.16k | for ((old, _path), new) in component_type |
3461 | 5.16k | .defined_resources |
3462 | 5.16k | .iter() |
3463 | 5.16k | .zip(&fresh_defined_resources) |
3464 | | { |
3465 | 0 | let prev = mapping.resources.insert(*old, *new); |
3466 | 0 | assert!(prev.is_none()); |
3467 | | } |
3468 | | |
3469 | | // Perform the remapping operation over all the exports that will be |
3470 | | // listed for the final instance type. Note that this is performed |
3471 | | // both for all the export types in addition to the explicitly exported |
3472 | | // resources list. |
3473 | | // |
3474 | | // Note that this is a crucial step of the instantiation process which |
3475 | | // is intentionally transforming the type of a component based on the |
3476 | | // variables provided by imports and additionally ensuring that all |
3477 | | // references to the component's defined resources are rebound to the |
3478 | | // fresh ones introduced just above. |
3479 | 33.0k | for entity in exports.values_mut() { |
3480 | 33.0k | types.remap_component_entity(&mut entity.ty, &mut mapping); |
3481 | 33.0k | } |
3482 | 5.16k | let component_type = &types[component_type_id]; |
3483 | 5.16k | let explicit_resources = component_type |
3484 | 5.16k | .explicit_resources |
3485 | 5.16k | .iter() |
3486 | 5.16k | .map(|(id, path)| { |
3487 | 651 | ( |
3488 | 651 | mapping.resources.get(id).copied().unwrap_or(*id), |
3489 | 651 | path.clone(), |
3490 | 651 | ) |
3491 | 651 | }) |
3492 | 5.16k | .collect::<IndexMap<_, _>>(); |
3493 | | |
3494 | | // Technically in the last formalism that was consulted in writing this |
3495 | | // implementation there are two further steps that are part of the |
3496 | | // instantiation process: |
3497 | | // |
3498 | | // 1. The set of defined resources from the instance created, which are |
3499 | | // added to the outer component, is the subset of the instance's |
3500 | | // original defined resources and the free variables of the exports. |
3501 | | // |
3502 | | // 2. Each element of this subset is required to be "explicit in" the |
3503 | | // instance, or otherwise explicitly exported somewhere within the |
3504 | | // instance. |
3505 | | // |
3506 | | // With the syntactic structure of the component model, however, neither |
3507 | | // of these conditions should be necessary. The main reason for this is |
3508 | | // that this function is specifically dealing with instantiation of |
3509 | | // components which should already have these properties validated |
3510 | | // about them. Subsequently we shouldn't have to re-check them. |
3511 | | // |
3512 | | // In debug mode, however, do a sanity check. |
3513 | 5.16k | if cfg!(debug_assertions) { |
3514 | 0 | let mut free = IndexSet::default(); |
3515 | 0 | for ty in exports.values() { |
3516 | 0 | types.free_variables_component_entity(&ty.ty, &mut free); |
3517 | 0 | } |
3518 | 0 | assert!(fresh_defined_resources.is_subset(&free)); |
3519 | 0 | for resource in fresh_defined_resources.iter() { |
3520 | 0 | assert!(explicit_resources.contains_key(resource)); |
3521 | | } |
3522 | 5.16k | } |
3523 | | |
3524 | | // And as the final step of the instantiation process all of the |
3525 | | // new defined resources from this component instantiation are moved |
3526 | | // onto `self`. Note that concrete instances never have defined |
3527 | | // resources (see more comments in `instantiate_exports`) so the |
3528 | | // `defined_resources` listing in the final type is always empty. This |
3529 | | // represents how by having a concrete instance the definitions |
3530 | | // referred to in that instance are now problems for the outer |
3531 | | // component rather than the inner instance since the instance is bound |
3532 | | // to the component. |
3533 | | // |
3534 | | // All defined resources here have no known representation, so they're |
3535 | | // all listed with `None`. Also note that none of the resources were |
3536 | | // exported yet so `self.explicit_resources` is not updated yet. If |
3537 | | // this instance is exported, however, it'll consult the type's |
3538 | | // `explicit_resources` array and use that appropriately. |
3539 | 5.16k | for resource in fresh_defined_resources { |
3540 | 0 | self.defined_resources.insert(resource, None); |
3541 | 0 | } |
3542 | | |
3543 | 5.16k | Ok(types.push_ty(ComponentInstanceType { |
3544 | 5.16k | info, |
3545 | 5.16k | defined_resources: Default::default(), |
3546 | 5.16k | explicit_resources, |
3547 | 5.16k | exports, |
3548 | 5.16k | })) |
3549 | 5.16k | } |
3550 | | |
3551 | 0 | fn instantiate_component_exports( |
3552 | 0 | &mut self, |
3553 | 0 | exports: Vec<crate::ComponentExport>, |
3554 | 0 | types: &mut TypeAlloc, |
3555 | 0 | offset: usize, |
3556 | 0 | ) -> Result<ComponentInstanceTypeId> { |
3557 | 0 | let mut info = TypeInfo::new(); |
3558 | 0 | let mut inst_exports = IndexMap::default(); |
3559 | 0 | let mut explicit_resources = IndexMap::default(); |
3560 | 0 | let mut export_names = IndexSet::default(); |
3561 | | |
3562 | | // NB: It's intentional that this context is empty since no indices are |
3563 | | // introduced in the bag-of-exports construct which means there's no |
3564 | | // way syntactically to register something inside of this. |
3565 | 0 | let names = ComponentNameContext::default(); |
3566 | | |
3567 | 0 | for export in exports { |
3568 | 0 | assert!(export.ty.is_none()); |
3569 | 0 | let ty = match export.kind { |
3570 | | ComponentExternalKind::Module => { |
3571 | 0 | ComponentEntityType::Module(self.module_at(export.index, offset)?) |
3572 | | } |
3573 | | ComponentExternalKind::Component => { |
3574 | 0 | ComponentEntityType::Component(self.component_at(export.index, offset)?) |
3575 | | } |
3576 | | ComponentExternalKind::Instance => { |
3577 | 0 | let ty = self.instance_at(export.index, offset)?; |
3578 | | |
3579 | | // When an instance is exported from an instance then |
3580 | | // all explicitly exported resources on the sub-instance are |
3581 | | // now also listed as exported resources on the outer |
3582 | | // instance, just with one more element in their path. |
3583 | 0 | explicit_resources.extend(types[ty].explicit_resources.iter().map( |
3584 | 0 | |(id, path)| { |
3585 | 0 | let mut new_path = vec![inst_exports.len()]; |
3586 | 0 | new_path.extend(path); |
3587 | 0 | (*id, new_path) |
3588 | 0 | }, |
3589 | | )); |
3590 | 0 | ComponentEntityType::Instance(ty) |
3591 | | } |
3592 | | ComponentExternalKind::Func => { |
3593 | 0 | ComponentEntityType::Func(self.function_at(export.index, offset)?) |
3594 | | } |
3595 | | ComponentExternalKind::Value => { |
3596 | 0 | self.check_value_support(offset)?; |
3597 | 0 | ComponentEntityType::Value(*self.value_at(export.index, offset)?) |
3598 | | } |
3599 | | ComponentExternalKind::Type => { |
3600 | 0 | let ty = self.component_type_at(export.index, offset)?; |
3601 | | // If this is an export of a resource type be sure to |
3602 | | // record that in the explicit list with the appropriate |
3603 | | // path because if this instance ends up getting used |
3604 | | // it'll count towards the "explicit in" check. |
3605 | 0 | if let ComponentAnyTypeId::Resource(id) = ty { |
3606 | 0 | explicit_resources.insert(id.resource(), vec![inst_exports.len()]); |
3607 | 0 | } |
3608 | 0 | ComponentEntityType::Type { |
3609 | 0 | referenced: ty, |
3610 | 0 | // The created type index here isn't used anywhere |
3611 | 0 | // in index spaces because a "bag of exports" |
3612 | 0 | // doesn't build up its own index spaces. Just fill |
3613 | 0 | // in the same index here in this case as what's |
3614 | 0 | // referenced. |
3615 | 0 | created: ty, |
3616 | 0 | } |
3617 | | } |
3618 | | }; |
3619 | | |
3620 | 0 | names.validate_extern( |
3621 | 0 | &export.name, |
3622 | 0 | ExternKind::Export, |
3623 | 0 | &ty, |
3624 | 0 | types, |
3625 | 0 | offset, |
3626 | 0 | &mut export_names, |
3627 | 0 | &mut inst_exports, |
3628 | 0 | &mut info, |
3629 | 0 | &self.features, |
3630 | 0 | )?; |
3631 | | } |
3632 | | |
3633 | 0 | Ok(types.push_ty(ComponentInstanceType { |
3634 | 0 | info, |
3635 | 0 | explicit_resources, |
3636 | 0 | exports: inst_exports, |
3637 | 0 |
|
3638 | 0 | // NB: the list of defined resources for this instance itself |
3639 | 0 | // is always empty. Even if this instance exports resources, |
3640 | 0 | // it's empty. |
3641 | 0 | // |
3642 | 0 | // The reason for this is a bit subtle. The general idea, though, is |
3643 | 0 | // that the defined resources list here is only used for instance |
3644 | 0 | // types that are sort of "floating around" and haven't actually |
3645 | 0 | // been attached to something yet. For example when an instance type |
3646 | 0 | // is simply declared it can have defined resources introduced |
3647 | 0 | // through `(export "name" (type (sub resource)))`. These |
3648 | 0 | // definitions, however, are local to the instance itself and aren't |
3649 | 0 | // defined elsewhere. |
3650 | 0 | // |
3651 | 0 | // Here, though, no new definitions were introduced. The instance |
3652 | 0 | // created here is a "bag of exports" which could only refer to |
3653 | 0 | // preexisting items. This means that inherently no new resources |
3654 | 0 | // were created so there's nothing to put in this list. Any |
3655 | 0 | // resources referenced by the instance must be bound by the outer |
3656 | 0 | // component context or further above. |
3657 | 0 | // |
3658 | 0 | // Furthermore, however, actual instances of instances, which this |
3659 | 0 | // is, aren't allowed to have defined resources. Instead the |
3660 | 0 | // resources would have to be injected into the outer component |
3661 | 0 | // enclosing the instance. That means that even if bag-of-exports |
3662 | 0 | // could declare a new resource then the resource would be moved |
3663 | 0 | // from here to `self.defined_resources`. This doesn't exist at this |
3664 | 0 | // time, though, so this still remains empty and |
3665 | 0 | // `self.defined_resources` remains unperturbed. |
3666 | 0 | defined_resources: Default::default(), |
3667 | 0 | })) |
3668 | 0 | } |
3669 | | |
3670 | 16.7k | fn instantiate_core_exports( |
3671 | 16.7k | &mut self, |
3672 | 16.7k | exports: Vec<crate::Export>, |
3673 | 16.7k | types: &mut TypeAlloc, |
3674 | 16.7k | offset: usize, |
3675 | 16.7k | ) -> Result<ComponentCoreInstanceTypeId> { |
3676 | 50.7k | fn insert_export( |
3677 | 50.7k | types: &TypeList, |
3678 | 50.7k | name: &str, |
3679 | 50.7k | export: EntityType, |
3680 | 50.7k | exports: &mut IndexMap<String, EntityType>, |
3681 | 50.7k | info: &mut TypeInfo, |
3682 | 50.7k | offset: usize, |
3683 | 50.7k | ) -> Result<()> { |
3684 | 50.7k | info.combine(export.info(types), offset)?; |
3685 | | |
3686 | 50.7k | if exports.insert(name.to_string(), export).is_some() { |
3687 | 0 | bail!( |
3688 | 0 | offset, |
3689 | | "duplicate instantiation export name `{name}` already defined", |
3690 | | ) |
3691 | 50.7k | } |
3692 | | |
3693 | 50.7k | Ok(()) |
3694 | 50.7k | } |
3695 | | |
3696 | 16.7k | let mut info = TypeInfo::new(); |
3697 | 16.7k | let mut inst_exports = IndexMap::default(); |
3698 | 50.7k | for export in exports { |
3699 | 50.7k | match export.kind { |
3700 | | ExternalKind::Func | ExternalKind::FuncExact => { |
3701 | 48.7k | insert_export( |
3702 | 48.7k | types, |
3703 | 48.7k | export.name, |
3704 | 48.7k | EntityType::Func(self.core_function_at(export.index, offset)?), |
3705 | 48.7k | &mut inst_exports, |
3706 | 48.7k | &mut info, |
3707 | 48.7k | offset, |
3708 | 0 | )?; |
3709 | | } |
3710 | 2.03k | ExternalKind::Table => insert_export( |
3711 | 2.03k | types, |
3712 | 2.03k | export.name, |
3713 | 2.03k | EntityType::Table(*self.table_at(export.index, offset)?), |
3714 | 2.03k | &mut inst_exports, |
3715 | 2.03k | &mut info, |
3716 | 2.03k | offset, |
3717 | 0 | )?, |
3718 | 0 | ExternalKind::Memory => insert_export( |
3719 | 0 | types, |
3720 | 0 | export.name, |
3721 | 0 | EntityType::Memory(*self.memory_at(export.index, offset)?), |
3722 | 0 | &mut inst_exports, |
3723 | 0 | &mut info, |
3724 | 0 | offset, |
3725 | 0 | )?, |
3726 | | ExternalKind::Global => { |
3727 | 0 | insert_export( |
3728 | 0 | types, |
3729 | 0 | export.name, |
3730 | 0 | EntityType::Global(*self.global_at(export.index, offset)?), |
3731 | 0 | &mut inst_exports, |
3732 | 0 | &mut info, |
3733 | 0 | offset, |
3734 | 0 | )?; |
3735 | | } |
3736 | | ExternalKind::Tag => { |
3737 | 0 | require_feature::exceptions( |
3738 | 0 | self.features, |
3739 | | "exceptions proposal not enabled", |
3740 | 0 | offset, |
3741 | 0 | )?; |
3742 | 0 | insert_export( |
3743 | 0 | types, |
3744 | 0 | export.name, |
3745 | 0 | EntityType::Tag(self.tag_at(export.index, offset)?), |
3746 | 0 | &mut inst_exports, |
3747 | 0 | &mut info, |
3748 | 0 | offset, |
3749 | 0 | )? |
3750 | | } |
3751 | | } |
3752 | | } |
3753 | | |
3754 | 16.7k | Ok(types.push_ty(InstanceType { |
3755 | 16.7k | info, |
3756 | 16.7k | kind: CoreInstanceTypeKind::Exports(inst_exports), |
3757 | 16.7k | })) |
3758 | 16.7k | } |
3759 | | |
3760 | 45.1k | fn alias_core_instance_export( |
3761 | 45.1k | &mut self, |
3762 | 45.1k | instance_index: u32, |
3763 | 45.1k | kind: ExternalKind, |
3764 | 45.1k | name: &str, |
3765 | 45.1k | types: &TypeList, |
3766 | 45.1k | offset: usize, |
3767 | 45.1k | ) -> Result<()> { |
3768 | | macro_rules! push_module_export { |
3769 | | ($expected:path, $collection:ident, $ty:literal) => {{ |
3770 | | match self.core_instance_export(instance_index, name, types, offset)? { |
3771 | | $expected(ty) => { |
3772 | | self.$collection.push(*ty); |
3773 | | } |
3774 | | _ => { |
3775 | | bail!( |
3776 | | offset, |
3777 | | "export `{name}` for core instance {instance_index} is not a {}", |
3778 | | $ty |
3779 | | ) |
3780 | | } |
3781 | | } |
3782 | | }}; |
3783 | | } |
3784 | | |
3785 | 45.1k | match kind { |
3786 | | ExternalKind::Func | ExternalKind::FuncExact => { |
3787 | 34.2k | check_max( |
3788 | 34.2k | self.function_count(), |
3789 | | 1, |
3790 | | MAX_WASM_FUNCTIONS, |
3791 | 34.2k | "functions", |
3792 | 34.2k | offset, |
3793 | 0 | )?; |
3794 | 34.2k | push_module_export!(EntityType::Func, core_funcs, "function"); |
3795 | | } |
3796 | | ExternalKind::Table => { |
3797 | 2.03k | check_max( |
3798 | 2.03k | self.core_tables.len(), |
3799 | | 1, |
3800 | | MAX_CORE_INDEX_SPACE_ITEMS, |
3801 | 2.03k | "tables", |
3802 | 2.03k | offset, |
3803 | 0 | )?; |
3804 | 2.03k | push_module_export!(EntityType::Table, core_tables, "table"); |
3805 | | } |
3806 | | ExternalKind::Memory => { |
3807 | 8.84k | check_max( |
3808 | 8.84k | self.core_memories.len(), |
3809 | | 1, |
3810 | | MAX_CORE_INDEX_SPACE_ITEMS, |
3811 | 8.84k | "memories", |
3812 | 8.84k | offset, |
3813 | 0 | )?; |
3814 | 8.84k | push_module_export!(EntityType::Memory, core_memories, "memory"); |
3815 | | } |
3816 | | ExternalKind::Global => { |
3817 | 0 | check_max( |
3818 | 0 | self.core_globals.len(), |
3819 | | 1, |
3820 | | MAX_CORE_INDEX_SPACE_ITEMS, |
3821 | 0 | "globals", |
3822 | 0 | offset, |
3823 | 0 | )?; |
3824 | 0 | push_module_export!(EntityType::Global, core_globals, "global"); |
3825 | | } |
3826 | | ExternalKind::Tag => { |
3827 | 0 | require_feature::exceptions( |
3828 | 0 | self.features, |
3829 | | "exceptions proposal not enabled", |
3830 | 0 | offset, |
3831 | 0 | )?; |
3832 | 0 | check_max( |
3833 | 0 | self.core_tags.len(), |
3834 | | 1, |
3835 | | MAX_CORE_INDEX_SPACE_ITEMS, |
3836 | 0 | "tags", |
3837 | 0 | offset, |
3838 | 0 | )?; |
3839 | 0 | push_module_export!(EntityType::Tag, core_tags, "tag"); |
3840 | | } |
3841 | | } |
3842 | | |
3843 | 45.1k | Ok(()) |
3844 | 45.1k | } |
3845 | | |
3846 | 11.3k | fn alias_instance_export( |
3847 | 11.3k | &mut self, |
3848 | 11.3k | instance_index: u32, |
3849 | 11.3k | kind: ComponentExternalKind, |
3850 | 11.3k | name: &str, |
3851 | 11.3k | types: &mut TypeAlloc, |
3852 | 11.3k | offset: usize, |
3853 | 11.3k | ) -> Result<()> { |
3854 | 11.3k | if let ComponentExternalKind::Value = kind { |
3855 | 0 | self.check_value_support(offset)?; |
3856 | 11.3k | } |
3857 | 11.3k | let mut ty = match types[self.instance_at(instance_index, offset)?] |
3858 | | .exports |
3859 | 11.3k | .get(name) |
3860 | | { |
3861 | 11.3k | Some(ty) => ty.ty, |
3862 | 0 | None => bail!( |
3863 | 0 | offset, |
3864 | | "instance {instance_index} has no export named `{name}`" |
3865 | | ), |
3866 | | }; |
3867 | | |
3868 | 11.3k | let ok = match (ty, kind) { |
3869 | 0 | (ComponentEntityType::Module(_), ComponentExternalKind::Module) => true, |
3870 | 0 | (ComponentEntityType::Module(_), _) => false, |
3871 | 0 | (ComponentEntityType::Component(_), ComponentExternalKind::Component) => true, |
3872 | 0 | (ComponentEntityType::Component(_), _) => false, |
3873 | 2.91k | (ComponentEntityType::Func(_), ComponentExternalKind::Func) => true, |
3874 | 0 | (ComponentEntityType::Func(_), _) => false, |
3875 | 0 | (ComponentEntityType::Instance(_), ComponentExternalKind::Instance) => true, |
3876 | 0 | (ComponentEntityType::Instance(_), _) => false, |
3877 | 0 | (ComponentEntityType::Value(_), ComponentExternalKind::Value) => true, |
3878 | 0 | (ComponentEntityType::Value(_), _) => false, |
3879 | 8.41k | (ComponentEntityType::Type { .. }, ComponentExternalKind::Type) => true, |
3880 | 0 | (ComponentEntityType::Type { .. }, _) => false, |
3881 | | }; |
3882 | 11.3k | if !ok { |
3883 | 0 | bail!( |
3884 | 0 | offset, |
3885 | | "export `{name}` for instance {instance_index} is not a {}", |
3886 | 0 | kind.desc(), |
3887 | | ); |
3888 | 11.3k | } |
3889 | | |
3890 | 11.3k | self.add_entity(&mut ty, None, types, offset)?; |
3891 | 11.3k | Ok(()) |
3892 | 11.3k | } |
3893 | | |
3894 | 0 | fn alias_module(components: &mut [Self], count: u32, index: u32, offset: usize) -> Result<()> { |
3895 | 0 | let component = Self::check_alias_count(components, count, offset)?; |
3896 | 0 | let ty = component.module_at(index, offset)?; |
3897 | | |
3898 | 0 | let current = components.last_mut().unwrap(); |
3899 | 0 | check_max( |
3900 | 0 | current.core_modules.len(), |
3901 | | 1, |
3902 | | MAX_WASM_MODULES, |
3903 | 0 | "modules", |
3904 | 0 | offset, |
3905 | 0 | )?; |
3906 | | |
3907 | 0 | current.core_modules.push(ty); |
3908 | 0 | Ok(()) |
3909 | 0 | } |
3910 | | |
3911 | 0 | fn alias_component( |
3912 | 0 | components: &mut [Self], |
3913 | 0 | count: u32, |
3914 | 0 | index: u32, |
3915 | 0 | offset: usize, |
3916 | 0 | ) -> Result<()> { |
3917 | 0 | let component = Self::check_alias_count(components, count, offset)?; |
3918 | 0 | let ty = component.component_at(index, offset)?; |
3919 | | |
3920 | 0 | let current = components.last_mut().unwrap(); |
3921 | 0 | check_max( |
3922 | 0 | current.components.len(), |
3923 | | 1, |
3924 | | MAX_WASM_COMPONENTS, |
3925 | 0 | "components", |
3926 | 0 | offset, |
3927 | 0 | )?; |
3928 | | |
3929 | 0 | current.components.push(ty); |
3930 | 0 | Ok(()) |
3931 | 0 | } |
3932 | | |
3933 | 0 | fn alias_core_type( |
3934 | 0 | components: &mut [Self], |
3935 | 0 | count: u32, |
3936 | 0 | index: u32, |
3937 | 0 | offset: usize, |
3938 | 0 | ) -> Result<()> { |
3939 | 0 | let component = Self::check_alias_count(components, count, offset)?; |
3940 | 0 | let ty = component.core_type_at(index, offset)?; |
3941 | | |
3942 | 0 | let current = components.last_mut().unwrap(); |
3943 | 0 | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; |
3944 | | |
3945 | 0 | current.core_types.push(ty); |
3946 | | |
3947 | 0 | Ok(()) |
3948 | 0 | } |
3949 | | |
3950 | 10.1k | fn alias_type( |
3951 | 10.1k | components: &mut [Self], |
3952 | 10.1k | count: u32, |
3953 | 10.1k | index: u32, |
3954 | 10.1k | types: &mut TypeAlloc, |
3955 | 10.1k | offset: usize, |
3956 | 10.1k | ) -> Result<()> { |
3957 | 10.1k | let component = Self::check_alias_count(components, count, offset)?; |
3958 | 10.1k | let ty = component.component_type_at(index, offset)?; |
3959 | | |
3960 | | // If `count` "crossed a component boundary", meaning that it went from |
3961 | | // one component to another, then this must additionally verify that |
3962 | | // `ty` has no free variables with respect to resources. This is |
3963 | | // intended to preserve the property for components where each component |
3964 | | // is an isolated unit that can theoretically be extracted from other |
3965 | | // components. If resources from other components were allowed to leak |
3966 | | // in then it would prevent that. |
3967 | | // |
3968 | | // This check is done by calculating the `pos` within `components` that |
3969 | | // our target `component` above was selected at. Once this is acquired |
3970 | | // the component to the "right" is checked, and if that's a component |
3971 | | // then it's considered as crossing a component boundary meaning the |
3972 | | // free variables check runs. |
3973 | | // |
3974 | | // The reason this works is that in the list of `ComponentState` types |
3975 | | // it's guaranteed that any `is_type` components are contiguous at the |
3976 | | // end of the array. This means that if state one level deeper than the |
3977 | | // target of this alias is a `!is_type` component, then the target must |
3978 | | // be a component as well. If the one-level deeper state `is_type` then |
3979 | | // the target is either a type or a component, both of which are valid |
3980 | | // (as aliases can reach the enclosing component and have as many free |
3981 | | // variables as they want). |
3982 | 10.1k | let pos_after_component = components.len() - (count as usize); |
3983 | 10.1k | if let Some(component) = components.get(pos_after_component) { |
3984 | 10.1k | if component.kind == ComponentKind::Component { |
3985 | 0 | let mut free = IndexSet::default(); |
3986 | 0 | types.free_variables_any_type_id(ty, &mut free); |
3987 | 0 | if !free.is_empty() { |
3988 | 0 | bail!( |
3989 | 0 | offset, |
3990 | | "cannot alias outer type which transitively refers \ |
3991 | | to resources not defined in the current component" |
3992 | | ); |
3993 | 0 | } |
3994 | 10.1k | } |
3995 | 0 | } |
3996 | | |
3997 | 10.1k | let current = components.last_mut().unwrap(); |
3998 | 10.1k | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; |
3999 | | |
4000 | 10.1k | current.types.push(ty); |
4001 | | |
4002 | 10.1k | Ok(()) |
4003 | 10.1k | } |
4004 | | |
4005 | 10.1k | fn check_alias_count(components: &[Self], count: u32, offset: usize) -> Result<&Self> { |
4006 | 10.1k | let count = count as usize; |
4007 | 10.1k | if count >= components.len() { |
4008 | 0 | bail!(offset, "invalid outer alias count of {count}"); |
4009 | 10.1k | } |
4010 | | |
4011 | 10.1k | Ok(&components[components.len() - count - 1]) |
4012 | 10.1k | } |
4013 | | |
4014 | 227k | fn create_defined_type( |
4015 | 227k | &self, |
4016 | 227k | ty: crate::ComponentDefinedType, |
4017 | 227k | types: &TypeList, |
4018 | 227k | offset: usize, |
4019 | 227k | ) -> Result<ComponentDefinedType> { |
4020 | 227k | match ty { |
4021 | 2.39k | crate::ComponentDefinedType::Primitive(ty) => { |
4022 | 2.39k | self.check_primitive_type(ty, offset)?; |
4023 | 2.39k | Ok(ComponentDefinedType::Primitive(ty)) |
4024 | | } |
4025 | 7.04k | crate::ComponentDefinedType::Record(fields) => { |
4026 | 7.04k | self.create_record_type(fields.as_ref(), types, offset) |
4027 | | } |
4028 | 8.06k | crate::ComponentDefinedType::Variant(cases) => { |
4029 | 8.06k | self.create_variant_type(cases.as_ref(), types, offset) |
4030 | | } |
4031 | 6.81k | crate::ComponentDefinedType::List(ty) => { |
4032 | 6.81k | let element = self.create_component_val_type(ty, offset)?; |
4033 | 6.81k | let mut info = TypeInfo::new(); |
4034 | 6.81k | info.combine(element.info(types), offset)?; |
4035 | 6.81k | Ok(ComponentDefinedType::List { element, info }) |
4036 | | } |
4037 | 0 | crate::ComponentDefinedType::Map(key, value) => { |
4038 | 0 | require_feature::cm_map( |
4039 | 0 | self.features, |
4040 | | "Maps require the component model map feature", |
4041 | 0 | offset, |
4042 | 0 | )?; |
4043 | 0 | let key = self.create_component_val_type(key, offset)?; |
4044 | 0 | let value = self.create_component_val_type(value, offset)?; |
4045 | 0 | let mut info = TypeInfo::new(); |
4046 | 0 | info.combine(key.info(types), offset)?; |
4047 | 0 | info.combine(value.info(types), offset)?; |
4048 | 0 | Ok(ComponentDefinedType::Map { key, value, info }) |
4049 | | } |
4050 | 4.02k | crate::ComponentDefinedType::FixedLengthList(ty, elements) => { |
4051 | 4.02k | require_feature::cm_fixed_length_lists( |
4052 | 4.02k | self.features, |
4053 | | "Fixed-length lists require the component model fixed-length lists feature", |
4054 | 4.02k | offset, |
4055 | 0 | )?; |
4056 | 4.02k | if elements < 1 { |
4057 | 0 | bail!( |
4058 | 0 | offset, |
4059 | | "Fixed-length lists must have more than zero elements" |
4060 | | ) |
4061 | 4.02k | } |
4062 | 4.02k | check_max( |
4063 | | 0, |
4064 | 4.02k | elements, |
4065 | | MAX_WASM_FIXED_LENGTH_LIST_ELEMENTS, |
4066 | 4.02k | "fixed-length list element", |
4067 | 4.02k | offset, |
4068 | 0 | )?; |
4069 | 4.02k | let element = self.create_component_val_type(ty, offset)?; |
4070 | 4.02k | let mut info = TypeInfo::new(); |
4071 | 4.02k | info.combine(element.info(types), offset)?; |
4072 | 4.02k | Ok(ComponentDefinedType::FixedLengthList { |
4073 | 4.02k | element, |
4074 | 4.02k | length: elements, |
4075 | 4.02k | info, |
4076 | 4.02k | }) |
4077 | | } |
4078 | 41.8k | crate::ComponentDefinedType::Tuple(tys) => { |
4079 | 41.8k | self.create_tuple_type(tys.as_ref(), types, offset) |
4080 | | } |
4081 | 1.27k | crate::ComponentDefinedType::Flags(names) => { |
4082 | 1.27k | self.create_flags_type(names.as_ref(), offset) |
4083 | | } |
4084 | 88.6k | crate::ComponentDefinedType::Enum(cases) => { |
4085 | 88.6k | self.create_enum_type(cases.as_ref(), offset) |
4086 | | } |
4087 | 19.4k | crate::ComponentDefinedType::Option(ty) => { |
4088 | 19.4k | let ty = self.create_component_val_type(ty, offset)?; |
4089 | 19.4k | let mut info = TypeInfo::new(); |
4090 | 19.4k | info.combine(ty.info(types), offset)?; |
4091 | 19.4k | Ok(ComponentDefinedType::Option { ty, info }) |
4092 | | } |
4093 | 32.8k | crate::ComponentDefinedType::Result { ok, err } => { |
4094 | 32.8k | let ok = ok |
4095 | 32.8k | .map(|ty| self.create_component_val_type(ty, offset)) |
4096 | 32.8k | .transpose()?; |
4097 | 32.8k | let err = err |
4098 | 32.8k | .map(|ty| self.create_component_val_type(ty, offset)) |
4099 | 32.8k | .transpose()?; |
4100 | 32.8k | let mut info = TypeInfo::new(); |
4101 | 32.8k | if let Some(ty) = &ok { |
4102 | 30.5k | info.combine(ty.info(types), offset)?; |
4103 | 2.28k | } |
4104 | 32.8k | if let Some(ty) = &err { |
4105 | 29.3k | info.combine(ty.info(types), offset)?; |
4106 | 3.47k | } |
4107 | 32.8k | Ok(ComponentDefinedType::Result { ok, err, info }) |
4108 | | } |
4109 | 1.39k | crate::ComponentDefinedType::Own(idx) => Ok(ComponentDefinedType::Own( |
4110 | 1.39k | self.resource_at(idx, types, offset)?, |
4111 | | )), |
4112 | 2.63k | crate::ComponentDefinedType::Borrow(idx) => Ok(ComponentDefinedType::Borrow( |
4113 | 2.63k | self.resource_at(idx, types, offset)?, |
4114 | | )), |
4115 | 6.84k | crate::ComponentDefinedType::Future(ty) => { |
4116 | 6.84k | require_feature::cm_async( |
4117 | 6.84k | self.features, |
4118 | | "`future` requires the component model async feature", |
4119 | 6.84k | offset, |
4120 | 0 | )?; |
4121 | 6.84k | let ty = ty |
4122 | 6.84k | .map(|ty| self.create_component_val_type(ty, offset)) |
4123 | 6.84k | .transpose()?; |
4124 | 6.84k | let mut info = TypeInfo::new(); |
4125 | 6.84k | if let Some(ty) = &ty { |
4126 | 6.26k | info.combine(ty.info(types), offset)?; |
4127 | 583 | } |
4128 | 6.84k | Ok(ComponentDefinedType::Future { ty, info }) |
4129 | | } |
4130 | 4.15k | crate::ComponentDefinedType::Stream(ty) => { |
4131 | 4.15k | require_feature::cm_async( |
4132 | 4.15k | self.features, |
4133 | | "`stream` requires the component model async feature", |
4134 | 4.15k | offset, |
4135 | 0 | )?; |
4136 | 4.15k | let ty = ty |
4137 | 4.15k | .map(|ty| self.create_component_val_type(ty, offset)) |
4138 | 4.15k | .transpose()?; |
4139 | 4.15k | let prim = match ty { |
4140 | 738 | Some(ComponentValType::Primitive(p)) => Some(p), |
4141 | 3.41k | Some(ComponentValType::Type(id)) => match types[id] { |
4142 | 0 | ComponentDefinedType::Primitive(p) => Some(p), |
4143 | 3.41k | _ => None, |
4144 | | }, |
4145 | 0 | None => None, |
4146 | | }; |
4147 | 4.15k | if prim == Some(crate::PrimitiveValType::Char) { |
4148 | 22 | bail!( |
4149 | 22 | offset, |
4150 | | "`stream<char>` is not valid at this time, use `stream<u8>` \ |
4151 | | with a defined by encoding instead for now" |
4152 | | ) |
4153 | 4.12k | } |
4154 | 4.12k | let mut info = TypeInfo::new(); |
4155 | 4.12k | if let Some(ty) = &ty { |
4156 | 4.12k | info.combine(ty.info(types), offset)?; |
4157 | 0 | } |
4158 | 4.12k | Ok(ComponentDefinedType::Stream { ty, info }) |
4159 | | } |
4160 | | } |
4161 | 227k | } |
4162 | | |
4163 | 7.04k | fn create_record_type( |
4164 | 7.04k | &self, |
4165 | 7.04k | fields: &[(&str, crate::ComponentValType)], |
4166 | 7.04k | types: &TypeList, |
4167 | 7.04k | offset: usize, |
4168 | 7.04k | ) -> Result<ComponentDefinedType> { |
4169 | 7.04k | let mut info = TypeInfo::new(); |
4170 | 7.04k | let mut field_map = IndexMap::default(); |
4171 | 7.04k | field_map.reserve(fields.len()); |
4172 | | |
4173 | 7.04k | if fields.is_empty() { |
4174 | 0 | bail!(offset, "record type must have at least one field"); |
4175 | 7.04k | } |
4176 | | |
4177 | 25.6k | for (name, ty) in fields { |
4178 | 25.6k | let kebab = to_kebab_string(name, "record field", offset)?; |
4179 | 25.6k | let ty = self.create_component_val_type(*ty, offset)?; |
4180 | | |
4181 | 25.6k | match field_map.entry(kebab) { |
4182 | 0 | Entry::Occupied(e) => bail!( |
4183 | 0 | offset, |
4184 | | "record field name `{name}` conflicts with previous field name `{prev}`", |
4185 | 0 | prev = e.key() |
4186 | | ), |
4187 | 25.6k | Entry::Vacant(e) => { |
4188 | 25.6k | info.combine(ty.info(types), offset)?; |
4189 | 25.6k | e.insert(ty); |
4190 | | } |
4191 | | } |
4192 | | } |
4193 | | |
4194 | 7.04k | Ok(ComponentDefinedType::Record(RecordType { |
4195 | 7.04k | info, |
4196 | 7.04k | fields: field_map, |
4197 | 7.04k | })) |
4198 | 7.04k | } |
4199 | | |
4200 | 8.06k | fn create_variant_type( |
4201 | 8.06k | &self, |
4202 | 8.06k | cases: &[crate::VariantCase], |
4203 | 8.06k | types: &TypeList, |
4204 | 8.06k | offset: usize, |
4205 | 8.06k | ) -> Result<ComponentDefinedType> { |
4206 | 8.06k | let mut info = TypeInfo::new(); |
4207 | 8.06k | let mut case_map: IndexMap<KebabString, VariantCase> = IndexMap::default(); |
4208 | 8.06k | case_map.reserve(cases.len()); |
4209 | | |
4210 | 8.06k | if cases.is_empty() { |
4211 | 0 | bail!(offset, "variant type must have at least one case"); |
4212 | 8.06k | } |
4213 | | |
4214 | 8.06k | if cases.len() > u32::MAX as usize { |
4215 | 0 | return Err(Error::new( |
4216 | 0 | "variant type cannot be represented with a 32-bit discriminant value", |
4217 | 0 | offset, |
4218 | 0 | )); |
4219 | 8.06k | } |
4220 | | |
4221 | 36.7k | for case in cases { |
4222 | 36.7k | let name = to_kebab_string(case.name, "variant case", offset)?; |
4223 | | |
4224 | 36.7k | let ty = case |
4225 | 36.7k | .ty |
4226 | 36.7k | .map(|ty| self.create_component_val_type(ty, offset)) |
4227 | 36.7k | .transpose()?; |
4228 | | |
4229 | 36.7k | match case_map.entry(name) { |
4230 | 0 | Entry::Occupied(e) => bail!( |
4231 | 0 | offset, |
4232 | | "variant case name `{name}` conflicts with previous case name `{prev}`", |
4233 | | name = case.name, |
4234 | 0 | prev = e.key() |
4235 | | ), |
4236 | 36.7k | Entry::Vacant(e) => { |
4237 | 36.7k | if let Some(ty) = ty { |
4238 | 33.9k | info.combine(ty.info(types), offset)?; |
4239 | 2.82k | } |
4240 | 36.7k | e.insert(VariantCase { ty }); |
4241 | | } |
4242 | | } |
4243 | | } |
4244 | | |
4245 | 8.06k | Ok(ComponentDefinedType::Variant(VariantType { |
4246 | 8.06k | info, |
4247 | 8.06k | cases: case_map, |
4248 | 8.06k | })) |
4249 | 8.06k | } |
4250 | | |
4251 | 41.8k | fn create_tuple_type( |
4252 | 41.8k | &self, |
4253 | 41.8k | tys: &[crate::ComponentValType], |
4254 | 41.8k | types: &TypeList, |
4255 | 41.8k | offset: usize, |
4256 | 41.8k | ) -> Result<ComponentDefinedType> { |
4257 | 41.8k | let mut info = TypeInfo::new(); |
4258 | 41.8k | if tys.is_empty() { |
4259 | 0 | bail!(offset, "tuple type must have at least one type"); |
4260 | 41.8k | } |
4261 | 41.8k | let types = tys |
4262 | 41.8k | .iter() |
4263 | 151k | .map(|ty| { |
4264 | 151k | let ty = self.create_component_val_type(*ty, offset)?; |
4265 | 151k | info.combine(ty.info(types), offset)?; |
4266 | 151k | Ok(ty) |
4267 | 151k | }) |
4268 | 41.8k | .collect::<Result<_>>()?; |
4269 | | |
4270 | 41.8k | Ok(ComponentDefinedType::Tuple(TupleType { info, types })) |
4271 | 41.8k | } |
4272 | | |
4273 | 1.27k | fn create_flags_type(&self, names: &[&str], offset: usize) -> Result<ComponentDefinedType> { |
4274 | 1.27k | let mut names_set = IndexSet::default(); |
4275 | 1.27k | names_set.reserve(names.len()); |
4276 | | |
4277 | 1.27k | if names.is_empty() { |
4278 | 0 | bail!(offset, "flags must have at least one entry"); |
4279 | 1.27k | } |
4280 | | |
4281 | 1.27k | if names.len() > 32 { |
4282 | 0 | bail!(offset, "cannot have more than 32 flags"); |
4283 | 1.27k | } |
4284 | | |
4285 | 4.01k | for name in names { |
4286 | 4.01k | let kebab = to_kebab_string(name, "flag", offset)?; |
4287 | 4.01k | if let Some(prev) = names_set.replace(kebab) { |
4288 | 0 | bail!( |
4289 | 0 | offset, |
4290 | | "flag name `{name}` conflicts with previous flag name `{prev}`", |
4291 | | ); |
4292 | 4.01k | } |
4293 | | } |
4294 | | |
4295 | 1.27k | Ok(ComponentDefinedType::Flags(names_set)) |
4296 | 1.27k | } |
4297 | | |
4298 | 88.6k | fn create_enum_type(&self, cases: &[&str], offset: usize) -> Result<ComponentDefinedType> { |
4299 | 88.6k | if cases.len() > u32::MAX as usize { |
4300 | 0 | return Err(Error::new( |
4301 | 0 | "enumeration type cannot be represented with a 32-bit discriminant value", |
4302 | 0 | offset, |
4303 | 0 | )); |
4304 | 88.6k | } |
4305 | | |
4306 | 88.6k | if cases.is_empty() { |
4307 | 0 | bail!(offset, "enum type must have at least one variant"); |
4308 | 88.6k | } |
4309 | | |
4310 | 88.6k | let mut tags = IndexSet::default(); |
4311 | 88.6k | tags.reserve(cases.len()); |
4312 | | |
4313 | 638k | for tag in cases { |
4314 | 638k | let kebab = to_kebab_string(tag, "enum tag", offset)?; |
4315 | 638k | if let Some(prev) = tags.replace(kebab) { |
4316 | 0 | bail!( |
4317 | 0 | offset, |
4318 | | "enum tag name `{tag}` conflicts with previous tag name `{prev}`", |
4319 | | ); |
4320 | 638k | } |
4321 | | } |
4322 | | |
4323 | 88.6k | Ok(ComponentDefinedType::Enum(tags)) |
4324 | 88.6k | } |
4325 | | |
4326 | 434k | fn create_component_val_type( |
4327 | 434k | &self, |
4328 | 434k | ty: crate::ComponentValType, |
4329 | 434k | offset: usize, |
4330 | 434k | ) -> Result<ComponentValType> { |
4331 | 434k | Ok(match ty { |
4332 | 296k | crate::ComponentValType::Primitive(pt) => { |
4333 | 296k | self.check_primitive_type(pt, offset)?; |
4334 | 296k | ComponentValType::Primitive(pt) |
4335 | | } |
4336 | 137k | crate::ComponentValType::Type(idx) => { |
4337 | 137k | ComponentValType::Type(self.defined_type_at(idx, offset)?) |
4338 | | } |
4339 | | }) |
4340 | 434k | } |
4341 | | |
4342 | 0 | pub fn core_type_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreTypeId> { |
4343 | 0 | self.core_types |
4344 | 0 | .get(idx as usize) |
4345 | 0 | .copied() |
4346 | 0 | .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) |
4347 | 0 | } |
4348 | | |
4349 | 453k | pub fn component_type_at(&self, idx: u32, offset: usize) -> Result<ComponentAnyTypeId> { |
4350 | 453k | self.types |
4351 | 453k | .get(idx as usize) |
4352 | 453k | .copied() |
4353 | 453k | .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) |
4354 | 453k | } |
4355 | | |
4356 | 9.12k | fn function_type_at<'a>( |
4357 | 9.12k | &self, |
4358 | 9.12k | idx: u32, |
4359 | 9.12k | types: &'a TypeList, |
4360 | 9.12k | offset: usize, |
4361 | 9.12k | ) -> Result<&'a ComponentFuncType> { |
4362 | 9.12k | let id = self.component_type_at(idx, offset)?; |
4363 | 9.12k | match id { |
4364 | 9.12k | ComponentAnyTypeId::Func(id) => Ok(&types[id]), |
4365 | 0 | _ => bail!(offset, "type index {idx} is not a function type"), |
4366 | | } |
4367 | 9.12k | } |
4368 | | |
4369 | 22.5k | fn function_at(&self, idx: u32, offset: usize) -> Result<ComponentFuncTypeId> { |
4370 | 22.5k | self.funcs.get(idx as usize).copied().ok_or_else(|| { |
4371 | 0 | format_err!( |
4372 | 0 | offset, |
4373 | | "unknown function {idx}: function index out of bounds" |
4374 | | ) |
4375 | 0 | }) |
4376 | 22.5k | } |
4377 | | |
4378 | 5.16k | fn component_at(&self, idx: u32, offset: usize) -> Result<ComponentTypeId> { |
4379 | 5.16k | self.components.get(idx as usize).copied().ok_or_else(|| { |
4380 | 0 | format_err!( |
4381 | 0 | offset, |
4382 | | "unknown component {idx}: component index out of bounds" |
4383 | | ) |
4384 | 0 | }) |
4385 | 5.16k | } |
4386 | | |
4387 | 16.5k | fn instance_at(&self, idx: u32, offset: usize) -> Result<ComponentInstanceTypeId> { |
4388 | 16.5k | self.instances.get(idx as usize).copied().ok_or_else(|| { |
4389 | 0 | format_err!( |
4390 | 0 | offset, |
4391 | | "unknown instance {idx}: instance index out of bounds" |
4392 | | ) |
4393 | 0 | }) |
4394 | 16.5k | } |
4395 | | |
4396 | 0 | fn value_at(&mut self, idx: u32, offset: usize) -> Result<&ComponentValType> { |
4397 | 0 | match self.values.get_mut(idx as usize) { |
4398 | 0 | Some((ty, used)) if !*used => { |
4399 | 0 | *used = true; |
4400 | 0 | Ok(ty) |
4401 | | } |
4402 | 0 | Some(_) => bail!(offset, "value {idx} cannot be used more than once"), |
4403 | 0 | None => bail!(offset, "unknown value {idx}: value index out of bounds"), |
4404 | | } |
4405 | 0 | } |
4406 | | |
4407 | 141k | fn defined_type_at(&self, idx: u32, offset: usize) -> Result<ComponentDefinedTypeId> { |
4408 | 141k | match self.component_type_at(idx, offset)? { |
4409 | 141k | ComponentAnyTypeId::Defined(id) => Ok(id), |
4410 | 0 | _ => bail!(offset, "type index {idx} is not a defined type"), |
4411 | | } |
4412 | 141k | } |
4413 | | |
4414 | 68.5k | fn core_function_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> { |
4415 | 68.5k | match self.core_funcs.get(idx as usize) { |
4416 | 68.5k | Some(id) => Ok(*id), |
4417 | 0 | None => bail!( |
4418 | 0 | offset, |
4419 | | "unknown core function {idx}: function index out of bounds" |
4420 | | ), |
4421 | | } |
4422 | 68.5k | } |
4423 | | |
4424 | 21.7k | fn module_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreModuleTypeId> { |
4425 | 21.7k | match self.core_modules.get(idx as usize) { |
4426 | 21.7k | Some(id) => Ok(*id), |
4427 | 0 | None => bail!(offset, "unknown module {idx}: module index out of bounds"), |
4428 | | } |
4429 | 21.7k | } |
4430 | | |
4431 | 61.9k | fn core_instance_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreInstanceTypeId> { |
4432 | 61.9k | match self.core_instances.get(idx as usize) { |
4433 | 61.9k | Some(id) => Ok(*id), |
4434 | 0 | None => bail!( |
4435 | 0 | offset, |
4436 | | "unknown core instance {idx}: instance index out of bounds" |
4437 | | ), |
4438 | | } |
4439 | 61.9k | } |
4440 | | |
4441 | 45.1k | fn core_instance_export<'a>( |
4442 | 45.1k | &self, |
4443 | 45.1k | instance_index: u32, |
4444 | 45.1k | name: &str, |
4445 | 45.1k | types: &'a TypeList, |
4446 | 45.1k | offset: usize, |
4447 | 45.1k | ) -> Result<&'a EntityType> { |
4448 | 45.1k | match types[self.core_instance_at(instance_index, offset)?] |
4449 | 45.1k | .internal_exports(types) |
4450 | 45.1k | .get(name) |
4451 | | { |
4452 | 45.1k | Some(export) => Ok(export), |
4453 | 0 | None => bail!( |
4454 | 0 | offset, |
4455 | | "core instance {instance_index} has no export named `{name}`" |
4456 | | ), |
4457 | | } |
4458 | 45.1k | } |
4459 | | |
4460 | 0 | fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> { |
4461 | 0 | match self.core_globals.get(idx as usize) { |
4462 | 0 | Some(t) => Ok(t), |
4463 | 0 | None => bail!(offset, "unknown global {idx}: global index out of bounds"), |
4464 | | } |
4465 | 0 | } |
4466 | | |
4467 | 2.03k | fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> { |
4468 | 2.03k | match self.core_tables.get(idx as usize) { |
4469 | 2.03k | Some(t) => Ok(t), |
4470 | 0 | None => bail!(offset, "unknown table {idx}: table index out of bounds"), |
4471 | | } |
4472 | 2.03k | } |
4473 | | |
4474 | 8.44k | fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> { |
4475 | 8.44k | match self.core_memories.get(idx as usize) { |
4476 | 8.44k | Some(t) => Ok(t), |
4477 | 0 | None => bail!(offset, "unknown memory {idx}: memory index out of bounds"), |
4478 | | } |
4479 | 8.44k | } |
4480 | | |
4481 | 0 | fn tag_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> { |
4482 | 0 | match self.core_tags.get(idx as usize) { |
4483 | 0 | Some(t) => Ok(*t), |
4484 | 0 | None => bail!(offset, "unknown tag {idx}: tag index out of bounds"), |
4485 | | } |
4486 | 0 | } |
4487 | | |
4488 | | /// Validates that the linear memory at `idx` is valid to use as a canonical |
4489 | | /// ABI memory. |
4490 | | /// |
4491 | | /// At this time this requires that the memory is a plain 32-bit or 64-bit linear |
4492 | | /// memory. Notably this disallows shared memory. |
4493 | 5.34k | fn cabi_memory_at(&self, idx: u32, offset: usize) -> Result<PtrSize> { |
4494 | 5.34k | let ty = self.memory_at(idx, offset)?; |
4495 | 5.34k | let valid_memory_type = MemoryType { |
4496 | 5.34k | initial: 0, |
4497 | 5.34k | maximum: None, |
4498 | 5.34k | memory64: ty.memory64, |
4499 | 5.34k | shared: false, |
4500 | 5.34k | page_size_log2: ty.page_size_log2, |
4501 | 5.34k | }; |
4502 | 5.34k | if ty.memory64 { |
4503 | 0 | require_feature::cm64( |
4504 | 0 | self.features, |
4505 | | "64-bit memories require the `cm64` feature to be enabled", |
4506 | 0 | offset, |
4507 | 0 | )?; |
4508 | 5.34k | } |
4509 | 5.34k | SubtypeCx::memory_type(ty, &valid_memory_type, offset)?; |
4510 | 5.34k | Ok(if ty.memory64 { |
4511 | 0 | PtrSize::Ptr64 |
4512 | | } else { |
4513 | 5.34k | PtrSize::Ptr32 |
4514 | | }) |
4515 | 5.34k | } |
4516 | | |
4517 | | /// Completes the translation of this component, performing final |
4518 | | /// validation of its structure. |
4519 | | /// |
4520 | | /// This method is required to be called for translating all components. |
4521 | | /// Internally this will convert local data structures into a |
4522 | | /// `ComponentType` which is suitable to use to describe the type of this |
4523 | | /// component. |
4524 | 87.6k | pub fn finish(&mut self, types: &TypeAlloc, offset: usize) -> Result<ComponentType> { |
4525 | 87.6k | let mut ty = ComponentType { |
4526 | 87.6k | // Inherit some fields based on translation of the component. |
4527 | 87.6k | info: self.type_info, |
4528 | 87.6k | imports: self.imports.clone(), |
4529 | 87.6k | exports: self.exports.clone(), |
4530 | 87.6k | |
4531 | 87.6k | // This is filled in as a subset of `self.defined_resources` |
4532 | 87.6k | // depending on what's actually used by the exports. See the |
4533 | 87.6k | // bottom of this function. |
4534 | 87.6k | defined_resources: Default::default(), |
4535 | 87.6k | |
4536 | 87.6k | // These are inherited directly from what was calculated for this |
4537 | 87.6k | // component. |
4538 | 87.6k | imported_resources: mem::take(&mut self.imported_resources) |
4539 | 87.6k | .into_iter() |
4540 | 87.6k | .collect(), |
4541 | 87.6k | explicit_resources: mem::take(&mut self.explicit_resources), |
4542 | 87.6k | }; |
4543 | | |
4544 | | // Collect all "free variables", or resources, from the imports of this |
4545 | | // component. None of the resources defined within this component can |
4546 | | // be used as part of the exports. This set is then used to reject any |
4547 | | // of `self.defined_resources` which show up. |
4548 | 87.6k | let mut free = IndexSet::default(); |
4549 | 87.6k | for ty in ty.imports.values() { |
4550 | 33.8k | types.free_variables_component_entity(&ty.ty, &mut free); |
4551 | 33.8k | } |
4552 | 87.6k | for (resource, _path) in self.defined_resources.iter() { |
4553 | | // FIXME: this error message is quite opaque and doesn't indicate |
4554 | | // more contextual information such as: |
4555 | | // |
4556 | | // * what was the exported resource found in the imports |
4557 | | // * which import was the resource found within |
4558 | | // |
4559 | | // These are possible to calculate here if necessary, however. |
4560 | 2.25k | if free.contains(resource) { |
4561 | 0 | bail!(offset, "local resource type found in imports"); |
4562 | 2.25k | } |
4563 | | } |
4564 | | |
4565 | | // The next step in validation a component, with respect to resources, |
4566 | | // is to minimize the set of defined resources to only those that |
4567 | | // are actually used by the exports. This weeds out resources that are |
4568 | | // defined, used within a component, and never exported, for example. |
4569 | | // |
4570 | | // The free variables of all exports are inserted into the `free` set |
4571 | | // (which is reused from the imports after clearing it). The defined |
4572 | | // resources calculated for this component are then inserted into this |
4573 | | // type's list of defined resources if it's contained somewhere in |
4574 | | // the free variables. |
4575 | | // |
4576 | | // Note that at the same time all defined resources must be exported, |
4577 | | // somehow, transitively from this component. The `explicit_resources` |
4578 | | // map is consulted for this purpose which lists all explicitly |
4579 | | // exported resources in the component, regardless from whence they |
4580 | | // came. If not present in this map then it's not exported and an error |
4581 | | // is returned. |
4582 | | // |
4583 | | // NB: the "types are exported" check is probably sufficient nowadays |
4584 | | // that the check of the `explicit_resources` map is probably not |
4585 | | // necessary, but it's left here for completeness and out of an |
4586 | | // abundance of caution. |
4587 | 87.6k | free.clear(); |
4588 | 145k | for ty in ty.exports.values() { |
4589 | 145k | types.free_variables_component_entity(&ty.ty, &mut free); |
4590 | 145k | } |
4591 | 87.6k | for (id, _rep) in mem::take(&mut self.defined_resources) { |
4592 | 2.25k | if !free.contains(&id) { |
4593 | 0 | continue; |
4594 | 2.25k | } |
4595 | | |
4596 | 2.25k | let path = match ty.explicit_resources.get(&id).cloned() { |
4597 | 2.25k | Some(path) => path, |
4598 | | // FIXME: this error message is quite opaque and doesn't |
4599 | | // indicate more contextual information such as: |
4600 | | // |
4601 | | // * which resource wasn't found in an export |
4602 | | // * which export has a reference to the resource |
4603 | | // |
4604 | | // These are possible to calculate here if necessary, however. |
4605 | 0 | None => bail!( |
4606 | 0 | offset, |
4607 | | "local resource type found in export but not exported itself" |
4608 | | ), |
4609 | | }; |
4610 | | |
4611 | 2.25k | ty.defined_resources.push((id, path)); |
4612 | | } |
4613 | | |
4614 | 87.6k | Ok(ty) |
4615 | 87.6k | } |
4616 | | |
4617 | 0 | fn check_value_support(&self, offset: usize) -> Result<()> { |
4618 | 0 | require_feature::cm_values( |
4619 | 0 | self.features, |
4620 | | "support for component model `value`s is not enabled", |
4621 | 0 | offset, |
4622 | 0 | )?; |
4623 | 0 | Ok(()) |
4624 | 0 | } |
4625 | | |
4626 | 299k | fn check_primitive_type(&self, ty: crate::PrimitiveValType, offset: usize) -> Result<()> { |
4627 | 299k | if ty == crate::PrimitiveValType::ErrorContext { |
4628 | 40.8k | require_feature::cm_error_context( |
4629 | 40.8k | self.features, |
4630 | | "`error-context` requires the component model error-context feature", |
4631 | 40.8k | offset, |
4632 | 0 | )?; |
4633 | 258k | } |
4634 | 299k | Ok(()) |
4635 | 299k | } |
4636 | | } |
4637 | | |
4638 | | impl InternRecGroup for ComponentState { |
4639 | 0 | fn features(&self) -> &WasmFeatures { |
4640 | 0 | &self.features |
4641 | 0 | } |
4642 | | |
4643 | 0 | fn add_type_id(&mut self, id: CoreTypeId) { |
4644 | 0 | self.core_types.push(ComponentCoreTypeId::Sub(id)); |
4645 | 0 | } |
4646 | | |
4647 | 0 | fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> { |
4648 | 0 | match self.core_type_at(idx, offset)? { |
4649 | 0 | ComponentCoreTypeId::Sub(id) => Ok(id), |
4650 | | ComponentCoreTypeId::Module(_) => { |
4651 | 0 | bail!(offset, "type index {idx} is a module type, not a sub type"); |
4652 | | } |
4653 | | } |
4654 | 0 | } |
4655 | | |
4656 | 0 | fn types_len(&self) -> u32 { |
4657 | 0 | u32::try_from(self.core_types.len()).unwrap() |
4658 | 0 | } |
4659 | | } |
4660 | | |
4661 | | impl ComponentNameContext { |
4662 | | /// Registers that the resource `id` is named `name` within this context. |
4663 | 5.54k | fn register(&mut self, name: &str, id: AliasableResourceId) { |
4664 | 5.54k | let idx = self.all_resource_names.len(); |
4665 | 5.54k | let prev = self.resource_name_map.insert(id, idx); |
4666 | 5.54k | assert!( |
4667 | 5.54k | prev.is_none(), |
4668 | | "for {id:?}, inserted {idx:?} but already had {prev:?}" |
4669 | | ); |
4670 | 5.54k | self.all_resource_names.insert(name.to_string()); |
4671 | 5.54k | } |
4672 | | |
4673 | 295k | fn validate_extern( |
4674 | 295k | &self, |
4675 | 295k | name: &ComponentExternName<'_>, |
4676 | 295k | kind: ExternKind, |
4677 | 295k | ty: &ComponentEntityType, |
4678 | 295k | types: &TypeAlloc, |
4679 | 295k | offset: usize, |
4680 | 295k | kind_names: &mut IndexSet<ComponentName>, |
4681 | 295k | items: &mut IndexMap<String, ComponentItem>, |
4682 | 295k | info: &mut TypeInfo, |
4683 | 295k | features: &WasmFeatures, |
4684 | 295k | ) -> Result<()> { |
4685 | | let ComponentExternName { |
4686 | 295k | name, |
4687 | 295k | implements, |
4688 | 295k | external_id, |
4689 | 295k | version_suffix, |
4690 | 295k | } = *name; |
4691 | | // First validate that `name` is even a valid kebab name, meaning it's |
4692 | | // in kebab-case, is an ID, etc. |
4693 | 295k | let kebab = |
4694 | 295k | ComponentName::new_with_features(name, offset, *features).with_context(|| { |
4695 | 0 | format!("{} name `{name}` is not a valid extern name", kind.desc(),) |
4696 | 0 | })?; |
4697 | | |
4698 | 295k | if let ExternKind::Export = kind { |
4699 | 261k | match kebab.kind() { |
4700 | | ComponentNameKind::Label(_) |
4701 | | | ComponentNameKind::Method(_) |
4702 | | | ComponentNameKind::Static(_) |
4703 | | | ComponentNameKind::Constructor(_) |
4704 | 261k | | ComponentNameKind::Interface(_) => {} |
4705 | | |
4706 | | ComponentNameKind::Hash(_) |
4707 | | | ComponentNameKind::Url(_) |
4708 | | | ComponentNameKind::Dependency(_) => { |
4709 | 0 | bail!(offset, "name `{name}` is not a valid export name") |
4710 | | } |
4711 | | } |
4712 | 33.8k | } |
4713 | | |
4714 | 295k | if let Some(implements) = implements { |
4715 | 11.6k | require_feature::cm_implements( |
4716 | 11.6k | *features, |
4717 | | "the `cm-implements` feature is not active", |
4718 | 11.6k | offset, |
4719 | 0 | )?; |
4720 | 11.6k | match kebab.kind() { |
4721 | 11.6k | ComponentNameKind::Label(_) => {} |
4722 | 0 | _ => bail!(offset, "name `{name}` is not valid with `implements`",), |
4723 | | } |
4724 | | |
4725 | 11.6k | match ty { |
4726 | 11.6k | ComponentEntityType::Instance(_) => {} |
4727 | 0 | _ => bail!(offset, "only instances can have an `implements`"), |
4728 | | } |
4729 | | |
4730 | 11.6k | let implements = ComponentName::new_with_features(implements, offset, *features) |
4731 | 11.6k | .with_context(|| format!("`{implements}` is not a valid name"))?; |
4732 | 11.6k | match implements.kind() { |
4733 | 11.6k | ComponentNameKind::Interface(_) => {} |
4734 | 0 | _ => bail!(offset, "name `{implements}` must be an interface"), |
4735 | | } |
4736 | 283k | } |
4737 | | |
4738 | 295k | if let Some(_) = version_suffix { |
4739 | 0 | require_feature::cm_canon_names( |
4740 | 0 | *features, |
4741 | | "the `cm-canon-names` feature is not active", |
4742 | 0 | offset, |
4743 | 0 | )?; |
4744 | 0 | match ty { |
4745 | 0 | ComponentEntityType::Instance(_) => {} |
4746 | 0 | _ => bail!(offset, "only instances can have an `versionsuffix`"), |
4747 | | } |
4748 | 295k | } |
4749 | | |
4750 | 295k | if let Some(_) = external_id { |
4751 | 156k | require_feature::cm_implements( |
4752 | 156k | *features, |
4753 | | "the `cm-implements` feature is not active", |
4754 | 156k | offset, |
4755 | 0 | )?; |
4756 | 139k | } |
4757 | | |
4758 | | // Validate that the kebab name, if it has structure such as |
4759 | | // `[method]a.b`, is indeed valid with respect to known resources. |
4760 | 295k | self.validate(&kebab, version_suffix, ty, types, offset) |
4761 | 295k | .with_context(|| format!("{} name `{kebab}` is not valid", kind.desc()))?; |
4762 | | |
4763 | | // Top-level kebab-names must all be unique, even between both imports |
4764 | | // and exports ot a component. For those names consult the `kebab_names` |
4765 | | // set. |
4766 | 295k | if let Some(prev) = kind_names.replace(kebab.clone()) { |
4767 | 0 | bail!( |
4768 | 0 | offset, |
4769 | | "{} name `{kebab}` conflicts with previous name `{prev}`", |
4770 | 0 | kind.desc() |
4771 | | ); |
4772 | 295k | } |
4773 | | |
4774 | | // Otherwise all strings must be unique, regardless of their name, so |
4775 | | // consult the `items` set to ensure that we're not for example |
4776 | | // importing the same interface ID twice. |
4777 | 295k | match items.entry(name.to_string()) { |
4778 | 0 | Entry::Occupied(e) => { |
4779 | 0 | bail!( |
4780 | 0 | offset, |
4781 | | "{kind} name `{name}` conflicts with previous name `{prev}`", |
4782 | 0 | kind = kind.desc(), |
4783 | 0 | prev = e.key(), |
4784 | | ); |
4785 | | } |
4786 | 295k | Entry::Vacant(e) => { |
4787 | 295k | e.insert(ComponentItem { |
4788 | 295k | ty: *ty, |
4789 | 295k | implements: implements.map(|s| s.to_string()), |
4790 | 295k | version_suffix: version_suffix.map(|s| s.to_string()), |
4791 | 295k | external_id: external_id.map(|s| s.to_string()), |
4792 | | }); |
4793 | 295k | info.combine(ty.info(types), offset)?; |
4794 | | } |
4795 | | } |
4796 | 295k | Ok(()) |
4797 | 295k | } |
4798 | | |
4799 | | /// Validates that the `name` provided is allowed to have the type `ty`. |
4800 | 295k | fn validate( |
4801 | 295k | &self, |
4802 | 295k | name: &ComponentName, |
4803 | 295k | version_suffix: Option<&str>, |
4804 | 295k | ty: &ComponentEntityType, |
4805 | 295k | types: &TypeAlloc, |
4806 | 295k | offset: usize, |
4807 | 295k | ) -> Result<()> { |
4808 | 295k | let func = || { |
4809 | 10.8k | let id = match ty { |
4810 | 10.8k | ComponentEntityType::Func(id) => *id, |
4811 | 0 | _ => bail!(offset, "item is not a func"), |
4812 | | }; |
4813 | 10.8k | Ok(&types[id]) |
4814 | 10.8k | }; |
4815 | | |
4816 | 295k | match name.kind() { |
4817 | | // No validation necessary for these styles of names |
4818 | | ComponentNameKind::Label(_) |
4819 | | | ComponentNameKind::Url(_) |
4820 | | | ComponentNameKind::Hash(_) |
4821 | 228k | | ComponentNameKind::Dependency(_) => {} |
4822 | | |
4823 | | // Validate the `version_suffix` field in the context of interface |
4824 | | // names. |
4825 | 56.4k | ComponentNameKind::Interface(name) => { |
4826 | 56.4k | if let Err(e) = name.version(version_suffix) { |
4827 | 0 | bail!(offset, "invalid interface version: {e}"); |
4828 | 56.4k | } |
4829 | | } |
4830 | | |
4831 | | // Constructors must return `(own $resource)` or |
4832 | | // `(result (own $Tresource))` and the `$resource` must be named |
4833 | | // within this context to match `rname`. |
4834 | 924 | ComponentNameKind::Constructor(rname) => { |
4835 | 924 | let ty = func()?; |
4836 | 924 | if ty.async_ { |
4837 | 0 | bail!(offset, "constructor function cannot be async"); |
4838 | 924 | } |
4839 | 924 | let ty = match ty.result { |
4840 | 924 | Some(result) => result, |
4841 | 0 | None => bail!(offset, "function should return one value"), |
4842 | | }; |
4843 | 924 | let resource = match ty { |
4844 | 0 | ComponentValType::Primitive(_) => None, |
4845 | 924 | ComponentValType::Type(ty) => match &types[ty] { |
4846 | 924 | ComponentDefinedType::Own(id) => Some(id), |
4847 | | ComponentDefinedType::Result { |
4848 | 0 | ok: Some(ComponentValType::Type(ok)), |
4849 | | .. |
4850 | 0 | } => match &types[*ok] { |
4851 | 0 | ComponentDefinedType::Own(id) => Some(id), |
4852 | 0 | _ => None, |
4853 | | }, |
4854 | 0 | _ => None, |
4855 | | }, |
4856 | | }; |
4857 | 924 | let resource = match resource { |
4858 | 924 | Some(id) => id, |
4859 | 0 | None => bail!( |
4860 | 0 | offset, |
4861 | | "function should return `(own $T)` or `(result (own $T))`" |
4862 | | ), |
4863 | | }; |
4864 | 924 | self.validate_resource_name(*resource, rname, offset)?; |
4865 | | } |
4866 | | |
4867 | | // Methods must take `(param "self" (borrow $resource))` as the |
4868 | | // first argument where `$resources` matches the name `resource` as |
4869 | | // named in this context. |
4870 | 3.87k | ComponentNameKind::Method(name) => { |
4871 | 3.87k | let ty = func()?; |
4872 | 3.87k | if ty.params.len() == 0 { |
4873 | 0 | bail!(offset, "function should have at least one argument"); |
4874 | 3.87k | } |
4875 | 3.87k | let (pname, pty) = &ty.params[0]; |
4876 | 3.87k | if pname.as_str() != "self" { |
4877 | 0 | bail!( |
4878 | 0 | offset, |
4879 | | "function should have a first argument called `self`", |
4880 | | ); |
4881 | 3.87k | } |
4882 | 3.87k | let id = match pty { |
4883 | 0 | ComponentValType::Primitive(_) => None, |
4884 | 3.87k | ComponentValType::Type(ty) => match &types[*ty] { |
4885 | 3.87k | ComponentDefinedType::Borrow(id) => Some(id), |
4886 | 0 | _ => None, |
4887 | | }, |
4888 | | }; |
4889 | 3.87k | let id = match id { |
4890 | 3.87k | Some(id) => id, |
4891 | 0 | None => bail!( |
4892 | 0 | offset, |
4893 | | "function should take a first argument of `(borrow $T)`" |
4894 | | ), |
4895 | | }; |
4896 | 3.87k | self.validate_resource_name(*id, name.resource(), offset)?; |
4897 | | } |
4898 | | |
4899 | | // Static methods don't have much validation beyond that they must |
4900 | | // be a function and the resource name referred to must already be |
4901 | | // in this context. |
4902 | 6.05k | ComponentNameKind::Static(name) => { |
4903 | 6.05k | func()?; |
4904 | 6.05k | if !self.all_resource_names.contains(name.resource().as_str()) { |
4905 | 0 | bail!(offset, "static resource name is not known in this context"); |
4906 | 6.05k | } |
4907 | | } |
4908 | | } |
4909 | | |
4910 | 295k | Ok(()) |
4911 | 295k | } |
4912 | | |
4913 | 4.79k | fn validate_resource_name( |
4914 | 4.79k | &self, |
4915 | 4.79k | id: AliasableResourceId, |
4916 | 4.79k | name: KebabStr<'_>, |
4917 | 4.79k | offset: usize, |
4918 | 4.79k | ) -> Result<()> { |
4919 | 4.79k | let expected_name_idx = match self.resource_name_map.get(&id) { |
4920 | 4.79k | Some(idx) => *idx, |
4921 | | None => { |
4922 | 0 | bail!( |
4923 | 0 | offset, |
4924 | | "resource used in function does not have a name in this context" |
4925 | | ) |
4926 | | } |
4927 | | }; |
4928 | 4.79k | let expected_name = &self.all_resource_names[expected_name_idx]; |
4929 | 4.79k | if name.as_str() != expected_name { |
4930 | 0 | bail!( |
4931 | 0 | offset, |
4932 | | "function does not match expected resource name `{expected_name}`" |
4933 | | ); |
4934 | 4.79k | } |
4935 | 4.79k | Ok(()) |
4936 | 4.79k | } |
4937 | | } |
4938 | | |
4939 | | use self::append_only::*; |
4940 | | |
4941 | | mod append_only { |
4942 | | use crate::prelude::IndexMap; |
4943 | | use core::hash::Hash; |
4944 | | use core::ops::Deref; |
4945 | | |
4946 | | pub struct IndexMapAppendOnly<K, V>(IndexMap<K, V>); |
4947 | | |
4948 | | impl<K, V> IndexMapAppendOnly<K, V> |
4949 | | where |
4950 | | K: Hash + Eq + Ord + PartialEq + Clone, |
4951 | | { |
4952 | 7.44k | pub fn insert(&mut self, key: K, value: V) { |
4953 | 7.44k | let prev = self.0.insert(key, value); |
4954 | 7.44k | assert!(prev.is_none()); |
4955 | 7.44k | } <wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, alloc::vec::Vec<usize>>>::insert Line | Count | Source | 4952 | 2.49k | pub fn insert(&mut self, key: K, value: V) { | 4953 | 2.49k | let prev = self.0.insert(key, value); | 4954 | 2.49k | assert!(prev.is_none()); | 4955 | 2.49k | } |
<wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, core::option::Option<wasmparser::readers::core::types::ValType>>>::insert Line | Count | Source | 4952 | 4.94k | pub fn insert(&mut self, key: K, value: V) { | 4953 | 4.94k | let prev = self.0.insert(key, value); | 4954 | 4.94k | assert!(prev.is_none()); | 4955 | 4.94k | } |
|
4956 | | } |
4957 | | |
4958 | | impl<K, V> Deref for IndexMapAppendOnly<K, V> { |
4959 | | type Target = IndexMap<K, V>; |
4960 | 140k | fn deref(&self) -> &IndexMap<K, V> { |
4961 | 140k | &self.0 |
4962 | 140k | } <wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, alloc::vec::Vec<usize>> as core::ops::deref::Deref>::deref Line | Count | Source | 4960 | 51.3k | fn deref(&self) -> &IndexMap<K, V> { | 4961 | 51.3k | &self.0 | 4962 | 51.3k | } |
<wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, core::option::Option<wasmparser::readers::core::types::ValType>> as core::ops::deref::Deref>::deref Line | Count | Source | 4960 | 88.9k | fn deref(&self) -> &IndexMap<K, V> { | 4961 | 88.9k | &self.0 | 4962 | 88.9k | } |
|
4963 | | } |
4964 | | |
4965 | | impl<K, V> Default for IndexMapAppendOnly<K, V> { |
4966 | 504k | fn default() -> Self { |
4967 | 504k | Self(Default::default()) |
4968 | 504k | } <wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, alloc::vec::Vec<usize>> as core::default::Default>::default Line | Count | Source | 4966 | 226k | fn default() -> Self { | 4967 | 226k | Self(Default::default()) | 4968 | 226k | } |
<wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, core::option::Option<wasmparser::readers::core::types::ValType>> as core::default::Default>::default Line | Count | Source | 4966 | 278k | fn default() -> Self { | 4967 | 278k | Self(Default::default()) | 4968 | 278k | } |
|
4969 | | } |
4970 | | |
4971 | | impl<K, V> IntoIterator for IndexMapAppendOnly<K, V> { |
4972 | | type IntoIter = <IndexMap<K, V> as IntoIterator>::IntoIter; |
4973 | | type Item = <IndexMap<K, V> as IntoIterator>::Item; |
4974 | 226k | fn into_iter(self) -> Self::IntoIter { |
4975 | 226k | self.0.into_iter() |
4976 | 226k | } <wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, alloc::vec::Vec<usize>> as core::iter::traits::collect::IntoIterator>::into_iter Line | Count | Source | 4974 | 87.6k | fn into_iter(self) -> Self::IntoIter { | 4975 | 87.6k | self.0.into_iter() | 4976 | 87.6k | } |
<wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, core::option::Option<wasmparser::readers::core::types::ValType>> as core::iter::traits::collect::IntoIterator>::into_iter Line | Count | Source | 4974 | 138k | fn into_iter(self) -> Self::IntoIter { | 4975 | 138k | self.0.into_iter() | 4976 | 138k | } |
|
4977 | | } |
4978 | | } |