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