/src/wasm-tools/crates/wasmparser/src/validator/component.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! State relating to validating a WebAssembly component. |
2 | | |
3 | | use super::{ |
4 | | check_max, |
5 | | component_types::{ |
6 | | Abi, AliasableResourceId, ComponentAnyTypeId, ComponentCoreInstanceTypeId, |
7 | | ComponentCoreModuleTypeId, ComponentCoreTypeId, ComponentDefinedType, |
8 | | ComponentDefinedTypeId, ComponentEntityType, ComponentFuncType, ComponentFuncTypeId, |
9 | | ComponentInstanceType, ComponentInstanceTypeId, ComponentType, ComponentTypeId, |
10 | | ComponentValType, Context, CoreInstanceTypeKind, InstanceType, LoweringInfo, ModuleType, |
11 | | RecordType, Remap, Remapping, ResourceId, SubtypeCx, TupleType, VariantCase, VariantType, |
12 | | }, |
13 | | core::{InternRecGroup, Module}, |
14 | | types::{CoreTypeId, EntityType, TypeAlloc, 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 | | BinaryReaderError, CanonicalOption, ComponentExportName, ComponentExternalKind, |
22 | | ComponentOuterAliasKind, ComponentTypeRef, CompositeInnerType, CompositeType, ExternalKind, |
23 | | FuncType, GlobalType, InstantiationArgKind, MemoryType, PackedIndex, RefType, Result, SubType, |
24 | | TableType, TypeBounds, ValType, WasmFeatures, |
25 | | }; |
26 | | use core::mem; |
27 | | |
28 | 1.70M | fn to_kebab_str<'a>(s: &'a str, desc: &str, offset: usize) -> Result<&'a KebabStr> { |
29 | 1.70M | match KebabStr::new(s) { |
30 | 1.70M | Some(s) => Ok(s), |
31 | | None => { |
32 | 0 | if s.is_empty() { |
33 | 0 | bail!(offset, "{desc} name cannot be empty"); |
34 | 0 | } |
35 | 0 |
|
36 | 0 | bail!(offset, "{desc} name `{s}` is not in kebab case"); |
37 | | } |
38 | | } |
39 | 1.70M | } |
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 | | |
46 | | // Core index spaces |
47 | | pub core_types: Vec<ComponentCoreTypeId>, |
48 | | pub core_funcs: Vec<CoreTypeId>, |
49 | | pub core_tags: Vec<CoreTypeId>, |
50 | | pub core_modules: Vec<ComponentCoreModuleTypeId>, |
51 | | pub core_instances: Vec<ComponentCoreInstanceTypeId>, |
52 | | pub core_memories: Vec<MemoryType>, |
53 | | pub core_tables: Vec<TableType>, |
54 | | pub core_globals: Vec<GlobalType>, |
55 | | |
56 | | // Component index spaces |
57 | | pub types: Vec<ComponentAnyTypeId>, |
58 | | pub funcs: Vec<ComponentFuncTypeId>, |
59 | | pub values: Vec<(ComponentValType, bool)>, |
60 | | pub instances: Vec<ComponentInstanceTypeId>, |
61 | | pub components: Vec<ComponentTypeId>, |
62 | | |
63 | | pub imports: IndexMap<String, ComponentEntityType>, |
64 | | pub import_names: IndexSet<ComponentName>, |
65 | | pub exports: IndexMap<String, ComponentEntityType>, |
66 | | pub export_names: IndexSet<ComponentName>, |
67 | | |
68 | | has_start: bool, |
69 | | type_info: TypeInfo, |
70 | | |
71 | | /// A mapping of imported resources in this component. |
72 | | /// |
73 | | /// This mapping represents all "type variables" imported into the |
74 | | /// component, or resources. This could be resources imported directly as |
75 | | /// a top-level type import or additionally transitively through other |
76 | | /// imported instances. |
77 | | /// |
78 | | /// The mapping element here is a "path" which is a list of indexes into |
79 | | /// the import map that will be generated for this component. Each index |
80 | | /// is an index into an `IndexMap`, and each list is guaranteed to have at |
81 | | /// least one element. |
82 | | /// |
83 | | /// An example of this map is: |
84 | | /// |
85 | | /// ```wasm |
86 | | /// (component |
87 | | /// ;; [0] - the first import |
88 | | /// (import "r" (type (sub resource))) |
89 | | /// |
90 | | /// ;; [1] - the second import |
91 | | /// (import "r2" (type (sub resource))) |
92 | | /// |
93 | | /// (import "i" (instance |
94 | | /// ;; [2, 0] - the third import, and the first export the instance |
95 | | /// (export "r3" (type (sub resource))) |
96 | | /// ;; [2, 1] - the third import, and the second export the instance |
97 | | /// (export "r4" (type (sub resource))) |
98 | | /// )) |
99 | | /// |
100 | | /// ;; ... |
101 | | /// ) |
102 | | /// ``` |
103 | | /// |
104 | | /// The `Vec<usize>` here can be thought of as `Vec<String>` but a |
105 | | /// (hopefully) more efficient representation. |
106 | | /// |
107 | | /// Finally note that this map is listed as an "append only" map because all |
108 | | /// insertions into it should always succeed. Any insertion which overlaps |
109 | | /// with a previous entry indicates a bug in the validator which needs to be |
110 | | /// corrected via other means. |
111 | | // |
112 | | // TODO: make these `SkolemResourceId` and then go fix all the compile |
113 | | // errors, don't add skolem things into the type area |
114 | | imported_resources: IndexMapAppendOnly<ResourceId, Vec<usize>>, |
115 | | |
116 | | /// A mapping of "defined" resources in this component, or those which |
117 | | /// are defined within the instantiation of this component. |
118 | | /// |
119 | | /// Defined resources, as the name implies, can sort of be thought of as |
120 | | /// "these are defined within the component". Note though that the means by |
121 | | /// which a local definition can occur are not simply those defined in the |
122 | | /// component but also in its transitively instantiated components |
123 | | /// internally. This means that this set closes over many transitive |
124 | | /// internal items in addition to those defined immediately in the component |
125 | | /// itself. |
126 | | /// |
127 | | /// The `Option<ValType>` in this mapping is whether or not the underlying |
128 | | /// representation of the resource is known to this component. Immediately |
129 | | /// defined resources, for example, will have `Some(I32)` here. Resources |
130 | | /// that come from transitively defined components, for example, will have |
131 | | /// `None`. In the type context all entries here are `None`. |
132 | | /// |
133 | | /// Note that like `imported_resources` all insertions into this map are |
134 | | /// expected to succeed to it's declared as append-only. |
135 | | defined_resources: IndexMapAppendOnly<ResourceId, Option<ValType>>, |
136 | | |
137 | | /// A mapping of explicitly exported resources from this component in |
138 | | /// addition to the path that they're exported at. |
139 | | /// |
140 | | /// For more information on the path here see the documentation for |
141 | | /// `imported_resources`. Note that the indexes here index into the |
142 | | /// list of exports of this component. |
143 | | explicit_resources: IndexMap<ResourceId, Vec<usize>>, |
144 | | |
145 | | /// The set of types which are considered "exported" from this component. |
146 | | /// |
147 | | /// This is added to whenever a type export is found, or an instance export |
148 | | /// which itself contains a type export. This additionally includes all |
149 | | /// imported types since those are suitable for export as well. |
150 | | /// |
151 | | /// This set is consulted whenever an exported item is added since all |
152 | | /// referenced types must be members of this set. |
153 | | exported_types: Set<ComponentAnyTypeId>, |
154 | | |
155 | | /// Same as `exported_types`, but for imports. |
156 | | imported_types: Set<ComponentAnyTypeId>, |
157 | | |
158 | | /// The set of top-level resource exports and their names. |
159 | | /// |
160 | | /// This context is used to validate method names such as `[method]foo.bar` |
161 | | /// to ensure that `foo` is an exported resource and that the type mentioned |
162 | | /// in a function type is actually named `foo`. |
163 | | /// |
164 | | /// Note that imports/exports have disjoint contexts to ensure that they're |
165 | | /// validated correctly. Namely you can't retroactively attach methods to an |
166 | | /// import, for example. |
167 | | toplevel_exported_resources: ComponentNameContext, |
168 | | |
169 | | /// Same as `toplevel_exported_resources`, but for imports. |
170 | | toplevel_imported_resources: ComponentNameContext, |
171 | | } |
172 | | |
173 | | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
174 | | pub enum ComponentKind { |
175 | | Component, |
176 | | InstanceType, |
177 | | ComponentType, |
178 | | } |
179 | | |
180 | | /// Helper context used to track information about resource names for method |
181 | | /// name validation. |
182 | | #[derive(Default)] |
183 | | struct ComponentNameContext { |
184 | | /// A map from a resource type id to an index in the `all_resource_names` |
185 | | /// set for the name of that resource. |
186 | | resource_name_map: Map<AliasableResourceId, usize>, |
187 | | |
188 | | /// All known resource names in this context, used to validate static method |
189 | | /// names to by ensuring that static methods' resource names are somewhere |
190 | | /// in this set. |
191 | | all_resource_names: IndexSet<String>, |
192 | | } |
193 | | |
194 | | #[derive(Debug, Copy, Clone)] |
195 | | pub enum ExternKind { |
196 | | Import, |
197 | | Export, |
198 | | } |
199 | | |
200 | | impl ExternKind { |
201 | 0 | pub fn desc(&self) -> &'static str { |
202 | 0 | match self { |
203 | 0 | ExternKind::Import => "import", |
204 | 0 | ExternKind::Export => "export", |
205 | | } |
206 | 0 | } |
207 | | } |
208 | | |
209 | | impl ComponentState { |
210 | 216k | pub fn new(kind: ComponentKind) -> Self { |
211 | 216k | Self { |
212 | 216k | kind, |
213 | 216k | core_types: Default::default(), |
214 | 216k | core_modules: Default::default(), |
215 | 216k | core_instances: Default::default(), |
216 | 216k | core_funcs: Default::default(), |
217 | 216k | core_memories: Default::default(), |
218 | 216k | core_tables: Default::default(), |
219 | 216k | core_globals: Default::default(), |
220 | 216k | core_tags: Default::default(), |
221 | 216k | types: Default::default(), |
222 | 216k | funcs: Default::default(), |
223 | 216k | values: Default::default(), |
224 | 216k | instances: Default::default(), |
225 | 216k | components: Default::default(), |
226 | 216k | imports: Default::default(), |
227 | 216k | exports: Default::default(), |
228 | 216k | import_names: Default::default(), |
229 | 216k | export_names: Default::default(), |
230 | 216k | has_start: Default::default(), |
231 | 216k | type_info: TypeInfo::new(), |
232 | 216k | imported_resources: Default::default(), |
233 | 216k | defined_resources: Default::default(), |
234 | 216k | explicit_resources: Default::default(), |
235 | 216k | exported_types: Default::default(), |
236 | 216k | imported_types: Default::default(), |
237 | 216k | toplevel_exported_resources: Default::default(), |
238 | 216k | toplevel_imported_resources: Default::default(), |
239 | 216k | } |
240 | 216k | } |
241 | | |
242 | 1.08M | pub fn type_count(&self) -> usize { |
243 | 1.08M | self.core_types.len() + self.types.len() |
244 | 1.08M | } |
245 | | |
246 | 165k | pub fn instance_count(&self) -> usize { |
247 | 165k | self.core_instances.len() + self.instances.len() |
248 | 165k | } |
249 | | |
250 | 515k | pub fn function_count(&self) -> usize { |
251 | 515k | self.core_funcs.len() + self.funcs.len() |
252 | 515k | } |
253 | | |
254 | 0 | pub fn add_core_type( |
255 | 0 | components: &mut [Self], |
256 | 0 | ty: crate::CoreType, |
257 | 0 | features: &WasmFeatures, |
258 | 0 | types: &mut TypeAlloc, |
259 | 0 | offset: usize, |
260 | 0 | check_limit: bool, |
261 | 0 | ) -> Result<()> { |
262 | 0 | let current = components.last_mut().unwrap(); |
263 | 0 | if check_limit { |
264 | 0 | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; |
265 | 0 | } |
266 | 0 | match ty { |
267 | 0 | crate::CoreType::Rec(rec) => { |
268 | 0 | current.canonicalize_and_intern_rec_group(features, types, rec, offset)?; |
269 | | } |
270 | 0 | crate::CoreType::Module(decls) => { |
271 | 0 | let mod_ty = Self::create_module_type( |
272 | 0 | components, |
273 | 0 | decls.into_vec(), |
274 | 0 | features, |
275 | 0 | types, |
276 | 0 | offset, |
277 | 0 | )?; |
278 | 0 | let id = ComponentCoreTypeId::Module(types.push_ty(mod_ty)); |
279 | 0 | components.last_mut().unwrap().core_types.push(id); |
280 | | } |
281 | | } |
282 | | |
283 | 0 | Ok(()) |
284 | 0 | } |
285 | | |
286 | 32.3k | pub fn add_core_module( |
287 | 32.3k | &mut self, |
288 | 32.3k | module: &Module, |
289 | 32.3k | types: &mut TypeAlloc, |
290 | 32.3k | offset: usize, |
291 | 32.3k | ) -> Result<()> { |
292 | 32.3k | let imports = module.imports_for_module_type(offset)?; |
293 | | |
294 | | // We have to clone the module's imports and exports here |
295 | | // because we cannot take the data out of the `MaybeOwned` |
296 | | // as it might be shared with a function validator. |
297 | 32.3k | let mod_ty = ModuleType { |
298 | 32.3k | info: TypeInfo::core(module.type_size), |
299 | 32.3k | imports, |
300 | 32.3k | exports: module.exports.clone(), |
301 | 32.3k | }; |
302 | 32.3k | |
303 | 32.3k | let mod_id = types.push_ty(mod_ty); |
304 | 32.3k | self.core_modules.push(mod_id); |
305 | 32.3k | |
306 | 32.3k | Ok(()) |
307 | 32.3k | } |
308 | | |
309 | 59.4k | pub fn add_core_instance( |
310 | 59.4k | &mut self, |
311 | 59.4k | instance: crate::Instance, |
312 | 59.4k | types: &mut TypeAlloc, |
313 | 59.4k | offset: usize, |
314 | 59.4k | ) -> Result<()> { |
315 | 59.4k | let instance = match instance { |
316 | 32.3k | crate::Instance::Instantiate { module_index, args } => { |
317 | 32.3k | self.instantiate_core_module(module_index, args.into_vec(), types, offset)? |
318 | | } |
319 | 27.0k | crate::Instance::FromExports(exports) => { |
320 | 27.0k | self.instantiate_core_exports(exports.into_vec(), types, offset)? |
321 | | } |
322 | | }; |
323 | | |
324 | 59.4k | self.core_instances.push(instance); |
325 | 59.4k | |
326 | 59.4k | Ok(()) |
327 | 59.4k | } |
328 | | |
329 | 1.19M | pub fn add_type( |
330 | 1.19M | components: &mut Vec<Self>, |
331 | 1.19M | ty: crate::ComponentType, |
332 | 1.19M | features: &WasmFeatures, |
333 | 1.19M | types: &mut TypeAlloc, |
334 | 1.19M | offset: usize, |
335 | 1.19M | check_limit: bool, |
336 | 1.19M | ) -> Result<()> { |
337 | 1.19M | assert!(!components.is_empty()); |
338 | | |
339 | 2.22M | fn current(components: &mut Vec<ComponentState>) -> &mut ComponentState { |
340 | 2.22M | components.last_mut().unwrap() |
341 | 2.22M | } |
342 | | |
343 | 1.19M | let id = match ty { |
344 | 828k | crate::ComponentType::Defined(ty) => { |
345 | 828k | let ty = current(components).create_defined_type(ty, types, features, offset)?; |
346 | 828k | types.push(ty).into() |
347 | | } |
348 | 197k | crate::ComponentType::Func(ty) => { |
349 | 197k | let ty = current(components).create_function_type(ty, types, features, offset)?; |
350 | 197k | types.push(ty).into() |
351 | | } |
352 | 76.5k | crate::ComponentType::Component(decls) => { |
353 | 76.5k | let ty = Self::create_component_type( |
354 | 76.5k | components, |
355 | 76.5k | decls.into_vec(), |
356 | 76.5k | features, |
357 | 76.5k | types, |
358 | 76.5k | offset, |
359 | 76.5k | )?; |
360 | 76.5k | types.push(ty).into() |
361 | | } |
362 | 89.9k | crate::ComponentType::Instance(decls) => { |
363 | 89.9k | let ty = Self::create_instance_type( |
364 | 89.9k | components, |
365 | 89.9k | decls.into_vec(), |
366 | 89.9k | features, |
367 | 89.9k | types, |
368 | 89.9k | offset, |
369 | 89.9k | )?; |
370 | 89.9k | types.push(ty).into() |
371 | | } |
372 | 5.28k | crate::ComponentType::Resource { rep, dtor } => { |
373 | 5.28k | let component = current(components); |
374 | 5.28k | |
375 | 5.28k | // Resource types cannot be declared in a type context, only |
376 | 5.28k | // within a component context. |
377 | 5.28k | if component.kind != ComponentKind::Component { |
378 | 0 | bail!( |
379 | 0 | offset, |
380 | 0 | "resources can only be defined within a concrete component" |
381 | 0 | ); |
382 | 5.28k | } |
383 | 5.28k | |
384 | 5.28k | // Current MVP restriction of the component model. |
385 | 5.28k | if rep != ValType::I32 { |
386 | 0 | bail!(offset, "resources can only be represented by `i32`"); |
387 | 5.28k | } |
388 | | |
389 | | // If specified validate that the destructor is both a valid |
390 | | // function and has the correct signature. |
391 | 5.28k | if let Some(dtor) = dtor { |
392 | 5.28k | let ty = component.core_function_at(dtor, offset)?; |
393 | 5.28k | let ty = types[ty].composite_type.unwrap_func(); |
394 | 5.28k | if ty.params() != [rep] || ty.results() != [] { |
395 | 0 | bail!( |
396 | 0 | offset, |
397 | 0 | "core function {dtor} has wrong signature for a destructor" |
398 | 0 | ); |
399 | 5.28k | } |
400 | 0 | } |
401 | | |
402 | | // As this is the introduction of a resource create a fresh new |
403 | | // identifier for the resource. This is then added into the |
404 | | // list of defined resources for this component, notably with a |
405 | | // rep listed to enable getting access to various intrinsics |
406 | | // such as `resource.rep`. |
407 | 5.28k | let id = types.alloc_resource_id(); |
408 | 5.28k | component.defined_resources.insert(id.resource(), Some(rep)); |
409 | 5.28k | id.into() |
410 | | } |
411 | | }; |
412 | | |
413 | 1.19M | let current = current(components); |
414 | 1.19M | if check_limit { |
415 | 826k | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; |
416 | 370k | } |
417 | 1.19M | current.types.push(id); |
418 | 1.19M | |
419 | 1.19M | Ok(()) |
420 | 1.19M | } |
421 | | |
422 | 150k | pub fn add_import( |
423 | 150k | &mut self, |
424 | 150k | import: crate::ComponentImport, |
425 | 150k | features: &WasmFeatures, |
426 | 150k | types: &mut TypeAlloc, |
427 | 150k | offset: usize, |
428 | 150k | ) -> Result<()> { |
429 | 150k | let mut entity = self.check_type_ref(&import.ty, features, types, offset)?; |
430 | 150k | self.add_entity( |
431 | 150k | &mut entity, |
432 | 150k | Some((import.name.0, ExternKind::Import)), |
433 | 150k | features, |
434 | 150k | types, |
435 | 150k | offset, |
436 | 150k | )?; |
437 | 150k | self.toplevel_imported_resources.validate_extern( |
438 | 150k | import.name.0, |
439 | 150k | ExternKind::Import, |
440 | 150k | &entity, |
441 | 150k | types, |
442 | 150k | offset, |
443 | 150k | &mut self.import_names, |
444 | 150k | &mut self.imports, |
445 | 150k | &mut self.type_info, |
446 | 150k | features, |
447 | 150k | )?; |
448 | 150k | Ok(()) |
449 | 150k | } |
450 | | |
451 | 976k | fn add_entity( |
452 | 976k | &mut self, |
453 | 976k | ty: &mut ComponentEntityType, |
454 | 976k | name_and_kind: Option<(&str, ExternKind)>, |
455 | 976k | features: &WasmFeatures, |
456 | 976k | types: &mut TypeAlloc, |
457 | 976k | offset: usize, |
458 | 976k | ) -> Result<()> { |
459 | 976k | let kind = name_and_kind.map(|(_, k)| k); |
460 | 976k | let (len, max, desc) = match ty { |
461 | 0 | ComponentEntityType::Module(id) => { |
462 | 0 | self.core_modules.push(*id); |
463 | 0 | (self.core_modules.len(), MAX_WASM_MODULES, "modules") |
464 | | } |
465 | 25.3k | ComponentEntityType::Component(id) => { |
466 | 25.3k | self.components.push(*id); |
467 | 25.3k | (self.components.len(), MAX_WASM_COMPONENTS, "components") |
468 | | } |
469 | 109k | ComponentEntityType::Instance(id) => { |
470 | 109k | match kind { |
471 | 25.3k | Some(ExternKind::Import) => self.prepare_instance_import(id, types), |
472 | 83.9k | Some(ExternKind::Export) => self.prepare_instance_export(id, types), |
473 | 0 | None => {} |
474 | | } |
475 | 109k | self.instances.push(*id); |
476 | 109k | (self.instance_count(), MAX_WASM_INSTANCES, "instances") |
477 | | } |
478 | 315k | ComponentEntityType::Func(id) => { |
479 | 315k | self.funcs.push(*id); |
480 | 315k | (self.function_count(), MAX_WASM_FUNCTIONS, "functions") |
481 | | } |
482 | 0 | ComponentEntityType::Value(ty) => { |
483 | 0 | self.check_value_support(features, offset)?; |
484 | 0 | let value_used = match kind { |
485 | 0 | Some(ExternKind::Import) | None => false, |
486 | 0 | Some(ExternKind::Export) => true, |
487 | | }; |
488 | 0 | self.values.push((*ty, value_used)); |
489 | 0 | (self.values.len(), MAX_WASM_VALUES, "values") |
490 | | } |
491 | | ComponentEntityType::Type { |
492 | 525k | created, |
493 | 525k | referenced, |
494 | 525k | } => { |
495 | 525k | self.types.push(*created); |
496 | | |
497 | | // Extra logic here for resources being imported and exported. |
498 | | // Note that if `created` is the same as `referenced` then this |
499 | | // is the original introduction of the resource which is where |
500 | | // `self.{imported,defined}_resources` are updated. |
501 | 525k | if let ComponentAnyTypeId::Resource(id) = *created { |
502 | 46.8k | match kind { |
503 | | Some(ExternKind::Import) => { |
504 | | // A fresh new resource is being imported into a |
505 | | // component. This arises from the import section of |
506 | | // a component or from the import declaration in a |
507 | | // component type. In both cases a new imported |
508 | | // resource is injected with a fresh new identifier |
509 | | // into our state. |
510 | 14.0k | if created == referenced { |
511 | 10.4k | self.imported_resources |
512 | 10.4k | .insert(id.resource(), vec![self.imports.len()]); |
513 | 10.4k | } |
514 | | } |
515 | | |
516 | | Some(ExternKind::Export) => { |
517 | | // A fresh resource is being exported from this |
518 | | // component. This arises as part of the |
519 | | // declaration of a component type, for example. In |
520 | | // this situation brand new resource identifier is |
521 | | // allocated and a definition is added, unlike the |
522 | | // import case where an imported resource is added. |
523 | | // Notably the representation of this new resource |
524 | | // is unknown so it's listed as `None`. |
525 | 32.7k | if created == referenced { |
526 | 20.5k | self.defined_resources.insert(id.resource(), None); |
527 | 20.5k | } |
528 | | |
529 | | // If this is a type export of a resource type then |
530 | | // update the `explicit_resources` list. A new |
531 | | // export path is about to be created for this |
532 | | // resource and this keeps track of that. |
533 | 32.7k | self.explicit_resources |
534 | 32.7k | .insert(id.resource(), vec![self.exports.len()]); |
535 | | } |
536 | | |
537 | 5.73k | None => {} |
538 | | } |
539 | 472k | } |
540 | 525k | (self.types.len(), MAX_WASM_TYPES, "types") |
541 | | } |
542 | | }; |
543 | | |
544 | 976k | check_max(len, 0, max, desc, offset)?; |
545 | | |
546 | | // Before returning perform the final validation of the type of the item |
547 | | // being imported/exported. This will ensure that everything is |
548 | | // appropriately named with respect to type definitions, resources, etc. |
549 | 976k | if let Some((name, kind)) = name_and_kind { |
550 | 925k | if !self.validate_and_register_named_types(Some(name), kind, ty, types) { |
551 | 0 | bail!( |
552 | 0 | offset, |
553 | 0 | "{} not valid to be used as {}", |
554 | 0 | ty.desc(), |
555 | 0 | kind.desc() |
556 | 0 | ); |
557 | 925k | } |
558 | 50.6k | } |
559 | 976k | Ok(()) |
560 | 976k | } |
561 | | |
562 | | /// Validates that the `ty` referenced only refers to named types internally |
563 | | /// and then inserts anything necessary, if applicable, to the defined sets |
564 | | /// within this component. |
565 | | /// |
566 | | /// This function will validate that `ty` only refers to named types. For |
567 | | /// example if it's a record then all of its fields must refer to named |
568 | | /// types. This consults either `self.imported_types` or |
569 | | /// `self.exported_types` as specified by `kind`. Note that this is not |
570 | | /// inherently recursive itself but it ends up being recursive since if |
571 | | /// recursive members were named then all their components must also be |
572 | | /// named. Consequently this check stops at the "one layer deep" position, |
573 | | /// or more accurately the position where types must be named (e.g. tuples |
574 | | /// aren't required to be named). |
575 | 1.53M | fn validate_and_register_named_types( |
576 | 1.53M | &mut self, |
577 | 1.53M | toplevel_name: Option<&str>, |
578 | 1.53M | kind: ExternKind, |
579 | 1.53M | ty: &ComponentEntityType, |
580 | 1.53M | types: &TypeAlloc, |
581 | 1.53M | ) -> bool { |
582 | 1.53M | if let ComponentEntityType::Type { created, .. } = ty { |
583 | | // If this is a top-level resource then register it in the |
584 | | // appropriate context so later validation of method-like-names |
585 | | // works out. |
586 | 894k | if let Some(name) = toplevel_name { |
587 | 500k | if let ComponentAnyTypeId::Resource(id) = *created { |
588 | 46.8k | let cx = match kind { |
589 | 14.0k | ExternKind::Import => &mut self.toplevel_imported_resources, |
590 | 32.7k | ExternKind::Export => &mut self.toplevel_exported_resources, |
591 | | }; |
592 | 46.8k | cx.register(name, id); |
593 | 453k | } |
594 | 394k | } |
595 | 640k | } |
596 | | |
597 | 1.53M | match self.kind { |
598 | 1.06M | ComponentKind::Component | ComponentKind::ComponentType => {} |
599 | 472k | ComponentKind::InstanceType => return true, |
600 | | } |
601 | 1.06M | let set = match kind { |
602 | 307k | ExternKind::Import => &self.imported_types, |
603 | 754k | ExternKind::Export => &self.exported_types, |
604 | | }; |
605 | 1.06M | match ty { |
606 | | // When a type is imported or exported than any recursive type |
607 | | // referred to by that import/export must additionally be exported |
608 | | // or imported. Here this walks the "first layer" of the type which |
609 | | // delegates to `TypeAlloc::type_named_type_id` to determine whether |
610 | | // the components of the type being named here are indeed all they |
611 | | // themselves named. |
612 | | ComponentEntityType::Type { |
613 | 596k | created, |
614 | 596k | referenced, |
615 | 596k | } => { |
616 | 596k | if !self.all_valtypes_named(types, *referenced, set) { |
617 | 0 | return false; |
618 | 596k | } |
619 | 596k | match kind { |
620 | | // Imported types are both valid for import and valid for |
621 | | // export. |
622 | 135k | ExternKind::Import => { |
623 | 135k | self.imported_types.insert(*created); |
624 | 135k | self.exported_types.insert(*created); |
625 | 135k | } |
626 | 460k | ExternKind::Export => { |
627 | 460k | self.exported_types.insert(*created); |
628 | 460k | } |
629 | | } |
630 | | |
631 | 596k | true |
632 | | } |
633 | | |
634 | | // Instances are slightly nuanced here. The general idea is that if |
635 | | // an instance is imported, then any type exported by the instance |
636 | | // is then also exported. Additionally for exports. To get this to |
637 | | // work out this arm will recursively call |
638 | | // `validate_and_register_named_types` which means that types are |
639 | | // inserted into `self.{imported,exported}_types` as-we-go rather |
640 | | // than all at once. |
641 | | // |
642 | | // This then recursively validates that all items in the instance |
643 | | // itself are valid to import/export, recursive instances are |
644 | | // captured, and everything is appropriately added to the right |
645 | | // imported/exported set. |
646 | 109k | ComponentEntityType::Instance(i) => types[*i] |
647 | 109k | .exports |
648 | 109k | .iter() |
649 | 609k | .all(|(_name, ty)| self.validate_and_register_named_types(None, kind, ty, types)), |
650 | | |
651 | | // All types referred to by a function must be named. |
652 | 331k | ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set), |
653 | | |
654 | 0 | ComponentEntityType::Value(ty) => types.type_named_valtype(ty, set), |
655 | | |
656 | | // Components/modules are always "closed" or "standalone" and don't |
657 | | // need validation with respect to their named types. |
658 | 25.3k | ComponentEntityType::Component(_) | ComponentEntityType::Module(_) => true, |
659 | | } |
660 | 1.53M | } |
661 | | |
662 | 596k | fn all_valtypes_named( |
663 | 596k | &self, |
664 | 596k | types: &TypeAlloc, |
665 | 596k | id: ComponentAnyTypeId, |
666 | 596k | set: &Set<ComponentAnyTypeId>, |
667 | 596k | ) -> bool { |
668 | 596k | match id { |
669 | | // Resource types, in isolation, are always valid to import or |
670 | | // export since they're either attached to an import or being |
671 | | // exported. |
672 | | // |
673 | | // Note that further validation of this happens in `finish`, too. |
674 | 52.4k | ComponentAnyTypeId::Resource(_) => true, |
675 | | |
676 | | // Component types are validated as they are constructed, |
677 | | // so all component types are valid to export if they've |
678 | | // already been constructed. |
679 | 51.1k | ComponentAnyTypeId::Component(_) => true, |
680 | | |
681 | 493k | ComponentAnyTypeId::Defined(id) => self.all_valtypes_named_in_defined(types, id, set), |
682 | 0 | ComponentAnyTypeId::Func(id) => self.all_valtypes_named_in_func(types, id, set), |
683 | 0 | ComponentAnyTypeId::Instance(id) => self.all_valtypes_named_in_instance(types, id, set), |
684 | | } |
685 | 596k | } |
686 | | |
687 | 0 | fn all_valtypes_named_in_instance( |
688 | 0 | &self, |
689 | 0 | types: &TypeAlloc, |
690 | 0 | id: ComponentInstanceTypeId, |
691 | 0 | set: &Set<ComponentAnyTypeId>, |
692 | 0 | ) -> bool { |
693 | 0 | // Instances must recursively have all referenced types named. |
694 | 0 | let ty = &types[id]; |
695 | 0 | ty.exports.values().all(|ty| match ty { |
696 | 0 | ComponentEntityType::Module(_) => true, |
697 | 0 | ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set), |
698 | 0 | ComponentEntityType::Type { created: id, .. } => { |
699 | 0 | self.all_valtypes_named(types, *id, set) |
700 | | } |
701 | 0 | ComponentEntityType::Value(ComponentValType::Type(id)) => { |
702 | 0 | self.all_valtypes_named_in_defined(types, *id, set) |
703 | | } |
704 | 0 | ComponentEntityType::Instance(id) => { |
705 | 0 | self.all_valtypes_named_in_instance(types, *id, set) |
706 | | } |
707 | | ComponentEntityType::Component(_) |
708 | 0 | | ComponentEntityType::Value(ComponentValType::Primitive(_)) => return true, |
709 | 0 | }) |
710 | 0 | } |
711 | | |
712 | 493k | fn all_valtypes_named_in_defined( |
713 | 493k | &self, |
714 | 493k | types: &TypeAlloc, |
715 | 493k | id: ComponentDefinedTypeId, |
716 | 493k | set: &Set<ComponentAnyTypeId>, |
717 | 493k | ) -> bool { |
718 | 493k | let ty = &types[id]; |
719 | 493k | match ty { |
720 | | // These types do not contain anything which must be |
721 | | // named. |
722 | | ComponentDefinedType::Primitive(_) |
723 | | | ComponentDefinedType::Flags(_) |
724 | | | ComponentDefinedType::Enum(_) |
725 | 433k | | ComponentDefinedType::ErrorContext => true, |
726 | | |
727 | | // Referenced types of all these aggregates must all be |
728 | | // named. |
729 | 32.9k | ComponentDefinedType::Record(r) => { |
730 | 103k | r.fields.values().all(|t| types.type_named_valtype(t, set)) |
731 | | } |
732 | 4.46k | ComponentDefinedType::Tuple(r) => { |
733 | 8.87k | r.types.iter().all(|t| types.type_named_valtype(t, set)) |
734 | | } |
735 | 17.6k | ComponentDefinedType::Variant(r) => r |
736 | 17.6k | .cases |
737 | 17.6k | .values() |
738 | 68.2k | .filter_map(|t| t.ty.as_ref()) |
739 | 55.6k | .all(|t| types.type_named_valtype(t, set)), |
740 | 419 | ComponentDefinedType::Result { ok, err } => { |
741 | 419 | ok.as_ref() |
742 | 419 | .map(|t| types.type_named_valtype(t, set)) |
743 | 419 | .unwrap_or(true) |
744 | 419 | && err |
745 | 419 | .as_ref() |
746 | 419 | .map(|t| types.type_named_valtype(t, set)) |
747 | 419 | .unwrap_or(true) |
748 | | } |
749 | 3.51k | ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => { |
750 | 3.64k | types.type_named_valtype(ty, set) |
751 | | } |
752 | | |
753 | | // The resource referred to by own/borrow must be named. |
754 | 0 | ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => { |
755 | 0 | set.contains(&ComponentAnyTypeId::from(*id)) |
756 | | } |
757 | | |
758 | 162 | ComponentDefinedType::Future(ty) => ty |
759 | 162 | .as_ref() |
760 | 162 | .map(|ty| types.type_named_valtype(ty, set)) |
761 | 162 | .unwrap_or(true), |
762 | 136 | ComponentDefinedType::Stream(ty) => types.type_named_valtype(ty, set), |
763 | | } |
764 | 493k | } |
765 | | |
766 | 331k | fn all_valtypes_named_in_func( |
767 | 331k | &self, |
768 | 331k | types: &TypeAlloc, |
769 | 331k | id: ComponentFuncTypeId, |
770 | 331k | set: &Set<ComponentAnyTypeId>, |
771 | 331k | ) -> bool { |
772 | 331k | let ty = &types[id]; |
773 | 331k | // Function types must have all their parameters/results named. |
774 | 331k | ty.params |
775 | 331k | .iter() |
776 | 720k | .map(|(_, ty)| ty) |
777 | 331k | .chain(ty.results.iter().map(|(_, ty)| ty)) |
778 | 1.01M | .all(|ty| types.type_named_valtype(ty, set)) |
779 | 331k | } |
780 | | |
781 | | /// Updates the type `id` specified, an identifier for a component instance |
782 | | /// type, to be imported into this component. |
783 | | /// |
784 | | /// Importing an instance type into a component specially handles the |
785 | | /// defined resources registered in the instance type. Notably all |
786 | | /// defined resources are "freshened" into brand new type variables and |
787 | | /// these new variables are substituted within the type. This is what |
788 | | /// creates a new `TypeId` and may update the `id` specified. |
789 | | /// |
790 | | /// One side effect of this operation, for example, is that if an instance |
791 | | /// type is used twice to import two different instances then the instances |
792 | | /// do not share resource types despite sharing the same original instance |
793 | | /// type. |
794 | 25.3k | fn prepare_instance_import(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) { |
795 | 25.3k | let ty = &types[*id]; |
796 | 25.3k | |
797 | 25.3k | // No special treatment for imports of instances which themselves have |
798 | 25.3k | // no defined resources |
799 | 25.3k | if ty.defined_resources.is_empty() { |
800 | 18.8k | return; |
801 | 6.54k | } |
802 | 6.54k | |
803 | 6.54k | let mut new_ty = ComponentInstanceType { |
804 | 6.54k | // Copied from the input verbatim |
805 | 6.54k | info: ty.info, |
806 | 6.54k | |
807 | 6.54k | // Copied over as temporary storage for now, and both of these are |
808 | 6.54k | // filled out and expanded below. |
809 | 6.54k | exports: ty.exports.clone(), |
810 | 6.54k | explicit_resources: ty.explicit_resources.clone(), |
811 | 6.54k | |
812 | 6.54k | // Explicitly discard this field since the |
813 | 6.54k | // defined resources are lifted into `self` |
814 | 6.54k | defined_resources: Default::default(), |
815 | 6.54k | }; |
816 | 6.54k | |
817 | 6.54k | // Create brand new resources for all defined ones in the instance. |
818 | 6.54k | let resources = (0..ty.defined_resources.len()) |
819 | 8.31k | .map(|_| types.alloc_resource_id()) |
820 | 6.54k | .collect::<IndexSet<_>>(); |
821 | 6.54k | |
822 | 6.54k | // Build a map from the defined resources in `ty` to those in `new_ty`. |
823 | 6.54k | // |
824 | 6.54k | // As part of this same loop the new resources, which were previously |
825 | 6.54k | // defined in `ty`, now become imported variables in `self`. Their |
826 | 6.54k | // path for where they're imported is updated as well with |
827 | 6.54k | // `self.next_import_index` as the import-to-be soon. |
828 | 6.54k | let mut mapping = Remapping::default(); |
829 | 6.54k | let ty = &types[*id]; |
830 | 8.31k | for (old, new) in ty.defined_resources.iter().zip(&resources) { |
831 | 8.31k | let prev = mapping.resources.insert(*old, new.resource()); |
832 | 8.31k | assert!(prev.is_none()); |
833 | | |
834 | 8.31k | let mut base = vec![self.imports.len()]; |
835 | 8.31k | base.extend(ty.explicit_resources[old].iter().copied()); |
836 | 8.31k | self.imported_resources.insert(new.resource(), base); |
837 | | } |
838 | | |
839 | | // Using the old-to-new resource mapping perform a substitution on |
840 | | // the `exports` and `explicit_resources` fields of `new_ty` |
841 | 70.4k | for ty in new_ty.exports.values_mut() { |
842 | 70.4k | types.remap_component_entity(ty, &mut mapping); |
843 | 70.4k | } |
844 | 8.40k | for (id, path) in mem::take(&mut new_ty.explicit_resources) { |
845 | 8.40k | let id = *mapping.resources.get(&id).unwrap_or(&id); |
846 | 8.40k | new_ty.explicit_resources.insert(id, path); |
847 | 8.40k | } |
848 | | |
849 | | // Now that `new_ty` is complete finish its registration and then |
850 | | // update `id` on the way out. |
851 | 6.54k | *id = types.push_ty(new_ty); |
852 | 25.3k | } |
853 | | |
854 | | /// Prepares an instance type, pointed to `id`, for being exported as a |
855 | | /// concrete instance from `self`. |
856 | | /// |
857 | | /// This will internally perform any resource "freshening" as required and |
858 | | /// then additionally update metadata within `self` about resources being |
859 | | /// exported or defined. |
860 | 83.9k | fn prepare_instance_export(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) { |
861 | 83.9k | // Exports of an instance mean that the enclosing context |
862 | 83.9k | // is inheriting the resources that the instance |
863 | 83.9k | // encapsulates. This means that the instance type |
864 | 83.9k | // recorded for this export will itself have no |
865 | 83.9k | // defined resources. |
866 | 83.9k | let ty = &types[*id]; |
867 | 83.9k | |
868 | 83.9k | // Check to see if `defined_resources` is non-empty, and if so then |
869 | 83.9k | // "freshen" all the resources and inherit them to our own defined |
870 | 83.9k | // resources, updating `id` in the process. |
871 | 83.9k | // |
872 | 83.9k | // Note though that this specifically is not rewriting the resources of |
873 | 83.9k | // exported instances. The `defined_resources` set on instance types is |
874 | 83.9k | // a little subtle (see its documentation for more info), but the |
875 | 83.9k | // general idea is that for a concrete instance it's always empty. Only |
876 | 83.9k | // for instance type definitions does it ever have elements in it. |
877 | 83.9k | // |
878 | 83.9k | // That means that if this set is non-empty then what's happening is |
879 | 83.9k | // that we're in a type context an exporting an instance of a previously |
880 | 83.9k | // specified type. In this case all resources are required to be |
881 | 83.9k | // "freshened" to ensure that multiple exports of the same type all |
882 | 83.9k | // export different types of resources. |
883 | 83.9k | // |
884 | 83.9k | // And finally note that this operation empties out the |
885 | 83.9k | // `defined_resources` set of the type that is registered for the |
886 | 83.9k | // instance, as this export is modeled as producing a concrete instance. |
887 | 83.9k | if !ty.defined_resources.is_empty() { |
888 | 8.89k | let mut new_ty = ty.clone(); |
889 | 8.89k | let mut mapping = Remapping::default(); |
890 | 12.2k | for old in mem::take(&mut new_ty.defined_resources) { |
891 | 12.2k | let new = types.alloc_resource_id(); |
892 | 12.2k | mapping.resources.insert(old, new.resource()); |
893 | 12.2k | self.defined_resources.insert(new.resource(), None); |
894 | 12.2k | } |
895 | 123k | for ty in new_ty.exports.values_mut() { |
896 | 123k | types.remap_component_entity(ty, &mut mapping); |
897 | 123k | } |
898 | 12.3k | for (id, path) in mem::take(&mut new_ty.explicit_resources) { |
899 | 12.3k | let id = mapping.resources.get(&id).copied().unwrap_or(id); |
900 | 12.3k | new_ty.explicit_resources.insert(id, path); |
901 | 12.3k | } |
902 | 8.89k | *id = types.push_ty(new_ty); |
903 | 75.0k | } |
904 | | |
905 | | // Any explicit resources in the instance are now additionally explicit |
906 | | // in this component since it's exported. |
907 | | // |
908 | | // The path to each explicit resources gets one element prepended which |
909 | | // is `self.next_export_index`, the index of the export about to be |
910 | | // generated. |
911 | 83.9k | let ty = &types[*id]; |
912 | 83.9k | for (id, path) in ty.explicit_resources.iter() { |
913 | 18.6k | let mut new_path = vec![self.exports.len()]; |
914 | 18.6k | new_path.extend(path); |
915 | 18.6k | self.explicit_resources.insert(*id, new_path); |
916 | 18.6k | } |
917 | 83.9k | } |
918 | | |
919 | 775k | pub fn add_export( |
920 | 775k | &mut self, |
921 | 775k | name: ComponentExportName<'_>, |
922 | 775k | mut ty: ComponentEntityType, |
923 | 775k | features: &WasmFeatures, |
924 | 775k | types: &mut TypeAlloc, |
925 | 775k | offset: usize, |
926 | 775k | check_limit: bool, |
927 | 775k | ) -> Result<()> { |
928 | 775k | if check_limit { |
929 | 565k | check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?; |
930 | 209k | } |
931 | 775k | self.add_entity( |
932 | 775k | &mut ty, |
933 | 775k | Some((name.0, ExternKind::Export)), |
934 | 775k | features, |
935 | 775k | types, |
936 | 775k | offset, |
937 | 775k | )?; |
938 | 775k | self.toplevel_exported_resources.validate_extern( |
939 | 775k | name.0, |
940 | 775k | ExternKind::Export, |
941 | 775k | &ty, |
942 | 775k | types, |
943 | 775k | offset, |
944 | 775k | &mut self.export_names, |
945 | 775k | &mut self.exports, |
946 | 775k | &mut self.type_info, |
947 | 775k | features, |
948 | 775k | )?; |
949 | 775k | Ok(()) |
950 | 775k | } |
951 | | |
952 | 42.3k | pub fn lift_function( |
953 | 42.3k | &mut self, |
954 | 42.3k | core_func_index: u32, |
955 | 42.3k | type_index: u32, |
956 | 42.3k | options: Vec<CanonicalOption>, |
957 | 42.3k | types: &TypeList, |
958 | 42.3k | offset: usize, |
959 | 42.3k | features: &WasmFeatures, |
960 | 42.3k | ) -> Result<()> { |
961 | 42.3k | let ty = self.function_type_at(type_index, types, offset)?; |
962 | 42.3k | let core_ty = types[self.core_function_at(core_func_index, offset)?].unwrap_func(); |
963 | | |
964 | | // Lifting a function is for an export, so match the expected canonical ABI |
965 | | // export signature |
966 | 42.3k | let info = ty.lower( |
967 | 42.3k | types, |
968 | 42.3k | if options.contains(&CanonicalOption::Async) { |
969 | 9.67k | if options |
970 | 9.67k | .iter() |
971 | 18.5k | .any(|v| matches!(v, CanonicalOption::Callback(_))) |
972 | | { |
973 | 7.92k | Abi::LiftAsync |
974 | | } else { |
975 | 1.74k | Abi::LiftAsyncStackful |
976 | | } |
977 | | } else { |
978 | 32.6k | Abi::LiftSync |
979 | | }, |
980 | | ); |
981 | 42.3k | self.check_options( |
982 | 42.3k | Some(core_ty), |
983 | 42.3k | &info, |
984 | 42.3k | &options, |
985 | 42.3k | types, |
986 | 42.3k | offset, |
987 | 42.3k | features, |
988 | 42.3k | true, |
989 | 42.3k | )?; |
990 | | |
991 | 42.3k | if core_ty.params() != info.params.as_slice() { |
992 | 0 | bail!( |
993 | 0 | offset, |
994 | 0 | "lowered parameter types `{:?}` do not match parameter types \ |
995 | 0 | `{:?}` of core function {core_func_index}", |
996 | 0 | info.params.as_slice(), |
997 | 0 | core_ty.params(), |
998 | 0 | ); |
999 | 42.3k | } |
1000 | 42.3k | |
1001 | 42.3k | if core_ty.results() != info.results.as_slice() { |
1002 | 0 | bail!( |
1003 | 0 | offset, |
1004 | 0 | "lowered result types `{:?}` do not match result types \ |
1005 | 0 | `{:?}` of core function {core_func_index}", |
1006 | 0 | info.results.as_slice(), |
1007 | 0 | core_ty.results() |
1008 | 0 | ); |
1009 | 42.3k | } |
1010 | 42.3k | |
1011 | 42.3k | self.funcs |
1012 | 42.3k | .push(self.types[type_index as usize].unwrap_func()); |
1013 | 42.3k | |
1014 | 42.3k | Ok(()) |
1015 | 42.3k | } |
1016 | | |
1017 | 35.2k | pub fn lower_function( |
1018 | 35.2k | &mut self, |
1019 | 35.2k | func_index: u32, |
1020 | 35.2k | options: Vec<CanonicalOption>, |
1021 | 35.2k | types: &mut TypeAlloc, |
1022 | 35.2k | offset: usize, |
1023 | 35.2k | features: &WasmFeatures, |
1024 | 35.2k | ) -> Result<()> { |
1025 | 35.2k | let ty = &types[self.function_at(func_index, offset)?]; |
1026 | | |
1027 | | // Lowering a function is for an import, so use a function type that matches |
1028 | | // the expected canonical ABI import signature. |
1029 | 35.2k | let info = ty.lower( |
1030 | 35.2k | types, |
1031 | 35.2k | if options.contains(&CanonicalOption::Async) { |
1032 | 8.97k | Abi::LowerAsync |
1033 | | } else { |
1034 | 26.3k | Abi::LowerSync |
1035 | | }, |
1036 | | ); |
1037 | | |
1038 | 35.2k | self.check_options(None, &info, &options, types, offset, features, true)?; |
1039 | | |
1040 | 35.2k | let id = types.intern_func_type(info.into_func_type(), offset); |
1041 | 35.2k | self.core_funcs.push(id); |
1042 | 35.2k | |
1043 | 35.2k | Ok(()) |
1044 | 35.2k | } |
1045 | | |
1046 | 5.28k | pub fn resource_new( |
1047 | 5.28k | &mut self, |
1048 | 5.28k | resource: u32, |
1049 | 5.28k | types: &mut TypeAlloc, |
1050 | 5.28k | offset: usize, |
1051 | 5.28k | ) -> Result<()> { |
1052 | 5.28k | let rep = self.check_local_resource(resource, types, offset)?; |
1053 | 5.28k | let id = types.intern_func_type(FuncType::new([rep], [ValType::I32]), offset); |
1054 | 5.28k | self.core_funcs.push(id); |
1055 | 5.28k | Ok(()) |
1056 | 5.28k | } |
1057 | | |
1058 | 9.44k | pub fn resource_drop( |
1059 | 9.44k | &mut self, |
1060 | 9.44k | resource: u32, |
1061 | 9.44k | types: &mut TypeAlloc, |
1062 | 9.44k | offset: usize, |
1063 | 9.44k | ) -> Result<()> { |
1064 | 9.44k | self.resource_at(resource, types, offset)?; |
1065 | 9.44k | let id = types.intern_func_type(FuncType::new([ValType::I32], []), offset); |
1066 | 9.44k | self.core_funcs.push(id); |
1067 | 9.44k | Ok(()) |
1068 | 9.44k | } |
1069 | | |
1070 | 5.28k | pub fn resource_rep( |
1071 | 5.28k | &mut self, |
1072 | 5.28k | resource: u32, |
1073 | 5.28k | types: &mut TypeAlloc, |
1074 | 5.28k | offset: usize, |
1075 | 5.28k | ) -> Result<()> { |
1076 | 5.28k | let rep = self.check_local_resource(resource, types, offset)?; |
1077 | 5.28k | let id = types.intern_func_type(FuncType::new([ValType::I32], [rep]), offset); |
1078 | 5.28k | self.core_funcs.push(id); |
1079 | 5.28k | Ok(()) |
1080 | 5.28k | } |
1081 | | |
1082 | 0 | pub fn task_backpressure( |
1083 | 0 | &mut self, |
1084 | 0 | types: &mut TypeAlloc, |
1085 | 0 | offset: usize, |
1086 | 0 | features: &WasmFeatures, |
1087 | 0 | ) -> Result<()> { |
1088 | 0 | if !features.component_model_async() { |
1089 | 0 | bail!( |
1090 | 0 | offset, |
1091 | 0 | "`task.backpressure` requires the component model async feature" |
1092 | 0 | ) |
1093 | 0 | } |
1094 | 0 |
|
1095 | 0 | self.core_funcs |
1096 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1097 | 0 | Ok(()) |
1098 | 0 | } |
1099 | | |
1100 | 0 | pub fn task_return( |
1101 | 0 | &mut self, |
1102 | 0 | type_index: u32, |
1103 | 0 | types: &mut TypeAlloc, |
1104 | 0 | offset: usize, |
1105 | 0 | features: &WasmFeatures, |
1106 | 0 | ) -> Result<()> { |
1107 | 0 | if !features.component_model_async() { |
1108 | 0 | bail!( |
1109 | 0 | offset, |
1110 | 0 | "`task.return` requires the component model async feature" |
1111 | 0 | ) |
1112 | 0 | } |
1113 | | |
1114 | 0 | let id = self.type_id_at(type_index, offset)?; |
1115 | | let Some(SubType { |
1116 | | composite_type: |
1117 | | CompositeType { |
1118 | | inner: CompositeInnerType::Func(_), |
1119 | | .. |
1120 | | }, |
1121 | | .. |
1122 | 0 | }) = types.get(id) |
1123 | | else { |
1124 | 0 | bail!(offset, "invalid `task.return` type index"); |
1125 | | }; |
1126 | | |
1127 | 0 | self.core_funcs.push(id); |
1128 | 0 | Ok(()) |
1129 | 0 | } |
1130 | | |
1131 | 0 | pub fn task_wait( |
1132 | 0 | &mut self, |
1133 | 0 | _async_: bool, |
1134 | 0 | memory: u32, |
1135 | 0 | types: &mut TypeAlloc, |
1136 | 0 | offset: usize, |
1137 | 0 | features: &WasmFeatures, |
1138 | 0 | ) -> Result<()> { |
1139 | 0 | if !features.component_model_async() { |
1140 | 0 | bail!( |
1141 | 0 | offset, |
1142 | 0 | "`task.wait` requires the component model async feature" |
1143 | 0 | ) |
1144 | 0 | } |
1145 | 0 |
|
1146 | 0 | self.memory_at(memory, offset)?; |
1147 | | |
1148 | 0 | self.core_funcs |
1149 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1150 | 0 | Ok(()) |
1151 | 0 | } |
1152 | | |
1153 | 0 | pub fn task_poll( |
1154 | 0 | &mut self, |
1155 | 0 | _async_: bool, |
1156 | 0 | memory: u32, |
1157 | 0 | types: &mut TypeAlloc, |
1158 | 0 | offset: usize, |
1159 | 0 | features: &WasmFeatures, |
1160 | 0 | ) -> Result<()> { |
1161 | 0 | if !features.component_model_async() { |
1162 | 0 | bail!( |
1163 | 0 | offset, |
1164 | 0 | "`task.poll` requires the component model async feature" |
1165 | 0 | ) |
1166 | 0 | } |
1167 | 0 |
|
1168 | 0 | self.memory_at(memory, offset)?; |
1169 | | |
1170 | 0 | self.core_funcs |
1171 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1172 | 0 | Ok(()) |
1173 | 0 | } |
1174 | | |
1175 | 0 | pub fn task_yield( |
1176 | 0 | &mut self, |
1177 | 0 | _async_: bool, |
1178 | 0 | types: &mut TypeAlloc, |
1179 | 0 | offset: usize, |
1180 | 0 | features: &WasmFeatures, |
1181 | 0 | ) -> Result<()> { |
1182 | 0 | if !features.component_model_async() { |
1183 | 0 | bail!( |
1184 | 0 | offset, |
1185 | 0 | "`task.yield` requires the component model async feature" |
1186 | 0 | ) |
1187 | 0 | } |
1188 | 0 |
|
1189 | 0 | self.core_funcs |
1190 | 0 | .push(types.intern_func_type(FuncType::new([], []), offset)); |
1191 | 0 | Ok(()) |
1192 | 0 | } |
1193 | | |
1194 | 0 | pub fn subtask_drop( |
1195 | 0 | &mut self, |
1196 | 0 | types: &mut TypeAlloc, |
1197 | 0 | offset: usize, |
1198 | 0 | features: &WasmFeatures, |
1199 | 0 | ) -> Result<()> { |
1200 | 0 | if !features.component_model_async() { |
1201 | 0 | bail!( |
1202 | 0 | offset, |
1203 | 0 | "`subtask.drop` requires the component model async feature" |
1204 | 0 | ) |
1205 | 0 | } |
1206 | 0 |
|
1207 | 0 | self.core_funcs |
1208 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1209 | 0 | Ok(()) |
1210 | 0 | } |
1211 | | |
1212 | 0 | pub fn stream_new( |
1213 | 0 | &mut self, |
1214 | 0 | ty: u32, |
1215 | 0 | types: &mut TypeAlloc, |
1216 | 0 | offset: usize, |
1217 | 0 | features: &WasmFeatures, |
1218 | 0 | ) -> Result<()> { |
1219 | 0 | if !features.component_model_async() { |
1220 | 0 | bail!( |
1221 | 0 | offset, |
1222 | 0 | "`stream.new` requires the component model async feature" |
1223 | 0 | ) |
1224 | 0 | } |
1225 | | |
1226 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1227 | 0 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1228 | 0 | bail!(offset, "`stream.new` requires a stream type") |
1229 | | }; |
1230 | | |
1231 | 0 | self.core_funcs |
1232 | 0 | .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); |
1233 | 0 | Ok(()) |
1234 | 0 | } |
1235 | | |
1236 | 0 | pub fn stream_read( |
1237 | 0 | &mut self, |
1238 | 0 | ty: u32, |
1239 | 0 | options: Vec<CanonicalOption>, |
1240 | 0 | types: &mut TypeAlloc, |
1241 | 0 | offset: usize, |
1242 | 0 | features: &WasmFeatures, |
1243 | 0 | ) -> Result<()> { |
1244 | 0 | if !features.component_model_async() { |
1245 | 0 | bail!( |
1246 | 0 | offset, |
1247 | 0 | "`stream.read` requires the component model async feature" |
1248 | 0 | ) |
1249 | 0 | } |
1250 | | |
1251 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1252 | 0 | let ComponentDefinedType::Stream(payload_type) = &types[ty] else { |
1253 | 0 | bail!(offset, "`stream.read` requires a stream type") |
1254 | | }; |
1255 | | |
1256 | 0 | let mut info = LoweringInfo::default(); |
1257 | 0 | info.requires_memory = true; |
1258 | 0 | info.requires_realloc = payload_type.contains_ptr(types); |
1259 | 0 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1260 | | |
1261 | 0 | self.core_funcs |
1262 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32; 3], [ValType::I32]), offset)); |
1263 | 0 | Ok(()) |
1264 | 0 | } |
1265 | | |
1266 | 0 | pub fn stream_write( |
1267 | 0 | &mut self, |
1268 | 0 | ty: u32, |
1269 | 0 | options: Vec<CanonicalOption>, |
1270 | 0 | types: &mut TypeAlloc, |
1271 | 0 | offset: usize, |
1272 | 0 | features: &WasmFeatures, |
1273 | 0 | ) -> Result<()> { |
1274 | 0 | if !features.component_model_async() { |
1275 | 0 | bail!( |
1276 | 0 | offset, |
1277 | 0 | "`stream.write` requires the component model async feature" |
1278 | 0 | ) |
1279 | 0 | } |
1280 | | |
1281 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1282 | 0 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1283 | 0 | bail!(offset, "`stream.write` requires a stream type") |
1284 | | }; |
1285 | | |
1286 | 0 | let mut info = LoweringInfo::default(); |
1287 | 0 | info.requires_memory = true; |
1288 | 0 | info.requires_realloc = false; |
1289 | 0 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1290 | | |
1291 | 0 | self.core_funcs |
1292 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32; 3], [ValType::I32]), offset)); |
1293 | 0 | Ok(()) |
1294 | 0 | } |
1295 | | |
1296 | 0 | pub fn stream_cancel_read( |
1297 | 0 | &mut self, |
1298 | 0 | ty: u32, |
1299 | 0 | _async_: bool, |
1300 | 0 | types: &mut TypeAlloc, |
1301 | 0 | offset: usize, |
1302 | 0 | features: &WasmFeatures, |
1303 | 0 | ) -> Result<()> { |
1304 | 0 | if !features.component_model_async() { |
1305 | 0 | bail!( |
1306 | 0 | offset, |
1307 | 0 | "`stream.cancel-read` requires the component model async feature" |
1308 | 0 | ) |
1309 | 0 | } |
1310 | | |
1311 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1312 | 0 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1313 | 0 | bail!(offset, "`stream.cancel-read` requires a stream type") |
1314 | | }; |
1315 | | |
1316 | 0 | self.core_funcs |
1317 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1318 | 0 | Ok(()) |
1319 | 0 | } |
1320 | | |
1321 | 0 | pub fn stream_cancel_write( |
1322 | 0 | &mut self, |
1323 | 0 | ty: u32, |
1324 | 0 | _async_: bool, |
1325 | 0 | types: &mut TypeAlloc, |
1326 | 0 | offset: usize, |
1327 | 0 | features: &WasmFeatures, |
1328 | 0 | ) -> Result<()> { |
1329 | 0 | if !features.component_model_async() { |
1330 | 0 | bail!( |
1331 | 0 | offset, |
1332 | 0 | "`stream.cancel-write` requires the component model async feature" |
1333 | 0 | ) |
1334 | 0 | } |
1335 | | |
1336 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1337 | 0 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1338 | 0 | bail!(offset, "`stream.cancel-write` requires a stream type") |
1339 | | }; |
1340 | | |
1341 | 0 | self.core_funcs |
1342 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1343 | 0 | Ok(()) |
1344 | 0 | } |
1345 | | |
1346 | 0 | pub fn stream_close_readable( |
1347 | 0 | &mut self, |
1348 | 0 | ty: u32, |
1349 | 0 | types: &mut TypeAlloc, |
1350 | 0 | offset: usize, |
1351 | 0 | features: &WasmFeatures, |
1352 | 0 | ) -> Result<()> { |
1353 | 0 | if !features.component_model_async() { |
1354 | 0 | bail!( |
1355 | 0 | offset, |
1356 | 0 | "`stream.close-readable` requires the component model async feature" |
1357 | 0 | ) |
1358 | 0 | } |
1359 | | |
1360 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1361 | 0 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1362 | 0 | bail!(offset, "`stream.close-readable` requires a stream type") |
1363 | | }; |
1364 | | |
1365 | 0 | self.core_funcs |
1366 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1367 | 0 | Ok(()) |
1368 | 0 | } |
1369 | | |
1370 | 0 | pub fn stream_close_writable( |
1371 | 0 | &mut self, |
1372 | 0 | ty: u32, |
1373 | 0 | types: &mut TypeAlloc, |
1374 | 0 | offset: usize, |
1375 | 0 | features: &WasmFeatures, |
1376 | 0 | ) -> Result<()> { |
1377 | 0 | if !features.component_model_async() { |
1378 | 0 | bail!( |
1379 | 0 | offset, |
1380 | 0 | "`stream.close-writable` requires the component model async feature" |
1381 | 0 | ) |
1382 | 0 | } |
1383 | | |
1384 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1385 | 0 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1386 | 0 | bail!(offset, "`stream.close-writable` requires a stream type") |
1387 | | }; |
1388 | | |
1389 | 0 | self.core_funcs |
1390 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset)); |
1391 | 0 | Ok(()) |
1392 | 0 | } |
1393 | | |
1394 | 0 | pub fn future_new( |
1395 | 0 | &mut self, |
1396 | 0 | ty: u32, |
1397 | 0 | types: &mut TypeAlloc, |
1398 | 0 | offset: usize, |
1399 | 0 | features: &WasmFeatures, |
1400 | 0 | ) -> Result<()> { |
1401 | 0 | if !features.component_model_async() { |
1402 | 0 | bail!( |
1403 | 0 | offset, |
1404 | 0 | "`future.new` requires the component model async feature" |
1405 | 0 | ) |
1406 | 0 | } |
1407 | | |
1408 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1409 | 0 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1410 | 0 | bail!(offset, "`future.new` requires a future type") |
1411 | | }; |
1412 | | |
1413 | 0 | self.core_funcs |
1414 | 0 | .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); |
1415 | 0 | Ok(()) |
1416 | 0 | } |
1417 | | |
1418 | 0 | pub fn future_read( |
1419 | 0 | &mut self, |
1420 | 0 | ty: u32, |
1421 | 0 | options: Vec<CanonicalOption>, |
1422 | 0 | types: &mut TypeAlloc, |
1423 | 0 | offset: usize, |
1424 | 0 | features: &WasmFeatures, |
1425 | 0 | ) -> Result<()> { |
1426 | 0 | if !features.component_model_async() { |
1427 | 0 | bail!( |
1428 | 0 | offset, |
1429 | 0 | "`future.read` requires the component model async feature" |
1430 | 0 | ) |
1431 | 0 | } |
1432 | | |
1433 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1434 | 0 | let ComponentDefinedType::Future(payload_type) = &types[ty] else { |
1435 | 0 | bail!(offset, "`future.read` requires a future type") |
1436 | | }; |
1437 | | |
1438 | 0 | let mut info = LoweringInfo::default(); |
1439 | 0 | info.requires_memory = true; |
1440 | 0 | info.requires_realloc = payload_type |
1441 | 0 | .map(|ty| ty.contains_ptr(types)) |
1442 | 0 | .unwrap_or(false); |
1443 | 0 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1444 | | |
1445 | 0 | self.core_funcs |
1446 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset)); |
1447 | 0 | Ok(()) |
1448 | 0 | } |
1449 | | |
1450 | 0 | pub fn future_write( |
1451 | 0 | &mut self, |
1452 | 0 | ty: u32, |
1453 | 0 | options: Vec<CanonicalOption>, |
1454 | 0 | types: &mut TypeAlloc, |
1455 | 0 | offset: usize, |
1456 | 0 | features: &WasmFeatures, |
1457 | 0 | ) -> Result<()> { |
1458 | 0 | if !features.component_model_async() { |
1459 | 0 | bail!( |
1460 | 0 | offset, |
1461 | 0 | "`future.write` requires the component model async feature" |
1462 | 0 | ) |
1463 | 0 | } |
1464 | | |
1465 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1466 | 0 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1467 | 0 | bail!(offset, "`future.write` requires a future type") |
1468 | | }; |
1469 | | |
1470 | 0 | let mut info = LoweringInfo::default(); |
1471 | 0 | info.requires_memory = true; |
1472 | 0 | info.requires_realloc = false; |
1473 | 0 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1474 | | |
1475 | 0 | self.core_funcs |
1476 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset)); |
1477 | 0 | Ok(()) |
1478 | 0 | } |
1479 | | |
1480 | 0 | pub fn future_cancel_read( |
1481 | 0 | &mut self, |
1482 | 0 | ty: u32, |
1483 | 0 | _async_: bool, |
1484 | 0 | types: &mut TypeAlloc, |
1485 | 0 | offset: usize, |
1486 | 0 | features: &WasmFeatures, |
1487 | 0 | ) -> Result<()> { |
1488 | 0 | if !features.component_model_async() { |
1489 | 0 | bail!( |
1490 | 0 | offset, |
1491 | 0 | "`future.cancel-read` requires the component model async feature" |
1492 | 0 | ) |
1493 | 0 | } |
1494 | | |
1495 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1496 | 0 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1497 | 0 | bail!(offset, "`future.cancel-read` requires a future type") |
1498 | | }; |
1499 | | |
1500 | 0 | self.core_funcs |
1501 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1502 | 0 | Ok(()) |
1503 | 0 | } |
1504 | | |
1505 | 0 | pub fn future_cancel_write( |
1506 | 0 | &mut self, |
1507 | 0 | ty: u32, |
1508 | 0 | _async_: bool, |
1509 | 0 | types: &mut TypeAlloc, |
1510 | 0 | offset: usize, |
1511 | 0 | features: &WasmFeatures, |
1512 | 0 | ) -> Result<()> { |
1513 | 0 | if !features.component_model_async() { |
1514 | 0 | bail!( |
1515 | 0 | offset, |
1516 | 0 | "`future.cancel-write` requires the component model async feature" |
1517 | 0 | ) |
1518 | 0 | } |
1519 | | |
1520 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1521 | 0 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1522 | 0 | bail!(offset, "`future.cancel-write` requires a future type") |
1523 | | }; |
1524 | | |
1525 | 0 | self.core_funcs |
1526 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1527 | 0 | Ok(()) |
1528 | 0 | } |
1529 | | |
1530 | 0 | pub fn future_close_readable( |
1531 | 0 | &mut self, |
1532 | 0 | ty: u32, |
1533 | 0 | types: &mut TypeAlloc, |
1534 | 0 | offset: usize, |
1535 | 0 | features: &WasmFeatures, |
1536 | 0 | ) -> Result<()> { |
1537 | 0 | if !features.component_model_async() { |
1538 | 0 | bail!( |
1539 | 0 | offset, |
1540 | 0 | "`future.close-readable` requires the component model async feature" |
1541 | 0 | ) |
1542 | 0 | } |
1543 | | |
1544 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1545 | 0 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1546 | 0 | bail!(offset, "`future.close-readable` requires a future type") |
1547 | | }; |
1548 | | |
1549 | 0 | self.core_funcs |
1550 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1551 | 0 | Ok(()) |
1552 | 0 | } |
1553 | | |
1554 | 0 | pub fn future_close_writable( |
1555 | 0 | &mut self, |
1556 | 0 | ty: u32, |
1557 | 0 | types: &mut TypeAlloc, |
1558 | 0 | offset: usize, |
1559 | 0 | features: &WasmFeatures, |
1560 | 0 | ) -> Result<()> { |
1561 | 0 | if !features.component_model_async() { |
1562 | 0 | bail!( |
1563 | 0 | offset, |
1564 | 0 | "`future.close-writable` requires the component model async feature" |
1565 | 0 | ) |
1566 | 0 | } |
1567 | | |
1568 | 0 | let ty = self.defined_type_at(ty, offset)?; |
1569 | 0 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1570 | 0 | bail!(offset, "`future.close-writable` requires a future type") |
1571 | | }; |
1572 | | |
1573 | 0 | self.core_funcs |
1574 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset)); |
1575 | 0 | Ok(()) |
1576 | 0 | } |
1577 | | |
1578 | 0 | pub fn error_context_new( |
1579 | 0 | &mut self, |
1580 | 0 | options: Vec<CanonicalOption>, |
1581 | 0 | types: &mut TypeAlloc, |
1582 | 0 | offset: usize, |
1583 | 0 | features: &WasmFeatures, |
1584 | 0 | ) -> Result<()> { |
1585 | 0 | if !features.component_model_async() { |
1586 | 0 | bail!( |
1587 | 0 | offset, |
1588 | 0 | "`error-context.new` requires the component model async feature" |
1589 | 0 | ) |
1590 | 0 | } |
1591 | 0 |
|
1592 | 0 | let mut info = LoweringInfo::default(); |
1593 | 0 | info.requires_memory = true; |
1594 | 0 | info.requires_realloc = false; |
1595 | 0 | self.check_options(None, &info, &options, types, offset, features, false)?; |
1596 | | |
1597 | 0 | self.core_funcs |
1598 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset)); |
1599 | 0 | Ok(()) |
1600 | 0 | } |
1601 | | |
1602 | 0 | pub fn error_context_debug_message( |
1603 | 0 | &mut self, |
1604 | 0 | options: Vec<CanonicalOption>, |
1605 | 0 | types: &mut TypeAlloc, |
1606 | 0 | offset: usize, |
1607 | 0 | features: &WasmFeatures, |
1608 | 0 | ) -> Result<()> { |
1609 | 0 | if !features.component_model_async() { |
1610 | 0 | bail!( |
1611 | 0 | offset, |
1612 | 0 | "`error-context.debug-message` requires the component model async feature" |
1613 | 0 | ) |
1614 | 0 | } |
1615 | 0 |
|
1616 | 0 | let mut info = LoweringInfo::default(); |
1617 | 0 | info.requires_memory = true; |
1618 | 0 | info.requires_realloc = true; |
1619 | 0 | self.check_options(None, &info, &options, types, offset, features, false)?; |
1620 | | |
1621 | 0 | self.core_funcs |
1622 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset)); |
1623 | 0 | Ok(()) |
1624 | 0 | } |
1625 | | |
1626 | 0 | pub fn error_context_drop( |
1627 | 0 | &mut self, |
1628 | 0 | types: &mut TypeAlloc, |
1629 | 0 | offset: usize, |
1630 | 0 | features: &WasmFeatures, |
1631 | 0 | ) -> Result<()> { |
1632 | 0 | if !features.component_model_async() { |
1633 | 0 | bail!( |
1634 | 0 | offset, |
1635 | 0 | "`error-context.drop` requires the component model async feature" |
1636 | 0 | ) |
1637 | 0 | } |
1638 | 0 |
|
1639 | 0 | self.core_funcs |
1640 | 0 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1641 | 0 | Ok(()) |
1642 | 0 | } |
1643 | | |
1644 | 10.5k | fn check_local_resource(&self, idx: u32, types: &TypeList, offset: usize) -> Result<ValType> { |
1645 | 10.5k | let resource = self.resource_at(idx, types, offset)?; |
1646 | 10.5k | match self |
1647 | 10.5k | .defined_resources |
1648 | 10.5k | .get(&resource.resource()) |
1649 | 10.5k | .and_then(|rep| *rep) |
1650 | | { |
1651 | 10.5k | Some(ty) => Ok(ty), |
1652 | 0 | None => bail!(offset, "type {idx} is not a local resource"), |
1653 | | } |
1654 | 10.5k | } |
1655 | | |
1656 | 51.5k | fn resource_at<'a>( |
1657 | 51.5k | &self, |
1658 | 51.5k | idx: u32, |
1659 | 51.5k | _types: &'a TypeList, |
1660 | 51.5k | offset: usize, |
1661 | 51.5k | ) -> Result<AliasableResourceId> { |
1662 | 51.5k | if let ComponentAnyTypeId::Resource(id) = self.component_type_at(idx, offset)? { |
1663 | 51.5k | return Ok(id); |
1664 | 0 | } |
1665 | 0 | bail!(offset, "type index {} is not a resource type", idx) |
1666 | 51.5k | } |
1667 | | |
1668 | 0 | pub fn thread_spawn( |
1669 | 0 | &mut self, |
1670 | 0 | func_ty_index: u32, |
1671 | 0 | types: &mut TypeAlloc, |
1672 | 0 | offset: usize, |
1673 | 0 | features: &WasmFeatures, |
1674 | 0 | ) -> Result<()> { |
1675 | 0 | if !features.shared_everything_threads() { |
1676 | 0 | bail!( |
1677 | 0 | offset, |
1678 | 0 | "`thread.spawn` requires the shared-everything-threads proposal" |
1679 | 0 | ) |
1680 | 0 | } |
1681 | | |
1682 | | // Validate the type accepted by `thread.spawn`. |
1683 | 0 | let core_type_id = match self.core_type_at(func_ty_index, offset)? { |
1684 | 0 | ComponentCoreTypeId::Sub(c) => c, |
1685 | 0 | ComponentCoreTypeId::Module(_) => bail!(offset, "expected a core function type"), |
1686 | | }; |
1687 | 0 | let sub_ty = &types[core_type_id]; |
1688 | 0 | if !sub_ty.composite_type.shared { |
1689 | 0 | bail!(offset, "spawn type must be shared"); |
1690 | 0 | } |
1691 | 0 | match &sub_ty.composite_type.inner { |
1692 | 0 | CompositeInnerType::Func(func_ty) => { |
1693 | 0 | if func_ty.params() != [ValType::I32] { |
1694 | 0 | bail!( |
1695 | 0 | offset, |
1696 | 0 | "spawn function must take a single `i32` argument (currently)" |
1697 | 0 | ); |
1698 | 0 | } |
1699 | 0 | if func_ty.results() != [] { |
1700 | 0 | bail!(offset, "spawn function must not return any values"); |
1701 | 0 | } |
1702 | | } |
1703 | 0 | _ => bail!(offset, "spawn type must be a function"), |
1704 | | } |
1705 | | |
1706 | | // Insert the core function. |
1707 | 0 | let packed_index = PackedIndex::from_id(core_type_id).ok_or_else(|| { |
1708 | 0 | format_err!(offset, "implementation limit: too many types in `TypeList`") |
1709 | 0 | })?; |
1710 | 0 | let start_func_ref = RefType::concrete(true, packed_index); |
1711 | 0 | let func_ty = FuncType::new([ValType::Ref(start_func_ref), ValType::I32], [ValType::I32]); |
1712 | 0 | let core_ty = SubType::func(func_ty, true); |
1713 | 0 | let id = types.intern_sub_type(core_ty, offset); |
1714 | 0 | self.core_funcs.push(id); |
1715 | 0 |
|
1716 | 0 | Ok(()) |
1717 | 0 | } |
1718 | | |
1719 | 0 | pub fn thread_hw_concurrency( |
1720 | 0 | &mut self, |
1721 | 0 | types: &mut TypeAlloc, |
1722 | 0 | offset: usize, |
1723 | 0 | features: &WasmFeatures, |
1724 | 0 | ) -> Result<()> { |
1725 | 0 | if !features.shared_everything_threads() { |
1726 | 0 | bail!( |
1727 | 0 | offset, |
1728 | 0 | "`thread.hw_concurrency` requires the shared-everything-threads proposal" |
1729 | 0 | ) |
1730 | 0 | } |
1731 | 0 |
|
1732 | 0 | let func_ty = FuncType::new([], [ValType::I32]); |
1733 | 0 | let core_ty = SubType::func(func_ty, true); |
1734 | 0 | let id = types.intern_sub_type(core_ty, offset); |
1735 | 0 | self.core_funcs.push(id); |
1736 | 0 |
|
1737 | 0 | Ok(()) |
1738 | 0 | } |
1739 | | |
1740 | 19.3k | pub fn add_component(&mut self, component: ComponentType, types: &mut TypeAlloc) -> Result<()> { |
1741 | 19.3k | let id = types.push_ty(component); |
1742 | 19.3k | self.components.push(id); |
1743 | 19.3k | Ok(()) |
1744 | 19.3k | } |
1745 | | |
1746 | 19.3k | pub fn add_instance( |
1747 | 19.3k | &mut self, |
1748 | 19.3k | instance: crate::ComponentInstance, |
1749 | 19.3k | features: &WasmFeatures, |
1750 | 19.3k | types: &mut TypeAlloc, |
1751 | 19.3k | offset: usize, |
1752 | 19.3k | ) -> Result<()> { |
1753 | 19.3k | let instance = match instance { |
1754 | | crate::ComponentInstance::Instantiate { |
1755 | 19.3k | component_index, |
1756 | 19.3k | args, |
1757 | 19.3k | } => self.instantiate_component( |
1758 | 19.3k | component_index, |
1759 | 19.3k | args.into_vec(), |
1760 | 19.3k | features, |
1761 | 19.3k | types, |
1762 | 19.3k | offset, |
1763 | 19.3k | )?, |
1764 | 0 | crate::ComponentInstance::FromExports(exports) => { |
1765 | 0 | self.instantiate_component_exports(exports.into_vec(), features, types, offset)? |
1766 | | } |
1767 | | }; |
1768 | | |
1769 | 19.3k | self.instances.push(instance); |
1770 | 19.3k | |
1771 | 19.3k | Ok(()) |
1772 | 19.3k | } |
1773 | | |
1774 | 208k | pub fn add_alias( |
1775 | 208k | components: &mut [Self], |
1776 | 208k | alias: crate::ComponentAlias, |
1777 | 208k | features: &WasmFeatures, |
1778 | 208k | types: &mut TypeAlloc, |
1779 | 208k | offset: usize, |
1780 | 208k | ) -> Result<()> { |
1781 | 208k | match alias { |
1782 | | crate::ComponentAlias::InstanceExport { |
1783 | 50.6k | instance_index, |
1784 | 50.6k | kind, |
1785 | 50.6k | name, |
1786 | 50.6k | } => components.last_mut().unwrap().alias_instance_export( |
1787 | 50.6k | instance_index, |
1788 | 50.6k | kind, |
1789 | 50.6k | name, |
1790 | 50.6k | features, |
1791 | 50.6k | types, |
1792 | 50.6k | offset, |
1793 | 50.6k | ), |
1794 | | crate::ComponentAlias::CoreInstanceExport { |
1795 | 139k | instance_index, |
1796 | 139k | kind, |
1797 | 139k | name, |
1798 | 139k | } => components.last_mut().unwrap().alias_core_instance_export( |
1799 | 139k | instance_index, |
1800 | 139k | kind, |
1801 | 139k | name, |
1802 | 139k | types, |
1803 | 139k | offset, |
1804 | 139k | ), |
1805 | 18.5k | crate::ComponentAlias::Outer { kind, count, index } => match kind { |
1806 | | ComponentOuterAliasKind::CoreModule => { |
1807 | 0 | Self::alias_module(components, count, index, offset) |
1808 | | } |
1809 | | ComponentOuterAliasKind::CoreType => { |
1810 | 0 | Self::alias_core_type(components, count, index, offset) |
1811 | | } |
1812 | | ComponentOuterAliasKind::Type => { |
1813 | 18.5k | Self::alias_type(components, count, index, types, offset) |
1814 | | } |
1815 | | ComponentOuterAliasKind::Component => { |
1816 | 0 | Self::alias_component(components, count, index, offset) |
1817 | | } |
1818 | | }, |
1819 | | } |
1820 | 208k | } |
1821 | | |
1822 | 0 | pub fn add_start( |
1823 | 0 | &mut self, |
1824 | 0 | func_index: u32, |
1825 | 0 | args: &[u32], |
1826 | 0 | results: u32, |
1827 | 0 | features: &WasmFeatures, |
1828 | 0 | types: &mut TypeList, |
1829 | 0 | offset: usize, |
1830 | 0 | ) -> Result<()> { |
1831 | 0 | if !features.component_model_values() { |
1832 | 0 | bail!( |
1833 | 0 | offset, |
1834 | 0 | "support for component model `value`s is not enabled" |
1835 | 0 | ); |
1836 | 0 | } |
1837 | 0 | if self.has_start { |
1838 | 0 | return Err(BinaryReaderError::new( |
1839 | 0 | "component cannot have more than one start function", |
1840 | 0 | offset, |
1841 | 0 | )); |
1842 | 0 | } |
1843 | | |
1844 | 0 | let ft = &types[self.function_at(func_index, offset)?]; |
1845 | | |
1846 | 0 | if ft.params.len() != args.len() { |
1847 | 0 | bail!( |
1848 | 0 | offset, |
1849 | 0 | "component start function requires {} arguments but was given {}", |
1850 | 0 | ft.params.len(), |
1851 | 0 | args.len() |
1852 | 0 | ); |
1853 | 0 | } |
1854 | 0 |
|
1855 | 0 | if ft.results.len() as u32 != results { |
1856 | 0 | bail!( |
1857 | 0 | offset, |
1858 | 0 | "component start function has a result count of {results} \ |
1859 | 0 | but the function type has a result count of {type_results}", |
1860 | 0 | type_results = ft.results.len(), |
1861 | 0 | ); |
1862 | 0 | } |
1863 | 0 |
|
1864 | 0 | let cx = SubtypeCx::new(types, types); |
1865 | 0 | for (i, ((_, ty), arg)) in ft.params.iter().zip(args).enumerate() { |
1866 | | // Ensure the value's type is a subtype of the parameter type |
1867 | 0 | cx.component_val_type(self.value_at(*arg, offset)?, ty, offset) |
1868 | 0 | .with_context(|| { |
1869 | 0 | format!("value type mismatch for component start function argument {i}") |
1870 | 0 | })?; |
1871 | | } |
1872 | | |
1873 | 0 | for (_, ty) in ft.results.iter() { |
1874 | 0 | self.values.push((*ty, false)); |
1875 | 0 | } |
1876 | | |
1877 | 0 | self.has_start = true; |
1878 | 0 |
|
1879 | 0 | Ok(()) |
1880 | 0 | } |
1881 | | |
1882 | 77.6k | fn check_options( |
1883 | 77.6k | &self, |
1884 | 77.6k | core_ty: Option<&FuncType>, |
1885 | 77.6k | info: &LoweringInfo, |
1886 | 77.6k | options: &[CanonicalOption], |
1887 | 77.6k | types: &TypeList, |
1888 | 77.6k | offset: usize, |
1889 | 77.6k | features: &WasmFeatures, |
1890 | 77.6k | allow_async: bool, |
1891 | 77.6k | ) -> Result<()> { |
1892 | 0 | fn display(option: CanonicalOption) -> &'static str { |
1893 | 0 | match option { |
1894 | 0 | CanonicalOption::UTF8 => "utf8", |
1895 | 0 | CanonicalOption::UTF16 => "utf16", |
1896 | 0 | CanonicalOption::CompactUTF16 => "latin1-utf16", |
1897 | 0 | CanonicalOption::Memory(_) => "memory", |
1898 | 0 | CanonicalOption::Realloc(_) => "realloc", |
1899 | 0 | CanonicalOption::PostReturn(_) => "post-return", |
1900 | 0 | CanonicalOption::Async => "async", |
1901 | 0 | CanonicalOption::Callback(_) => "callback", |
1902 | | } |
1903 | 0 | } |
1904 | | |
1905 | 77.6k | let mut encoding = None; |
1906 | 77.6k | let mut memory = None; |
1907 | 77.6k | let mut realloc = None; |
1908 | 77.6k | let mut post_return = None; |
1909 | 77.6k | let mut async_ = false; |
1910 | 77.6k | let mut callback = None; |
1911 | | |
1912 | 152k | for option in options { |
1913 | 74.8k | match option { |
1914 | | CanonicalOption::UTF8 | CanonicalOption::UTF16 | CanonicalOption::CompactUTF16 => { |
1915 | 1.16k | match encoding { |
1916 | 0 | Some(existing) => { |
1917 | 0 | bail!( |
1918 | 0 | offset, |
1919 | 0 | "canonical encoding option `{}` conflicts with option `{}`", |
1920 | 0 | display(existing), |
1921 | 0 | display(*option), |
1922 | 0 | ) |
1923 | | } |
1924 | 1.16k | None => encoding = Some(*option), |
1925 | | } |
1926 | | } |
1927 | 12.7k | CanonicalOption::Memory(idx) => { |
1928 | 12.7k | memory = match memory { |
1929 | | None => { |
1930 | 12.7k | self.memory_at(*idx, offset)?; |
1931 | 12.7k | Some(*idx) |
1932 | | } |
1933 | | Some(_) => { |
1934 | 0 | return Err(BinaryReaderError::new( |
1935 | 0 | "canonical option `memory` is specified more than once", |
1936 | 0 | offset, |
1937 | 0 | )) |
1938 | | } |
1939 | | } |
1940 | | } |
1941 | 1.66k | CanonicalOption::Realloc(idx) => { |
1942 | 1.66k | realloc = match realloc { |
1943 | | None => { |
1944 | 1.66k | let ty = types[self.core_function_at(*idx, offset)?].unwrap_func(); |
1945 | 1.66k | if ty.params() |
1946 | 1.66k | != [ValType::I32, ValType::I32, ValType::I32, ValType::I32] |
1947 | 1.66k | || ty.results() != [ValType::I32] |
1948 | | { |
1949 | 0 | return Err(BinaryReaderError::new( |
1950 | 0 | "canonical option `realloc` uses a core function with an incorrect signature", |
1951 | 0 | offset, |
1952 | 0 | )); |
1953 | 1.66k | } |
1954 | 1.66k | Some(*idx) |
1955 | | } |
1956 | | Some(_) => { |
1957 | 0 | return Err(BinaryReaderError::new( |
1958 | 0 | "canonical option `realloc` is specified more than once", |
1959 | 0 | offset, |
1960 | 0 | )) |
1961 | | } |
1962 | | } |
1963 | | } |
1964 | 32.6k | CanonicalOption::PostReturn(idx) => { |
1965 | 32.6k | post_return = match post_return { |
1966 | | None => { |
1967 | 32.6k | let core_ty = core_ty.ok_or_else(|| { |
1968 | 0 | BinaryReaderError::new( |
1969 | 0 | "canonical option `post-return` cannot be specified for lowerings", |
1970 | 0 | offset, |
1971 | 0 | ) |
1972 | 32.6k | })?; |
1973 | | |
1974 | 32.6k | let ty = types[self.core_function_at(*idx, offset)?].unwrap_func(); |
1975 | 32.6k | |
1976 | 32.6k | if ty.params() != core_ty.results() || !ty.results().is_empty() { |
1977 | 0 | return Err(BinaryReaderError::new( |
1978 | 0 | "canonical option `post-return` uses a core function with an incorrect signature", |
1979 | 0 | offset, |
1980 | 0 | )); |
1981 | 32.6k | } |
1982 | 32.6k | Some(*idx) |
1983 | | } |
1984 | | Some(_) => { |
1985 | 0 | return Err(BinaryReaderError::new( |
1986 | 0 | "canonical option `post-return` is specified more than once", |
1987 | 0 | offset, |
1988 | 0 | )) |
1989 | | } |
1990 | | } |
1991 | | } |
1992 | | CanonicalOption::Async => { |
1993 | 18.6k | if async_ { |
1994 | 0 | return Err(BinaryReaderError::new( |
1995 | 0 | "canonical option `async` is specified more than once", |
1996 | 0 | offset, |
1997 | 0 | )); |
1998 | | } else { |
1999 | 18.6k | if !features.component_model_async() { |
2000 | 0 | bail!( |
2001 | 0 | offset, |
2002 | 0 | "canonical option `async` requires the component model async feature" |
2003 | 0 | ); |
2004 | 18.6k | } |
2005 | 18.6k | |
2006 | 18.6k | async_ = true; |
2007 | | } |
2008 | | } |
2009 | 7.92k | CanonicalOption::Callback(idx) => { |
2010 | 7.92k | callback = match callback { |
2011 | | None => { |
2012 | 7.92k | if core_ty.is_none() { |
2013 | 0 | return Err(BinaryReaderError::new( |
2014 | 0 | "canonical option `callback` cannot be specified for lowerings", |
2015 | 0 | offset, |
2016 | 0 | )); |
2017 | 7.92k | } |
2018 | | |
2019 | 7.92k | let ty = types[self.core_function_at(*idx, offset)?].unwrap_func(); |
2020 | 7.92k | |
2021 | 7.92k | if ty.params() != [ValType::I32; 4] && ty.params() != [ValType::I32] { |
2022 | 0 | return Err(BinaryReaderError::new( |
2023 | 0 | "canonical option `callback` uses a core function with an incorrect signature", |
2024 | 0 | offset, |
2025 | 0 | )); |
2026 | 7.92k | } |
2027 | 7.92k | Some(*idx) |
2028 | | } |
2029 | | Some(_) => { |
2030 | 0 | return Err(BinaryReaderError::new( |
2031 | 0 | "canonical option `callback` is specified more than once", |
2032 | 0 | offset, |
2033 | 0 | )) |
2034 | | } |
2035 | | } |
2036 | | } |
2037 | | } |
2038 | | } |
2039 | | |
2040 | 77.6k | if async_ && !allow_async { |
2041 | 0 | bail!(offset, "async option not allowed here") |
2042 | 77.6k | } |
2043 | 77.6k | |
2044 | 77.6k | if callback.is_some() && !async_ { |
2045 | 0 | bail!(offset, "cannot specify callback without lifting async") |
2046 | 77.6k | } |
2047 | 77.6k | |
2048 | 77.6k | if info.requires_memory && memory.is_none() { |
2049 | 0 | return Err(BinaryReaderError::new( |
2050 | 0 | "canonical option `memory` is required", |
2051 | 0 | offset, |
2052 | 0 | )); |
2053 | 77.6k | } |
2054 | 77.6k | |
2055 | 77.6k | if info.requires_realloc && realloc.is_none() { |
2056 | 0 | return Err(BinaryReaderError::new( |
2057 | 0 | "canonical option `realloc` is required", |
2058 | 0 | offset, |
2059 | 0 | )); |
2060 | 77.6k | } |
2061 | 77.6k | |
2062 | 77.6k | Ok(()) |
2063 | 77.6k | } |
2064 | | |
2065 | 756k | fn check_type_ref( |
2066 | 756k | &mut self, |
2067 | 756k | ty: &ComponentTypeRef, |
2068 | 756k | features: &WasmFeatures, |
2069 | 756k | types: &mut TypeAlloc, |
2070 | 756k | offset: usize, |
2071 | 756k | ) -> Result<ComponentEntityType> { |
2072 | 352k | Ok(match ty { |
2073 | 0 | ComponentTypeRef::Module(index) => { |
2074 | 0 | let id = self.core_type_at(*index, offset)?; |
2075 | 0 | match id { |
2076 | | ComponentCoreTypeId::Sub(_) => { |
2077 | 0 | bail!(offset, "core type index {index} is not a module type") |
2078 | | } |
2079 | 0 | ComponentCoreTypeId::Module(id) => ComponentEntityType::Module(id), |
2080 | | } |
2081 | | } |
2082 | 288k | ComponentTypeRef::Func(index) => { |
2083 | 288k | let id = self.component_type_at(*index, offset)?; |
2084 | 288k | match id { |
2085 | 288k | ComponentAnyTypeId::Func(id) => ComponentEntityType::Func(id), |
2086 | 0 | _ => bail!(offset, "type index {index} is not a function type"), |
2087 | | } |
2088 | | } |
2089 | 0 | ComponentTypeRef::Value(ty) => { |
2090 | 0 | self.check_value_support(features, offset)?; |
2091 | 0 | let ty = match ty { |
2092 | 0 | crate::ComponentValType::Primitive(ty) => ComponentValType::Primitive(*ty), |
2093 | 0 | crate::ComponentValType::Type(index) => { |
2094 | 0 | ComponentValType::Type(self.defined_type_at(*index, offset)?) |
2095 | | } |
2096 | | }; |
2097 | 0 | ComponentEntityType::Value(ty) |
2098 | | } |
2099 | 321k | ComponentTypeRef::Type(TypeBounds::Eq(index)) => { |
2100 | 321k | let referenced = self.component_type_at(*index, offset)?; |
2101 | 321k | let created = types.with_unique(referenced); |
2102 | 321k | ComponentEntityType::Type { |
2103 | 321k | referenced, |
2104 | 321k | created, |
2105 | 321k | } |
2106 | | } |
2107 | | ComponentTypeRef::Type(TypeBounds::SubResource) => { |
2108 | 30.9k | let id = types.alloc_resource_id(); |
2109 | 30.9k | ComponentEntityType::Type { |
2110 | 30.9k | referenced: id.into(), |
2111 | 30.9k | created: id.into(), |
2112 | 30.9k | } |
2113 | | } |
2114 | 89.9k | ComponentTypeRef::Instance(index) => { |
2115 | 89.9k | let id = self.component_type_at(*index, offset)?; |
2116 | 89.9k | match id { |
2117 | 89.9k | ComponentAnyTypeId::Instance(id) => ComponentEntityType::Instance(id), |
2118 | 0 | _ => bail!(offset, "type index {index} is not an instance type"), |
2119 | | } |
2120 | | } |
2121 | 25.3k | ComponentTypeRef::Component(index) => { |
2122 | 25.3k | let id = self.component_type_at(*index, offset)?; |
2123 | 25.3k | match id { |
2124 | 25.3k | ComponentAnyTypeId::Component(id) => ComponentEntityType::Component(id), |
2125 | 0 | _ => bail!(offset, "type index {index} is not a component type"), |
2126 | | } |
2127 | | } |
2128 | | }) |
2129 | 756k | } |
2130 | | |
2131 | 209k | pub fn export_to_entity_type( |
2132 | 209k | &mut self, |
2133 | 209k | export: &crate::ComponentExport, |
2134 | 209k | features: &WasmFeatures, |
2135 | 209k | types: &mut TypeAlloc, |
2136 | 209k | offset: usize, |
2137 | 209k | ) -> Result<ComponentEntityType> { |
2138 | 209k | let actual = match export.kind { |
2139 | | ComponentExternalKind::Module => { |
2140 | 0 | ComponentEntityType::Module(self.module_at(export.index, offset)?) |
2141 | | } |
2142 | | ComponentExternalKind::Func => { |
2143 | 42.3k | ComponentEntityType::Func(self.function_at(export.index, offset)?) |
2144 | | } |
2145 | | ComponentExternalKind::Value => { |
2146 | 0 | self.check_value_support(features, offset)?; |
2147 | 0 | ComponentEntityType::Value(*self.value_at(export.index, offset)?) |
2148 | | } |
2149 | | ComponentExternalKind::Type => { |
2150 | 147k | let referenced = self.component_type_at(export.index, offset)?; |
2151 | 147k | let created = types.with_unique(referenced); |
2152 | 147k | ComponentEntityType::Type { |
2153 | 147k | referenced, |
2154 | 147k | created, |
2155 | 147k | } |
2156 | | } |
2157 | | ComponentExternalKind::Instance => { |
2158 | 19.3k | ComponentEntityType::Instance(self.instance_at(export.index, offset)?) |
2159 | | } |
2160 | | ComponentExternalKind::Component => { |
2161 | 0 | ComponentEntityType::Component(self.component_at(export.index, offset)?) |
2162 | | } |
2163 | | }; |
2164 | | |
2165 | 209k | let ascribed = match &export.ty { |
2166 | 40.5k | Some(ty) => self.check_type_ref(ty, features, types, offset)?, |
2167 | 168k | None => return Ok(actual), |
2168 | | }; |
2169 | | |
2170 | 40.5k | SubtypeCx::new(types, types) |
2171 | 40.5k | .component_entity_type(&actual, &ascribed, offset) |
2172 | 40.5k | .with_context(|| "ascribed type of export is not compatible with item's type")?; |
2173 | | |
2174 | 40.5k | Ok(ascribed) |
2175 | 209k | } |
2176 | | |
2177 | 0 | fn create_module_type( |
2178 | 0 | components: &[Self], |
2179 | 0 | decls: Vec<crate::ModuleTypeDeclaration>, |
2180 | 0 | features: &WasmFeatures, |
2181 | 0 | types: &mut TypeAlloc, |
2182 | 0 | offset: usize, |
2183 | 0 | ) -> Result<ModuleType> { |
2184 | 0 | let mut state = Module::default(); |
2185 | | |
2186 | 0 | for decl in decls { |
2187 | 0 | match decl { |
2188 | 0 | crate::ModuleTypeDeclaration::Type(rec) => { |
2189 | 0 | state.add_types(rec, features, types, offset, true)?; |
2190 | | } |
2191 | 0 | crate::ModuleTypeDeclaration::Export { name, mut ty } => { |
2192 | 0 | let ty = state.check_type_ref(&mut ty, features, types, offset)?; |
2193 | 0 | state.add_export(name, ty, features, offset, true, types)?; |
2194 | | } |
2195 | 0 | crate::ModuleTypeDeclaration::OuterAlias { kind, count, index } => { |
2196 | 0 | match kind { |
2197 | | crate::OuterAliasKind::Type => { |
2198 | 0 | let ty = if count == 0 { |
2199 | | // Local alias, check the local module state |
2200 | 0 | ComponentCoreTypeId::Sub(state.type_id_at(index, offset)?) |
2201 | | } else { |
2202 | | // Otherwise, check the enclosing component state |
2203 | 0 | let component = |
2204 | 0 | Self::check_alias_count(components, count - 1, offset)?; |
2205 | 0 | component.core_type_at(index, offset)? |
2206 | | }; |
2207 | | |
2208 | 0 | check_max(state.types.len(), 1, MAX_WASM_TYPES, "types", offset)?; |
2209 | | |
2210 | 0 | match ty { |
2211 | 0 | ComponentCoreTypeId::Sub(ty) => state.types.push(ty), |
2212 | | // TODO https://github.com/WebAssembly/component-model/issues/265 |
2213 | 0 | ComponentCoreTypeId::Module(_) => bail!( |
2214 | 0 | offset, |
2215 | 0 | "not implemented: aliasing core module types into a core \ |
2216 | 0 | module's types index space" |
2217 | 0 | ), |
2218 | | } |
2219 | | } |
2220 | | } |
2221 | | } |
2222 | 0 | crate::ModuleTypeDeclaration::Import(import) => { |
2223 | 0 | state.add_import(import, features, types, offset)?; |
2224 | | } |
2225 | | } |
2226 | | } |
2227 | | |
2228 | 0 | let imports = state.imports_for_module_type(offset)?; |
2229 | | |
2230 | 0 | Ok(ModuleType { |
2231 | 0 | info: TypeInfo::core(state.type_size), |
2232 | 0 | imports, |
2233 | 0 | exports: state.exports, |
2234 | 0 | }) |
2235 | 0 | } |
2236 | | |
2237 | 76.5k | fn create_component_type( |
2238 | 76.5k | components: &mut Vec<Self>, |
2239 | 76.5k | decls: Vec<crate::ComponentTypeDeclaration>, |
2240 | 76.5k | features: &WasmFeatures, |
2241 | 76.5k | types: &mut TypeAlloc, |
2242 | 76.5k | offset: usize, |
2243 | 76.5k | ) -> Result<ComponentType> { |
2244 | 76.5k | components.push(ComponentState::new(ComponentKind::ComponentType)); |
2245 | | |
2246 | 446k | for decl in decls { |
2247 | 369k | match decl { |
2248 | 0 | crate::ComponentTypeDeclaration::CoreType(ty) => { |
2249 | 0 | Self::add_core_type(components, ty, features, types, offset, true)?; |
2250 | | } |
2251 | 190k | crate::ComponentTypeDeclaration::Type(ty) => { |
2252 | 190k | Self::add_type(components, ty, features, types, offset, true)?; |
2253 | | } |
2254 | 93.4k | crate::ComponentTypeDeclaration::Export { name, ty } => { |
2255 | 93.4k | let current = components.last_mut().unwrap(); |
2256 | 93.4k | let ty = current.check_type_ref(&ty, features, types, offset)?; |
2257 | 93.4k | current.add_export(name, ty, features, types, offset, true)?; |
2258 | | } |
2259 | 68.6k | crate::ComponentTypeDeclaration::Import(import) => { |
2260 | 68.6k | components |
2261 | 68.6k | .last_mut() |
2262 | 68.6k | .unwrap() |
2263 | 68.6k | .add_import(import, features, types, offset)?; |
2264 | | } |
2265 | 16.9k | crate::ComponentTypeDeclaration::Alias(alias) => { |
2266 | 16.9k | Self::add_alias(components, alias, features, types, offset)?; |
2267 | | } |
2268 | | }; |
2269 | | } |
2270 | | |
2271 | 76.5k | components.pop().unwrap().finish(types, offset) |
2272 | 76.5k | } |
2273 | | |
2274 | 89.9k | fn create_instance_type( |
2275 | 89.9k | components: &mut Vec<Self>, |
2276 | 89.9k | decls: Vec<crate::InstanceTypeDeclaration>, |
2277 | 89.9k | features: &WasmFeatures, |
2278 | 89.9k | types: &mut TypeAlloc, |
2279 | 89.9k | offset: usize, |
2280 | 89.9k | ) -> Result<ComponentInstanceType> { |
2281 | 89.9k | components.push(ComponentState::new(ComponentKind::InstanceType)); |
2282 | | |
2283 | 1.21M | for decl in decls { |
2284 | 1.12M | match decl { |
2285 | 0 | crate::InstanceTypeDeclaration::CoreType(ty) => { |
2286 | 0 | Self::add_core_type(components, ty, features, types, offset, true)?; |
2287 | | } |
2288 | 635k | crate::InstanceTypeDeclaration::Type(ty) => { |
2289 | 635k | Self::add_type(components, ty, features, types, offset, true)?; |
2290 | | } |
2291 | 472k | crate::InstanceTypeDeclaration::Export { name, ty } => { |
2292 | 472k | let current = components.last_mut().unwrap(); |
2293 | 472k | let ty = current.check_type_ref(&ty, features, types, offset)?; |
2294 | 472k | current.add_export(name, ty, features, types, offset, true)?; |
2295 | | } |
2296 | 18.5k | crate::InstanceTypeDeclaration::Alias(alias) => { |
2297 | 18.5k | Self::add_alias(components, alias, features, types, offset)?; |
2298 | | } |
2299 | | }; |
2300 | | } |
2301 | | |
2302 | 89.9k | let mut state = components.pop().unwrap(); |
2303 | 89.9k | |
2304 | 89.9k | assert!(state.imported_resources.is_empty()); |
2305 | | |
2306 | 89.9k | Ok(ComponentInstanceType { |
2307 | 89.9k | info: state.type_info, |
2308 | 89.9k | |
2309 | 89.9k | // The defined resources for this instance type are those listed on |
2310 | 89.9k | // the component state. The path to each defined resource is |
2311 | 89.9k | // guaranteed to live within the `explicit_resources` map since, |
2312 | 89.9k | // when in the type context, the introduction of any defined |
2313 | 89.9k | // resource must have been done with `(export "x" (type (sub |
2314 | 89.9k | // resource)))` which, in a sense, "fuses" the introduction of the |
2315 | 89.9k | // variable with the export. This means that all defined resources, |
2316 | 89.9k | // if any, should be guaranteed to have an `explicit_resources` path |
2317 | 89.9k | // listed. |
2318 | 89.9k | defined_resources: mem::take(&mut state.defined_resources) |
2319 | 89.9k | .into_iter() |
2320 | 89.9k | .map(|(id, rep)| { |
2321 | 20.5k | assert!(rep.is_none()); |
2322 | 20.5k | id |
2323 | 89.9k | }) |
2324 | 89.9k | .collect(), |
2325 | 89.9k | |
2326 | 89.9k | // The map of what resources are explicitly exported and where |
2327 | 89.9k | // they're exported is plumbed through as-is. |
2328 | 89.9k | explicit_resources: mem::take(&mut state.explicit_resources), |
2329 | 89.9k | |
2330 | 89.9k | exports: mem::take(&mut state.exports), |
2331 | 89.9k | }) |
2332 | 89.9k | } |
2333 | | |
2334 | 197k | fn create_function_type( |
2335 | 197k | &self, |
2336 | 197k | ty: crate::ComponentFuncType, |
2337 | 197k | types: &TypeList, |
2338 | 197k | features: &WasmFeatures, |
2339 | 197k | offset: usize, |
2340 | 197k | ) -> Result<ComponentFuncType> { |
2341 | 197k | let mut info = TypeInfo::new(); |
2342 | 197k | |
2343 | 197k | if ty.results.type_count() > 1 && !features.component_model_multiple_returns() { |
2344 | 0 | bail!( |
2345 | 0 | offset, |
2346 | 0 | "multiple returns on a function is now a gated feature \ |
2347 | 0 | -- https://github.com/WebAssembly/component-model/pull/368" |
2348 | 0 | ); |
2349 | 197k | } |
2350 | 197k | |
2351 | 197k | let mut set = Set::default(); |
2352 | 197k | set.reserve(core::cmp::max(ty.params.len(), ty.results.type_count())); |
2353 | | |
2354 | 197k | let params = ty |
2355 | 197k | .params |
2356 | 197k | .iter() |
2357 | 497k | .map(|(name, ty)| { |
2358 | 497k | let name: &KebabStr = to_kebab_str(name, "function parameter", offset)?; |
2359 | 497k | if !set.insert(name) { |
2360 | 0 | bail!( |
2361 | 0 | offset, |
2362 | 0 | "function parameter name `{name}` conflicts with previous parameter name `{prev}`", |
2363 | 0 | prev = set.get(&name).unwrap(), |
2364 | 0 | ); |
2365 | 497k | } |
2366 | | |
2367 | 497k | let ty = self.create_component_val_type(*ty, offset)?; |
2368 | 497k | info.combine(ty.info(types), offset)?; |
2369 | 497k | Ok((name.to_owned(), ty)) |
2370 | 497k | }) |
2371 | 197k | .collect::<Result<_>>()?; |
2372 | | |
2373 | 197k | set.clear(); |
2374 | | |
2375 | 197k | let results = ty |
2376 | 197k | .results |
2377 | 197k | .iter() |
2378 | 197k | .map(|(name, ty)| { |
2379 | 170k | let name = name |
2380 | 170k | .map(|name| { |
2381 | 0 | let name = to_kebab_str(name, "function result", offset)?; |
2382 | 0 | if !set.insert(name) { |
2383 | 0 | bail!( |
2384 | 0 | offset, |
2385 | 0 | "function result name `{name}` conflicts with previous result name `{prev}`", |
2386 | 0 | prev = set.get(name).unwrap(), |
2387 | 0 | ); |
2388 | 0 | } |
2389 | 0 |
|
2390 | 0 | Ok(name.to_owned()) |
2391 | 170k | }) |
2392 | 170k | .transpose()?; |
2393 | | |
2394 | 170k | let ty = self.create_component_val_type(*ty, offset)?; |
2395 | 170k | let ty_info = ty.info(types); |
2396 | 170k | if ty_info.contains_borrow() { |
2397 | 0 | bail!(offset, "function result cannot contain a `borrow` type"); |
2398 | 170k | } |
2399 | 170k | info.combine(ty.info(types), offset)?; |
2400 | 170k | Ok((name, ty)) |
2401 | 197k | }) |
2402 | 197k | .collect::<Result<_>>()?; |
2403 | | |
2404 | 197k | Ok(ComponentFuncType { |
2405 | 197k | info, |
2406 | 197k | params, |
2407 | 197k | results, |
2408 | 197k | }) |
2409 | 197k | } |
2410 | | |
2411 | 32.3k | fn instantiate_core_module( |
2412 | 32.3k | &self, |
2413 | 32.3k | module_index: u32, |
2414 | 32.3k | module_args: Vec<crate::InstantiationArg>, |
2415 | 32.3k | types: &mut TypeAlloc, |
2416 | 32.3k | offset: usize, |
2417 | 32.3k | ) -> Result<ComponentCoreInstanceTypeId> { |
2418 | 27.0k | fn insert_arg<'a>( |
2419 | 27.0k | name: &'a str, |
2420 | 27.0k | arg: &'a InstanceType, |
2421 | 27.0k | args: &mut IndexMap<&'a str, &'a InstanceType>, |
2422 | 27.0k | offset: usize, |
2423 | 27.0k | ) -> Result<()> { |
2424 | 27.0k | if args.insert(name, arg).is_some() { |
2425 | 0 | bail!( |
2426 | 0 | offset, |
2427 | 0 | "duplicate module instantiation argument named `{name}`" |
2428 | 0 | ); |
2429 | 27.0k | } |
2430 | 27.0k | |
2431 | 27.0k | Ok(()) |
2432 | 27.0k | } |
2433 | | |
2434 | 32.3k | let module_type_id = self.module_at(module_index, offset)?; |
2435 | 32.3k | let mut args = IndexMap::default(); |
2436 | | |
2437 | | // Populate the arguments |
2438 | 59.4k | for module_arg in module_args { |
2439 | 27.0k | match module_arg.kind { |
2440 | | InstantiationArgKind::Instance => { |
2441 | 27.0k | let instance_type = &types[self.core_instance_at(module_arg.index, offset)?]; |
2442 | 27.0k | insert_arg(module_arg.name, instance_type, &mut args, offset)?; |
2443 | | } |
2444 | | } |
2445 | | } |
2446 | | |
2447 | | // Validate the arguments |
2448 | 32.3k | let module_type = &types[module_type_id]; |
2449 | 32.3k | let cx = SubtypeCx::new(types, types); |
2450 | 87.9k | for ((module, name), expected) in module_type.imports.iter() { |
2451 | 87.9k | let instance = args.get(module.as_str()).ok_or_else(|| { |
2452 | 0 | format_err!( |
2453 | 0 | offset, |
2454 | 0 | "missing module instantiation argument named `{module}`" |
2455 | 0 | ) |
2456 | 87.9k | })?; |
2457 | | |
2458 | 87.9k | let arg = instance |
2459 | 87.9k | .internal_exports(types) |
2460 | 87.9k | .get(name.as_str()) |
2461 | 87.9k | .ok_or_else(|| { |
2462 | 0 | format_err!( |
2463 | 0 | offset, |
2464 | 0 | "module instantiation argument `{module}` does not \ |
2465 | 0 | export an item named `{name}`", |
2466 | 0 | ) |
2467 | 87.9k | })?; |
2468 | | |
2469 | 87.9k | cx.entity_type(arg, expected, offset).with_context(|| { |
2470 | 0 | format!( |
2471 | 0 | "type mismatch for export `{name}` of module \ |
2472 | 0 | instantiation argument `{module}`" |
2473 | 0 | ) |
2474 | 87.9k | })?; |
2475 | | } |
2476 | | |
2477 | 32.3k | let mut info = TypeInfo::new(); |
2478 | 145k | for (_, ty) in module_type.exports.iter() { |
2479 | 145k | info.combine(ty.info(types), offset)?; |
2480 | | } |
2481 | | |
2482 | 32.3k | Ok(types.push_ty(InstanceType { |
2483 | 32.3k | info, |
2484 | 32.3k | kind: CoreInstanceTypeKind::Instantiated(module_type_id), |
2485 | 32.3k | })) |
2486 | 32.3k | } |
2487 | | |
2488 | 19.3k | fn instantiate_component( |
2489 | 19.3k | &mut self, |
2490 | 19.3k | component_index: u32, |
2491 | 19.3k | component_args: Vec<crate::ComponentInstantiationArg>, |
2492 | 19.3k | features: &WasmFeatures, |
2493 | 19.3k | types: &mut TypeAlloc, |
2494 | 19.3k | offset: usize, |
2495 | 19.3k | ) -> Result<ComponentInstanceTypeId> { |
2496 | 19.3k | let component_type_id = self.component_at(component_index, offset)?; |
2497 | 19.3k | let mut args = IndexMap::default(); |
2498 | | |
2499 | | // Populate the arguments |
2500 | 70.2k | for component_arg in component_args { |
2501 | 50.8k | let ty = match component_arg.kind { |
2502 | | ComponentExternalKind::Module => { |
2503 | 0 | ComponentEntityType::Module(self.module_at(component_arg.index, offset)?) |
2504 | | } |
2505 | | ComponentExternalKind::Component => { |
2506 | 0 | ComponentEntityType::Component(self.component_at(component_arg.index, offset)?) |
2507 | | } |
2508 | | ComponentExternalKind::Instance => { |
2509 | 0 | ComponentEntityType::Instance(self.instance_at(component_arg.index, offset)?) |
2510 | | } |
2511 | | ComponentExternalKind::Func => { |
2512 | 40.5k | ComponentEntityType::Func(self.function_at(component_arg.index, offset)?) |
2513 | | } |
2514 | | ComponentExternalKind::Value => { |
2515 | 0 | self.check_value_support(features, offset)?; |
2516 | 0 | ComponentEntityType::Value(*self.value_at(component_arg.index, offset)?) |
2517 | | } |
2518 | | ComponentExternalKind::Type => { |
2519 | 10.3k | let ty = self.component_type_at(component_arg.index, offset)?; |
2520 | 10.3k | ComponentEntityType::Type { |
2521 | 10.3k | referenced: ty, |
2522 | 10.3k | created: ty, |
2523 | 10.3k | } |
2524 | | } |
2525 | | }; |
2526 | 50.8k | match args.entry(component_arg.name.to_string()) { |
2527 | 0 | Entry::Occupied(e) => { |
2528 | 0 | bail!( |
2529 | 0 | offset, |
2530 | 0 | "instantiation argument `{name}` conflicts with previous argument `{prev}`", |
2531 | 0 | prev = e.key(), |
2532 | 0 | name = component_arg.name |
2533 | 0 | ); |
2534 | | } |
2535 | 50.8k | Entry::Vacant(e) => { |
2536 | 50.8k | e.insert(ty); |
2537 | 50.8k | } |
2538 | | } |
2539 | | } |
2540 | | |
2541 | | // Here comes the fun part of the component model, we're instantiating |
2542 | | // the component with type `component_type_id` with the `args` |
2543 | | // specified. Easy enough! |
2544 | | // |
2545 | | // This operation, however, is one of the lynchpins of safety in the |
2546 | | // component model. Additionally what this ends up implementing ranges |
2547 | | // from "well just check the types are equal" to "let's have a |
2548 | | // full-blown ML-style module type system in the component model". There |
2549 | | // are primarily two major tricky pieces to the component model which |
2550 | | // make this operation, instantiating components, hard: |
2551 | | // |
2552 | | // 1. Components can import and exports other components. This means |
2553 | | // that arguments to instantiation are along the lines of functions |
2554 | | // being passed to functions or similar. Effectively this means that |
2555 | | // the term "variance" comes into play with either contravariance |
2556 | | // or covariance depending on where you are in typechecking. This is |
2557 | | // one of the main rationales, however, that this check below is a |
2558 | | // check for subtyping as opposed to exact type equivalence. For |
2559 | | // example an instance that exports something is a subtype of an |
2560 | | // instance that exports nothing. Components get a bit trick since |
2561 | | // they both have imports and exports. My way of thinking about it |
2562 | | // is "who's asking for what". If you're asking for imports then |
2563 | | // I need to at least supply those imports, but I can possibly |
2564 | | // supply more. If you're asking for a thing which you'll give a set |
2565 | | // of imports, then I can give you something which takes less imports |
2566 | | // because what you give still suffices. (things like that). The |
2567 | | // real complication with components, however, comes with... |
2568 | | // |
2569 | | // 2. Resources. Resources in the component model are akin to "abstract |
2570 | | // types". They're not abstract in the sense that they have no |
2571 | | // representation, they're always backed by a 32-bit integer right |
2572 | | // now. Instead they're abstract in the sense that some components |
2573 | | // aren't allowed to understand the representation of a resource. |
2574 | | // For example if you import a resource you can't get the underlying |
2575 | | // internals of it. Furthermore the resource is strictly tracked |
2576 | | // within the component with `own` and `borrow` runtime semantics. |
2577 | | // The hardest part about resources, though, is handling them as |
2578 | | // part of instantiation and subtyping. |
2579 | | // |
2580 | | // For example one major aspect of resources is that if a component |
2581 | | // exports a resource then each instantiation of the component |
2582 | | // produces a fresh resource type. This means that the type recorded |
2583 | | // for the instantiation here can't simply be "I instantiated |
2584 | | // component X" since in such a situation the type of all |
2585 | | // instantiations would be the same, which they aren't. |
2586 | | // |
2587 | | // This sort of subtelty comes up quite frequently for resources. |
2588 | | // This file contains references to `imported_resources` and |
2589 | | // `defined_resources` for example which refer to the formal |
2590 | | // nature of components and their abstract variables. Specifically |
2591 | | // for instantiation though we're eventually faced with the problem |
2592 | | // of subtype checks where resource subtyping is defined as "does |
2593 | | // your id equal mine". Naively implemented that means anything with |
2594 | | // resources isn't subtypes of anything else since resource ids are |
2595 | | // unique between components. Instead what actually needs to happen |
2596 | | // is types need to be substituted. |
2597 | | // |
2598 | | // Much of the complexity here is not actually apparent here in this |
2599 | | // literal one function. Instead it's spread out across validation |
2600 | | // in this file and type-checking in the `types.rs` module. Note that |
2601 | | // the "spread out" nature isn't because we're bad maintainers |
2602 | | // (hopefully), but rather it's quite infectious how many parts need |
2603 | | // to handle resources and account for defined/imported variables. |
2604 | | // |
2605 | | // For example only one subtyping method is called here where `args` is |
2606 | | // passed in. This method is quite recursive in its nature though and |
2607 | | // will internally touch all the fields that this file maintains to |
2608 | | // end up putting into various bits and pieces of type information. |
2609 | | // |
2610 | | // Unfortunately there's probably not really a succinct way to read |
2611 | | // this method and understand everything. If you've written ML module |
2612 | | // type systems this will probably look quite familiar, but otherwise |
2613 | | // the whole system is not really easily approachable at this time. It's |
2614 | | // hoped in the future that there's a formalism to refer to which will |
2615 | | // make things more clear as the code would be able to reference this |
2616 | | // hypothetical formalism. Until that's the case, though, these |
2617 | | // comments are hopefully enough when augmented with communication with |
2618 | | // the authors. |
2619 | | |
2620 | 19.3k | let component_type = &types[component_type_id]; |
2621 | 19.3k | let mut exports = component_type.exports.clone(); |
2622 | 19.3k | let mut info = TypeInfo::new(); |
2623 | 136k | for (_, ty) in component_type.exports.iter() { |
2624 | 136k | info.combine(ty.info(types), offset)?; |
2625 | | } |
2626 | | |
2627 | | // Perform the subtype check that `args` matches the imports of |
2628 | | // `component_type_id`. The result of this subtype check is the |
2629 | | // production of a mapping of resource types from the imports to the |
2630 | | // arguments provided. This is a substitution map which is then used |
2631 | | // below to perform a substitution into the exports of the instance |
2632 | | // since the types of the exports are now in terms of whatever was |
2633 | | // supplied as imports. |
2634 | 19.3k | let mut mapping = SubtypeCx::new(types, types).open_instance_type( |
2635 | 19.3k | &args, |
2636 | 19.3k | component_type_id, |
2637 | 19.3k | ExternKind::Import, |
2638 | 19.3k | offset, |
2639 | 19.3k | )?; |
2640 | | |
2641 | | // Part of the instantiation of a component is that all of its |
2642 | | // defined resources become "fresh" on each instantiation. This |
2643 | | // means that each instantiation of a component gets brand new type |
2644 | | // variables representing its defined resources, modeling that each |
2645 | | // instantiation produces distinct types. The freshening is performed |
2646 | | // here by allocating new ids and inserting them into `mapping`. |
2647 | | // |
2648 | | // Note that technically the `mapping` from subtyping should be applied |
2649 | | // first and then the mapping for freshening should be applied |
2650 | | // afterwards. The keys of the map from subtyping are the imported |
2651 | | // resources from this component which are disjoint from its defined |
2652 | | // resources. That means it should be possible to place everything |
2653 | | // into one large map which maps from: |
2654 | | // |
2655 | | // * the component's imported resources go to whatever was explicitly |
2656 | | // supplied in the import map |
2657 | | // * the component's defined resources go to fresh new resources |
2658 | | // |
2659 | | // These two remapping operations can then get folded into one by |
2660 | | // placing everything in the same `mapping` and using that for a remap |
2661 | | // only once. |
2662 | 19.3k | let fresh_defined_resources = (0..component_type.defined_resources.len()) |
2663 | 19.3k | .map(|_| types.alloc_resource_id().resource()) |
2664 | 19.3k | .collect::<IndexSet<_>>(); |
2665 | 19.3k | let component_type = &types[component_type_id]; |
2666 | 19.3k | for ((old, _path), new) in component_type |
2667 | 19.3k | .defined_resources |
2668 | 19.3k | .iter() |
2669 | 19.3k | .zip(&fresh_defined_resources) |
2670 | | { |
2671 | 0 | let prev = mapping.resources.insert(*old, *new); |
2672 | 0 | assert!(prev.is_none()); |
2673 | | } |
2674 | | |
2675 | | // Perform the remapping operation over all the exports that will be |
2676 | | // listed for the final instance type. Note that this is performed |
2677 | | // both for all the export types in addition to the explicitly exported |
2678 | | // resources list. |
2679 | | // |
2680 | | // Note that this is a crucial step of the instantiation process which |
2681 | | // is intentionally transforming the type of a component based on the |
2682 | | // variables provided by imports and additionally ensuring that all |
2683 | | // references to the component's defined resources are rebound to the |
2684 | | // fresh ones introduced just above. |
2685 | 136k | for entity in exports.values_mut() { |
2686 | 136k | types.remap_component_entity(entity, &mut mapping); |
2687 | 136k | } |
2688 | 19.3k | let component_type = &types[component_type_id]; |
2689 | 19.3k | let explicit_resources = component_type |
2690 | 19.3k | .explicit_resources |
2691 | 19.3k | .iter() |
2692 | 19.3k | .map(|(id, path)| { |
2693 | 5.55k | ( |
2694 | 5.55k | mapping.resources.get(id).copied().unwrap_or(*id), |
2695 | 5.55k | path.clone(), |
2696 | 5.55k | ) |
2697 | 19.3k | }) |
2698 | 19.3k | .collect::<IndexMap<_, _>>(); |
2699 | 19.3k | |
2700 | 19.3k | // Technically in the last formalism that was consulted in writing this |
2701 | 19.3k | // implementation there are two further steps that are part of the |
2702 | 19.3k | // instantiation process: |
2703 | 19.3k | // |
2704 | 19.3k | // 1. The set of defined resources from the instance created, which are |
2705 | 19.3k | // added to the outer component, is the subset of the instance's |
2706 | 19.3k | // original defined resources and the free variables of the exports. |
2707 | 19.3k | // |
2708 | 19.3k | // 2. Each element of this subset is required to be "explicit in" the |
2709 | 19.3k | // instance, or otherwise explicitly exported somewhere within the |
2710 | 19.3k | // instance. |
2711 | 19.3k | // |
2712 | 19.3k | // With the syntactic structure of the component model, however, neither |
2713 | 19.3k | // of these conditions should be necessary. The main reason for this is |
2714 | 19.3k | // that this function is specifically dealing with instantiation of |
2715 | 19.3k | // components which should already have these properties validated |
2716 | 19.3k | // about them. Subsequently we shouldn't have to re-check them. |
2717 | 19.3k | // |
2718 | 19.3k | // In debug mode, however, do a sanity check. |
2719 | 19.3k | if cfg!(debug_assertions) { |
2720 | 0 | let mut free = IndexSet::default(); |
2721 | 0 | for ty in exports.values() { |
2722 | 0 | types.free_variables_component_entity(ty, &mut free); |
2723 | 0 | } |
2724 | 0 | assert!(fresh_defined_resources.is_subset(&free)); |
2725 | 0 | for resource in fresh_defined_resources.iter() { |
2726 | 0 | assert!(explicit_resources.contains_key(resource)); |
2727 | | } |
2728 | 19.3k | } |
2729 | | |
2730 | | // And as the final step of the instantiation process all of the |
2731 | | // new defined resources from this component instantiation are moved |
2732 | | // onto `self`. Note that concrete instances never have defined |
2733 | | // resources (see more comments in `instantiate_exports`) so the |
2734 | | // `defined_resources` listing in the final type is always empty. This |
2735 | | // represents how by having a concrete instance the definitions |
2736 | | // referred to in that instance are now problems for the outer |
2737 | | // component rather than the inner instance since the instance is bound |
2738 | | // to the component. |
2739 | | // |
2740 | | // All defined resources here have no known representation, so they're |
2741 | | // all listed with `None`. Also note that none of the resources were |
2742 | | // exported yet so `self.explicit_resources` is not updated yet. If |
2743 | | // this instance is exported, however, it'll consult the type's |
2744 | | // `explicit_resources` array and use that appropriately. |
2745 | 19.3k | for resource in fresh_defined_resources { |
2746 | 0 | self.defined_resources.insert(resource, None); |
2747 | 0 | } |
2748 | | |
2749 | 19.3k | Ok(types.push_ty(ComponentInstanceType { |
2750 | 19.3k | info, |
2751 | 19.3k | defined_resources: Default::default(), |
2752 | 19.3k | explicit_resources, |
2753 | 19.3k | exports, |
2754 | 19.3k | })) |
2755 | 19.3k | } |
2756 | | |
2757 | 0 | fn instantiate_component_exports( |
2758 | 0 | &mut self, |
2759 | 0 | exports: Vec<crate::ComponentExport>, |
2760 | 0 | features: &WasmFeatures, |
2761 | 0 | types: &mut TypeAlloc, |
2762 | 0 | offset: usize, |
2763 | 0 | ) -> Result<ComponentInstanceTypeId> { |
2764 | 0 | let mut info = TypeInfo::new(); |
2765 | 0 | let mut inst_exports = IndexMap::default(); |
2766 | 0 | let mut explicit_resources = IndexMap::default(); |
2767 | 0 | let mut export_names = IndexSet::default(); |
2768 | 0 |
|
2769 | 0 | // NB: It's intentional that this context is empty since no indices are |
2770 | 0 | // introduced in the bag-of-exports construct which means there's no |
2771 | 0 | // way syntactically to register something inside of this. |
2772 | 0 | let names = ComponentNameContext::default(); |
2773 | | |
2774 | 0 | for export in exports { |
2775 | 0 | assert!(export.ty.is_none()); |
2776 | 0 | let ty = match export.kind { |
2777 | | ComponentExternalKind::Module => { |
2778 | 0 | ComponentEntityType::Module(self.module_at(export.index, offset)?) |
2779 | | } |
2780 | | ComponentExternalKind::Component => { |
2781 | 0 | ComponentEntityType::Component(self.component_at(export.index, offset)?) |
2782 | | } |
2783 | | ComponentExternalKind::Instance => { |
2784 | 0 | let ty = self.instance_at(export.index, offset)?; |
2785 | | |
2786 | | // When an instance is exported from an instance then |
2787 | | // all explicitly exported resources on the sub-instance are |
2788 | | // now also listed as exported resources on the outer |
2789 | | // instance, just with one more element in their path. |
2790 | 0 | explicit_resources.extend(types[ty].explicit_resources.iter().map( |
2791 | 0 | |(id, path)| { |
2792 | 0 | let mut new_path = vec![inst_exports.len()]; |
2793 | 0 | new_path.extend(path); |
2794 | 0 | (*id, new_path) |
2795 | 0 | }, |
2796 | 0 | )); |
2797 | 0 | ComponentEntityType::Instance(ty) |
2798 | | } |
2799 | | ComponentExternalKind::Func => { |
2800 | 0 | ComponentEntityType::Func(self.function_at(export.index, offset)?) |
2801 | | } |
2802 | | ComponentExternalKind::Value => { |
2803 | 0 | self.check_value_support(features, offset)?; |
2804 | 0 | ComponentEntityType::Value(*self.value_at(export.index, offset)?) |
2805 | | } |
2806 | | ComponentExternalKind::Type => { |
2807 | 0 | let ty = self.component_type_at(export.index, offset)?; |
2808 | | // If this is an export of a resource type be sure to |
2809 | | // record that in the explicit list with the appropriate |
2810 | | // path because if this instance ends up getting used |
2811 | | // it'll count towards the "explicit in" check. |
2812 | 0 | if let ComponentAnyTypeId::Resource(id) = ty { |
2813 | 0 | explicit_resources.insert(id.resource(), vec![inst_exports.len()]); |
2814 | 0 | } |
2815 | 0 | ComponentEntityType::Type { |
2816 | 0 | referenced: ty, |
2817 | 0 | // The created type index here isn't used anywhere |
2818 | 0 | // in index spaces because a "bag of exports" |
2819 | 0 | // doesn't build up its own index spaces. Just fill |
2820 | 0 | // in the same index here in this case as what's |
2821 | 0 | // referenced. |
2822 | 0 | created: ty, |
2823 | 0 | } |
2824 | | } |
2825 | | }; |
2826 | | |
2827 | 0 | names.validate_extern( |
2828 | 0 | export.name.0, |
2829 | 0 | ExternKind::Export, |
2830 | 0 | &ty, |
2831 | 0 | types, |
2832 | 0 | offset, |
2833 | 0 | &mut export_names, |
2834 | 0 | &mut inst_exports, |
2835 | 0 | &mut info, |
2836 | 0 | features, |
2837 | 0 | )?; |
2838 | | } |
2839 | | |
2840 | 0 | Ok(types.push_ty(ComponentInstanceType { |
2841 | 0 | info, |
2842 | 0 | explicit_resources, |
2843 | 0 | exports: inst_exports, |
2844 | 0 |
|
2845 | 0 | // NB: the list of defined resources for this instance itself |
2846 | 0 | // is always empty. Even if this instance exports resources, |
2847 | 0 | // it's empty. |
2848 | 0 | // |
2849 | 0 | // The reason for this is a bit subtle. The general idea, though, is |
2850 | 0 | // that the defined resources list here is only used for instance |
2851 | 0 | // types that are sort of "floating around" and haven't actually |
2852 | 0 | // been attached to something yet. For example when an instance type |
2853 | 0 | // is simply declared it can have defined resources introduced |
2854 | 0 | // through `(export "name" (type (sub resource)))`. These |
2855 | 0 | // definitions, however, are local to the instance itself and aren't |
2856 | 0 | // defined elsewhere. |
2857 | 0 | // |
2858 | 0 | // Here, though, no new definitions were introduced. The instance |
2859 | 0 | // created here is a "bag of exports" which could only refer to |
2860 | 0 | // preexisting items. This means that inherently no new resources |
2861 | 0 | // were created so there's nothing to put in this list. Any |
2862 | 0 | // resources referenced by the instance must be bound by the outer |
2863 | 0 | // component context or further above. |
2864 | 0 | // |
2865 | 0 | // Furthermore, however, actual instances of instances, which this |
2866 | 0 | // is, aren't allowed to have defined resources. Instead the |
2867 | 0 | // resources would have to be injected into the outer component |
2868 | 0 | // enclosing the instance. That means that even if bag-of-exports |
2869 | 0 | // could declare a new resource then the resource would be moved |
2870 | 0 | // from here to `self.defined_resources`. This doesn't exist at this |
2871 | 0 | // time, though, so this still remains empty and |
2872 | 0 | // `self.defined_resources` remains unperturbed. |
2873 | 0 | defined_resources: Default::default(), |
2874 | 0 | })) |
2875 | 0 | } |
2876 | | |
2877 | 27.0k | fn instantiate_core_exports( |
2878 | 27.0k | &mut self, |
2879 | 27.0k | exports: Vec<crate::Export>, |
2880 | 27.0k | types: &mut TypeAlloc, |
2881 | 27.0k | offset: usize, |
2882 | 27.0k | ) -> Result<ComponentCoreInstanceTypeId> { |
2883 | 87.9k | fn insert_export( |
2884 | 87.9k | types: &TypeList, |
2885 | 87.9k | name: &str, |
2886 | 87.9k | export: EntityType, |
2887 | 87.9k | exports: &mut IndexMap<String, EntityType>, |
2888 | 87.9k | info: &mut TypeInfo, |
2889 | 87.9k | offset: usize, |
2890 | 87.9k | ) -> Result<()> { |
2891 | 87.9k | info.combine(export.info(types), offset)?; |
2892 | | |
2893 | 87.9k | if exports.insert(name.to_string(), export).is_some() { |
2894 | 0 | bail!( |
2895 | 0 | offset, |
2896 | 0 | "duplicate instantiation export name `{name}` already defined", |
2897 | 0 | ) |
2898 | 87.9k | } |
2899 | 87.9k | |
2900 | 87.9k | Ok(()) |
2901 | 87.9k | } |
2902 | | |
2903 | 27.0k | let mut info = TypeInfo::new(); |
2904 | 27.0k | let mut inst_exports = IndexMap::default(); |
2905 | 115k | for export in exports { |
2906 | 87.9k | match export.kind { |
2907 | | ExternalKind::Func => { |
2908 | | insert_export( |
2909 | 84.2k | types, |
2910 | 84.2k | export.name, |
2911 | 84.2k | EntityType::Func(self.core_function_at(export.index, offset)?), |
2912 | 84.2k | &mut inst_exports, |
2913 | 84.2k | &mut info, |
2914 | 84.2k | offset, |
2915 | 0 | )?; |
2916 | | } |
2917 | | ExternalKind::Table => insert_export( |
2918 | 3.71k | types, |
2919 | 3.71k | export.name, |
2920 | 3.71k | EntityType::Table(*self.table_at(export.index, offset)?), |
2921 | 3.71k | &mut inst_exports, |
2922 | 3.71k | &mut info, |
2923 | 3.71k | offset, |
2924 | 0 | )?, |
2925 | | ExternalKind::Memory => insert_export( |
2926 | 0 | types, |
2927 | 0 | export.name, |
2928 | 0 | EntityType::Memory(*self.memory_at(export.index, offset)?), |
2929 | 0 | &mut inst_exports, |
2930 | 0 | &mut info, |
2931 | 0 | offset, |
2932 | 0 | )?, |
2933 | | ExternalKind::Global => { |
2934 | | insert_export( |
2935 | 0 | types, |
2936 | 0 | export.name, |
2937 | 0 | EntityType::Global(*self.global_at(export.index, offset)?), |
2938 | 0 | &mut inst_exports, |
2939 | 0 | &mut info, |
2940 | 0 | offset, |
2941 | 0 | )?; |
2942 | | } |
2943 | | ExternalKind::Tag => insert_export( |
2944 | 0 | types, |
2945 | 0 | export.name, |
2946 | 0 | EntityType::Tag(self.core_function_at(export.index, offset)?), |
2947 | 0 | &mut inst_exports, |
2948 | 0 | &mut info, |
2949 | 0 | offset, |
2950 | 0 | )?, |
2951 | | } |
2952 | | } |
2953 | | |
2954 | 27.0k | Ok(types.push_ty(InstanceType { |
2955 | 27.0k | info, |
2956 | 27.0k | kind: CoreInstanceTypeKind::Exports(inst_exports), |
2957 | 27.0k | })) |
2958 | 27.0k | } |
2959 | | |
2960 | 139k | fn alias_core_instance_export( |
2961 | 139k | &mut self, |
2962 | 139k | instance_index: u32, |
2963 | 139k | kind: ExternalKind, |
2964 | 139k | name: &str, |
2965 | 139k | types: &TypeList, |
2966 | 139k | offset: usize, |
2967 | 139k | ) -> Result<()> { |
2968 | | macro_rules! push_module_export { |
2969 | | ($expected:path, $collection:ident, $ty:literal) => {{ |
2970 | | match self.core_instance_export(instance_index, name, types, offset)? { |
2971 | | $expected(ty) => { |
2972 | | self.$collection.push(*ty); |
2973 | | } |
2974 | | _ => { |
2975 | | bail!( |
2976 | | offset, |
2977 | | "export `{name}` for core instance {instance_index} is not a {}", |
2978 | | $ty |
2979 | | ) |
2980 | | } |
2981 | | } |
2982 | | }}; |
2983 | | } |
2984 | | |
2985 | 139k | match kind { |
2986 | | ExternalKind::Func => { |
2987 | 123k | check_max( |
2988 | 123k | self.function_count(), |
2989 | 123k | 1, |
2990 | 123k | MAX_WASM_FUNCTIONS, |
2991 | 123k | "functions", |
2992 | 123k | offset, |
2993 | 123k | )?; |
2994 | 123k | push_module_export!(EntityType::Func, core_funcs, "function"); |
2995 | | } |
2996 | | ExternalKind::Table => { |
2997 | 3.71k | check_max( |
2998 | 3.71k | self.core_tables.len(), |
2999 | 3.71k | 1, |
3000 | 3.71k | MAX_CORE_INDEX_SPACE_ITEMS, |
3001 | 3.71k | "tables", |
3002 | 3.71k | offset, |
3003 | 3.71k | )?; |
3004 | 3.71k | push_module_export!(EntityType::Table, core_tables, "table"); |
3005 | | |
3006 | 3.71k | let ty = self.core_tables.last().unwrap(); |
3007 | 3.71k | if ty.table64 { |
3008 | 0 | bail!( |
3009 | 0 | offset, |
3010 | 0 | "64-bit tables are not compatible with components yet" |
3011 | 0 | ); |
3012 | 3.71k | } |
3013 | 3.71k | if ty.shared { |
3014 | 0 | bail!( |
3015 | 0 | offset, |
3016 | 0 | "shared tables are not compatible with components yet" |
3017 | 0 | ); |
3018 | 3.71k | } |
3019 | | } |
3020 | | ExternalKind::Memory => { |
3021 | 12.4k | check_max( |
3022 | 12.4k | self.core_memories.len(), |
3023 | 12.4k | 1, |
3024 | 12.4k | MAX_CORE_INDEX_SPACE_ITEMS, |
3025 | 12.4k | "memories", |
3026 | 12.4k | offset, |
3027 | 12.4k | )?; |
3028 | 12.4k | push_module_export!(EntityType::Memory, core_memories, "memory"); |
3029 | | |
3030 | 12.4k | let ty = self.core_memories.last().unwrap(); |
3031 | 12.4k | if ty.memory64 { |
3032 | 0 | bail!( |
3033 | 0 | offset, |
3034 | 0 | "64-bit linear memories are not compatible with components yet" |
3035 | 0 | ); |
3036 | 12.4k | } |
3037 | 12.4k | if ty.shared { |
3038 | 0 | bail!( |
3039 | 0 | offset, |
3040 | 0 | "shared linear memories are not compatible with components yet" |
3041 | 0 | ); |
3042 | 12.4k | } |
3043 | | } |
3044 | | ExternalKind::Global => { |
3045 | 0 | check_max( |
3046 | 0 | self.core_globals.len(), |
3047 | 0 | 1, |
3048 | 0 | MAX_CORE_INDEX_SPACE_ITEMS, |
3049 | 0 | "globals", |
3050 | 0 | offset, |
3051 | 0 | )?; |
3052 | 0 | push_module_export!(EntityType::Global, core_globals, "global"); |
3053 | | } |
3054 | | ExternalKind::Tag => { |
3055 | 0 | check_max( |
3056 | 0 | self.core_tags.len(), |
3057 | 0 | 1, |
3058 | 0 | MAX_CORE_INDEX_SPACE_ITEMS, |
3059 | 0 | "tags", |
3060 | 0 | offset, |
3061 | 0 | )?; |
3062 | 0 | push_module_export!(EntityType::Tag, core_tags, "tag"); |
3063 | | } |
3064 | | } |
3065 | | |
3066 | 139k | Ok(()) |
3067 | 139k | } |
3068 | | |
3069 | 50.6k | fn alias_instance_export( |
3070 | 50.6k | &mut self, |
3071 | 50.6k | instance_index: u32, |
3072 | 50.6k | kind: ComponentExternalKind, |
3073 | 50.6k | name: &str, |
3074 | 50.6k | features: &WasmFeatures, |
3075 | 50.6k | types: &mut TypeAlloc, |
3076 | 50.6k | offset: usize, |
3077 | 50.6k | ) -> Result<()> { |
3078 | 50.6k | if let ComponentExternalKind::Value = kind { |
3079 | 0 | self.check_value_support(features, offset)?; |
3080 | 50.6k | } |
3081 | 50.6k | let mut ty = match types[self.instance_at(instance_index, offset)?] |
3082 | | .exports |
3083 | 50.6k | .get(name) |
3084 | | { |
3085 | 50.6k | Some(ty) => *ty, |
3086 | 0 | None => bail!( |
3087 | 0 | offset, |
3088 | 0 | "instance {instance_index} has no export named `{name}`" |
3089 | 0 | ), |
3090 | | }; |
3091 | | |
3092 | 50.6k | let ok = match (&ty, kind) { |
3093 | 0 | (ComponentEntityType::Module(_), ComponentExternalKind::Module) => true, |
3094 | 0 | (ComponentEntityType::Module(_), _) => false, |
3095 | 0 | (ComponentEntityType::Component(_), ComponentExternalKind::Component) => true, |
3096 | 0 | (ComponentEntityType::Component(_), _) => false, |
3097 | 25.4k | (ComponentEntityType::Func(_), ComponentExternalKind::Func) => true, |
3098 | 0 | (ComponentEntityType::Func(_), _) => false, |
3099 | 0 | (ComponentEntityType::Instance(_), ComponentExternalKind::Instance) => true, |
3100 | 0 | (ComponentEntityType::Instance(_), _) => false, |
3101 | 0 | (ComponentEntityType::Value(_), ComponentExternalKind::Value) => true, |
3102 | 0 | (ComponentEntityType::Value(_), _) => false, |
3103 | 25.2k | (ComponentEntityType::Type { .. }, ComponentExternalKind::Type) => true, |
3104 | 0 | (ComponentEntityType::Type { .. }, _) => false, |
3105 | | }; |
3106 | 50.6k | if !ok { |
3107 | 0 | bail!( |
3108 | 0 | offset, |
3109 | 0 | "export `{name}` for instance {instance_index} is not a {}", |
3110 | 0 | kind.desc(), |
3111 | 0 | ); |
3112 | 50.6k | } |
3113 | 50.6k | |
3114 | 50.6k | self.add_entity(&mut ty, None, features, types, offset)?; |
3115 | 50.6k | Ok(()) |
3116 | 50.6k | } |
3117 | | |
3118 | 0 | fn alias_module(components: &mut [Self], count: u32, index: u32, offset: usize) -> Result<()> { |
3119 | 0 | let component = Self::check_alias_count(components, count, offset)?; |
3120 | 0 | let ty = component.module_at(index, offset)?; |
3121 | | |
3122 | 0 | let current = components.last_mut().unwrap(); |
3123 | 0 | check_max( |
3124 | 0 | current.core_modules.len(), |
3125 | 0 | 1, |
3126 | 0 | MAX_WASM_MODULES, |
3127 | 0 | "modules", |
3128 | 0 | offset, |
3129 | 0 | )?; |
3130 | | |
3131 | 0 | current.core_modules.push(ty); |
3132 | 0 | Ok(()) |
3133 | 0 | } |
3134 | | |
3135 | 0 | fn alias_component( |
3136 | 0 | components: &mut [Self], |
3137 | 0 | count: u32, |
3138 | 0 | index: u32, |
3139 | 0 | offset: usize, |
3140 | 0 | ) -> Result<()> { |
3141 | 0 | let component = Self::check_alias_count(components, count, offset)?; |
3142 | 0 | let ty = component.component_at(index, offset)?; |
3143 | | |
3144 | 0 | let current = components.last_mut().unwrap(); |
3145 | 0 | check_max( |
3146 | 0 | current.components.len(), |
3147 | 0 | 1, |
3148 | 0 | MAX_WASM_COMPONENTS, |
3149 | 0 | "components", |
3150 | 0 | offset, |
3151 | 0 | )?; |
3152 | | |
3153 | 0 | current.components.push(ty); |
3154 | 0 | Ok(()) |
3155 | 0 | } |
3156 | | |
3157 | 0 | fn alias_core_type( |
3158 | 0 | components: &mut [Self], |
3159 | 0 | count: u32, |
3160 | 0 | index: u32, |
3161 | 0 | offset: usize, |
3162 | 0 | ) -> Result<()> { |
3163 | 0 | let component = Self::check_alias_count(components, count, offset)?; |
3164 | 0 | let ty = component.core_type_at(index, offset)?; |
3165 | | |
3166 | 0 | let current = components.last_mut().unwrap(); |
3167 | 0 | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; |
3168 | | |
3169 | 0 | current.core_types.push(ty); |
3170 | 0 |
|
3171 | 0 | Ok(()) |
3172 | 0 | } |
3173 | | |
3174 | 18.5k | fn alias_type( |
3175 | 18.5k | components: &mut [Self], |
3176 | 18.5k | count: u32, |
3177 | 18.5k | index: u32, |
3178 | 18.5k | types: &mut TypeAlloc, |
3179 | 18.5k | offset: usize, |
3180 | 18.5k | ) -> Result<()> { |
3181 | 18.5k | let component = Self::check_alias_count(components, count, offset)?; |
3182 | 18.5k | let ty = component.component_type_at(index, offset)?; |
3183 | | |
3184 | | // If `count` "crossed a component boundary", meaning that it went from |
3185 | | // one component to another, then this must additionally verify that |
3186 | | // `ty` has no free variables with respect to resources. This is |
3187 | | // intended to preserve the property for components where each component |
3188 | | // is an isolated unit that can theoretically be extracted from other |
3189 | | // components. If resources from other components were allowed to leak |
3190 | | // in then it would prevent that. |
3191 | | // |
3192 | | // This check is done by calculating the `pos` within `components` that |
3193 | | // our target `component` above was selected at. Once this is acquired |
3194 | | // the component to the "right" is checked, and if that's a component |
3195 | | // then it's considered as crossing a component boundary meaning the |
3196 | | // free variables check runs. |
3197 | | // |
3198 | | // The reason this works is that in the list of `ComponentState` types |
3199 | | // it's guaranteed that any `is_type` components are contiguous at the |
3200 | | // end of the array. This means that if state one level deeper than the |
3201 | | // target of this alias is a `!is_type` component, then the target must |
3202 | | // be a component as well. If the one-level deeper state `is_type` then |
3203 | | // the target is either a type or a component, both of which are valid |
3204 | | // (as aliases can reach the enclosing component and have as many free |
3205 | | // variables as they want). |
3206 | 18.5k | let pos_after_component = components.len() - (count as usize); |
3207 | 18.5k | if let Some(component) = components.get(pos_after_component) { |
3208 | 18.5k | if component.kind == ComponentKind::Component { |
3209 | 0 | let mut free = IndexSet::default(); |
3210 | 0 | types.free_variables_any_type_id(ty, &mut free); |
3211 | 0 | if !free.is_empty() { |
3212 | 0 | bail!( |
3213 | 0 | offset, |
3214 | 0 | "cannot alias outer type which transitively refers \ |
3215 | 0 | to resources not defined in the current component" |
3216 | 0 | ); |
3217 | 0 | } |
3218 | 18.5k | } |
3219 | 0 | } |
3220 | | |
3221 | 18.5k | let current = components.last_mut().unwrap(); |
3222 | 18.5k | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; |
3223 | | |
3224 | 18.5k | current.types.push(ty); |
3225 | 18.5k | |
3226 | 18.5k | Ok(()) |
3227 | 18.5k | } |
3228 | | |
3229 | 18.5k | fn check_alias_count(components: &[Self], count: u32, offset: usize) -> Result<&Self> { |
3230 | 18.5k | let count = count as usize; |
3231 | 18.5k | if count >= components.len() { |
3232 | 0 | bail!(offset, "invalid outer alias count of {count}"); |
3233 | 18.5k | } |
3234 | 18.5k | |
3235 | 18.5k | Ok(&components[components.len() - count - 1]) |
3236 | 18.5k | } |
3237 | | |
3238 | 828k | fn create_defined_type( |
3239 | 828k | &self, |
3240 | 828k | ty: crate::ComponentDefinedType, |
3241 | 828k | types: &TypeList, |
3242 | 828k | features: &WasmFeatures, |
3243 | 828k | offset: usize, |
3244 | 828k | ) -> Result<ComponentDefinedType> { |
3245 | 828k | match ty { |
3246 | 12.4k | crate::ComponentDefinedType::Primitive(ty) => Ok(ComponentDefinedType::Primitive(ty)), |
3247 | 19.1k | crate::ComponentDefinedType::Record(fields) => { |
3248 | 19.1k | self.create_record_type(fields.as_ref(), types, offset) |
3249 | | } |
3250 | 11.7k | crate::ComponentDefinedType::Variant(cases) => { |
3251 | 11.7k | self.create_variant_type(cases.as_ref(), types, offset) |
3252 | | } |
3253 | 18.1k | crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List( |
3254 | 18.1k | self.create_component_val_type(ty, offset)?, |
3255 | | )), |
3256 | 195k | crate::ComponentDefinedType::Tuple(tys) => { |
3257 | 195k | self.create_tuple_type(tys.as_ref(), types, offset) |
3258 | | } |
3259 | 5.78k | crate::ComponentDefinedType::Flags(names) => { |
3260 | 5.78k | self.create_flags_type(names.as_ref(), features, offset) |
3261 | | } |
3262 | 301k | crate::ComponentDefinedType::Enum(cases) => { |
3263 | 301k | self.create_enum_type(cases.as_ref(), offset) |
3264 | | } |
3265 | 87.2k | crate::ComponentDefinedType::Option(ty) => Ok(ComponentDefinedType::Option( |
3266 | 87.2k | self.create_component_val_type(ty, offset)?, |
3267 | | )), |
3268 | 22.8k | crate::ComponentDefinedType::Result { ok, err } => Ok(ComponentDefinedType::Result { |
3269 | 22.8k | ok: ok |
3270 | 22.8k | .map(|ty| self.create_component_val_type(ty, offset)) |
3271 | 22.8k | .transpose()?, |
3272 | 22.8k | err: err |
3273 | 22.8k | .map(|ty| self.create_component_val_type(ty, offset)) |
3274 | 22.8k | .transpose()?, |
3275 | | }), |
3276 | 9.17k | crate::ComponentDefinedType::Own(idx) => Ok(ComponentDefinedType::Own( |
3277 | 9.17k | self.resource_at(idx, types, offset)?, |
3278 | | )), |
3279 | 22.3k | crate::ComponentDefinedType::Borrow(idx) => Ok(ComponentDefinedType::Borrow( |
3280 | 22.3k | self.resource_at(idx, types, offset)?, |
3281 | | )), |
3282 | 64.0k | crate::ComponentDefinedType::Future(ty) => Ok(ComponentDefinedType::Future( |
3283 | 64.0k | ty.map(|ty| self.create_component_val_type(ty, offset)) |
3284 | 64.0k | .transpose()?, |
3285 | | )), |
3286 | 24.7k | crate::ComponentDefinedType::Stream(ty) => Ok(ComponentDefinedType::Stream( |
3287 | 24.7k | self.create_component_val_type(ty, offset)?, |
3288 | | )), |
3289 | 33.8k | crate::ComponentDefinedType::ErrorContext => Ok(ComponentDefinedType::ErrorContext), |
3290 | | } |
3291 | 828k | } |
3292 | | |
3293 | 19.1k | fn create_record_type( |
3294 | 19.1k | &self, |
3295 | 19.1k | fields: &[(&str, crate::ComponentValType)], |
3296 | 19.1k | types: &TypeList, |
3297 | 19.1k | offset: usize, |
3298 | 19.1k | ) -> Result<ComponentDefinedType> { |
3299 | 19.1k | let mut info = TypeInfo::new(); |
3300 | 19.1k | let mut field_map = IndexMap::default(); |
3301 | 19.1k | field_map.reserve(fields.len()); |
3302 | 19.1k | |
3303 | 19.1k | if fields.is_empty() { |
3304 | 0 | bail!(offset, "record type must have at least one field"); |
3305 | 19.1k | } |
3306 | | |
3307 | 87.8k | for (name, ty) in fields { |
3308 | 68.6k | let name = to_kebab_str(name, "record field", offset)?; |
3309 | 68.6k | let ty = self.create_component_val_type(*ty, offset)?; |
3310 | | |
3311 | 68.6k | match field_map.entry(name.to_owned()) { |
3312 | 0 | Entry::Occupied(e) => bail!( |
3313 | 0 | offset, |
3314 | 0 | "record field name `{name}` conflicts with previous field name `{prev}`", |
3315 | 0 | prev = e.key() |
3316 | 0 | ), |
3317 | 68.6k | Entry::Vacant(e) => { |
3318 | 68.6k | info.combine(ty.info(types), offset)?; |
3319 | 68.6k | e.insert(ty); |
3320 | | } |
3321 | | } |
3322 | | } |
3323 | | |
3324 | 19.1k | Ok(ComponentDefinedType::Record(RecordType { |
3325 | 19.1k | info, |
3326 | 19.1k | fields: field_map, |
3327 | 19.1k | })) |
3328 | 19.1k | } |
3329 | | |
3330 | 11.7k | fn create_variant_type( |
3331 | 11.7k | &self, |
3332 | 11.7k | cases: &[crate::VariantCase], |
3333 | 11.7k | types: &TypeList, |
3334 | 11.7k | offset: usize, |
3335 | 11.7k | ) -> Result<ComponentDefinedType> { |
3336 | 11.7k | let mut info = TypeInfo::new(); |
3337 | 11.7k | let mut case_map: IndexMap<KebabString, VariantCase> = IndexMap::default(); |
3338 | 11.7k | case_map.reserve(cases.len()); |
3339 | 11.7k | |
3340 | 11.7k | if cases.is_empty() { |
3341 | 0 | bail!(offset, "variant type must have at least one case"); |
3342 | 11.7k | } |
3343 | 11.7k | |
3344 | 11.7k | if cases.len() > u32::MAX as usize { |
3345 | 0 | return Err(BinaryReaderError::new( |
3346 | 0 | "variant type cannot be represented with a 32-bit discriminant value", |
3347 | 0 | offset, |
3348 | 0 | )); |
3349 | 11.7k | } |
3350 | | |
3351 | 41.2k | for (i, case) in cases.iter().enumerate() { |
3352 | 41.2k | if let Some(refines) = case.refines { |
3353 | 0 | if refines >= i as u32 { |
3354 | 0 | return Err(BinaryReaderError::new( |
3355 | 0 | "variant case can only refine a previously defined case", |
3356 | 0 | offset, |
3357 | 0 | )); |
3358 | 0 | } |
3359 | 41.2k | } |
3360 | | |
3361 | 41.2k | let name = to_kebab_str(case.name, "variant case", offset)?; |
3362 | | |
3363 | 41.2k | let ty = case |
3364 | 41.2k | .ty |
3365 | 41.2k | .map(|ty| self.create_component_val_type(ty, offset)) |
3366 | 41.2k | .transpose()?; |
3367 | | |
3368 | 41.2k | match case_map.entry(name.to_owned()) { |
3369 | 0 | Entry::Occupied(e) => bail!( |
3370 | 0 | offset, |
3371 | 0 | "variant case name `{name}` conflicts with previous case name `{prev}`", |
3372 | 0 | name = case.name, |
3373 | 0 | prev = e.key() |
3374 | 0 | ), |
3375 | 41.2k | Entry::Vacant(e) => { |
3376 | 41.2k | if let Some(ty) = ty { |
3377 | 35.6k | info.combine(ty.info(types), offset)?; |
3378 | 5.67k | } |
3379 | | |
3380 | | // Safety: the use of `KebabStr::new_unchecked` here is safe because the string |
3381 | | // was already verified to be kebab case. |
3382 | 41.2k | e.insert(VariantCase { |
3383 | 41.2k | ty, |
3384 | 41.2k | refines: case |
3385 | 41.2k | .refines |
3386 | 41.2k | .map(|i| KebabStr::new_unchecked(cases[i as usize].name).to_owned()), |
3387 | 41.2k | }); |
3388 | 41.2k | } |
3389 | | } |
3390 | | } |
3391 | | |
3392 | 11.7k | Ok(ComponentDefinedType::Variant(VariantType { |
3393 | 11.7k | info, |
3394 | 11.7k | cases: case_map, |
3395 | 11.7k | })) |
3396 | 11.7k | } |
3397 | | |
3398 | 195k | fn create_tuple_type( |
3399 | 195k | &self, |
3400 | 195k | tys: &[crate::ComponentValType], |
3401 | 195k | types: &TypeList, |
3402 | 195k | offset: usize, |
3403 | 195k | ) -> Result<ComponentDefinedType> { |
3404 | 195k | let mut info = TypeInfo::new(); |
3405 | 195k | if tys.is_empty() { |
3406 | 0 | bail!(offset, "tuple type must have at least one type"); |
3407 | 195k | } |
3408 | 195k | let types = tys |
3409 | 195k | .iter() |
3410 | 732k | .map(|ty| { |
3411 | 732k | let ty = self.create_component_val_type(*ty, offset)?; |
3412 | 732k | info.combine(ty.info(types), offset)?; |
3413 | 732k | Ok(ty) |
3414 | 732k | }) |
3415 | 195k | .collect::<Result<_>>()?; |
3416 | | |
3417 | 195k | Ok(ComponentDefinedType::Tuple(TupleType { info, types })) |
3418 | 195k | } |
3419 | | |
3420 | 5.78k | fn create_flags_type( |
3421 | 5.78k | &self, |
3422 | 5.78k | names: &[&str], |
3423 | 5.78k | features: &WasmFeatures, |
3424 | 5.78k | offset: usize, |
3425 | 5.78k | ) -> Result<ComponentDefinedType> { |
3426 | 5.78k | let mut names_set = IndexSet::default(); |
3427 | 5.78k | names_set.reserve(names.len()); |
3428 | 5.78k | |
3429 | 5.78k | if names.is_empty() { |
3430 | 0 | bail!(offset, "flags must have at least one entry"); |
3431 | 5.78k | } |
3432 | 5.78k | |
3433 | 5.78k | if names.len() > 32 && !features.component_model_more_flags() { |
3434 | 0 | bail!( |
3435 | 0 | offset, |
3436 | 0 | "cannot have more than 32 flags; this was previously \ |
3437 | 0 | accepted and if this is required for your project please \ |
3438 | 0 | leave a comment on \ |
3439 | 0 | https://github.com/WebAssembly/component-model/issues/370" |
3440 | 0 | ); |
3441 | 5.78k | } |
3442 | | |
3443 | 24.6k | for name in names { |
3444 | 18.8k | let name = to_kebab_str(name, "flag", offset)?; |
3445 | 18.8k | if !names_set.insert(name.to_owned()) { |
3446 | 0 | bail!( |
3447 | 0 | offset, |
3448 | 0 | "flag name `{name}` conflicts with previous flag name `{prev}`", |
3449 | 0 | prev = names_set.get(name).unwrap() |
3450 | 0 | ); |
3451 | 18.8k | } |
3452 | | } |
3453 | | |
3454 | 5.78k | Ok(ComponentDefinedType::Flags(names_set)) |
3455 | 5.78k | } |
3456 | | |
3457 | 301k | fn create_enum_type(&self, cases: &[&str], offset: usize) -> Result<ComponentDefinedType> { |
3458 | 301k | if cases.len() > u32::MAX as usize { |
3459 | 0 | return Err(BinaryReaderError::new( |
3460 | 0 | "enumeration type cannot be represented with a 32-bit discriminant value", |
3461 | 0 | offset, |
3462 | 0 | )); |
3463 | 301k | } |
3464 | 301k | |
3465 | 301k | if cases.is_empty() { |
3466 | 0 | bail!(offset, "enum type must have at least one variant"); |
3467 | 301k | } |
3468 | 301k | |
3469 | 301k | let mut tags = IndexSet::default(); |
3470 | 301k | tags.reserve(cases.len()); |
3471 | | |
3472 | 1.37M | for tag in cases { |
3473 | 1.07M | let tag = to_kebab_str(tag, "enum tag", offset)?; |
3474 | 1.07M | if !tags.insert(tag.to_owned()) { |
3475 | 0 | bail!( |
3476 | 0 | offset, |
3477 | 0 | "enum tag name `{tag}` conflicts with previous tag name `{prev}`", |
3478 | 0 | prev = tags.get(tag).unwrap() |
3479 | 0 | ); |
3480 | 1.07M | } |
3481 | | } |
3482 | | |
3483 | 301k | Ok(ComponentDefinedType::Enum(tags)) |
3484 | 301k | } |
3485 | | |
3486 | 1.73M | fn create_component_val_type( |
3487 | 1.73M | &self, |
3488 | 1.73M | ty: crate::ComponentValType, |
3489 | 1.73M | offset: usize, |
3490 | 1.73M | ) -> Result<ComponentValType> { |
3491 | 1.73M | Ok(match ty { |
3492 | 1.00M | crate::ComponentValType::Primitive(pt) => ComponentValType::Primitive(pt), |
3493 | 733k | crate::ComponentValType::Type(idx) => { |
3494 | 733k | ComponentValType::Type(self.defined_type_at(idx, offset)?) |
3495 | | } |
3496 | | }) |
3497 | 1.73M | } |
3498 | | |
3499 | 0 | pub fn core_type_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreTypeId> { |
3500 | 0 | self.core_types |
3501 | 0 | .get(idx as usize) |
3502 | 0 | .copied() |
3503 | 0 | .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) |
3504 | 0 | } |
3505 | | |
3506 | 1.73M | pub fn component_type_at(&self, idx: u32, offset: usize) -> Result<ComponentAnyTypeId> { |
3507 | 1.73M | self.types |
3508 | 1.73M | .get(idx as usize) |
3509 | 1.73M | .copied() |
3510 | 1.73M | .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) |
3511 | 1.73M | } |
3512 | | |
3513 | 42.3k | fn function_type_at<'a>( |
3514 | 42.3k | &self, |
3515 | 42.3k | idx: u32, |
3516 | 42.3k | types: &'a TypeList, |
3517 | 42.3k | offset: usize, |
3518 | 42.3k | ) -> Result<&'a ComponentFuncType> { |
3519 | 42.3k | let id = self.component_type_at(idx, offset)?; |
3520 | 42.3k | match id { |
3521 | 42.3k | ComponentAnyTypeId::Func(id) => Ok(&types[id]), |
3522 | 0 | _ => bail!(offset, "type index {idx} is not a function type"), |
3523 | | } |
3524 | 42.3k | } |
3525 | | |
3526 | 118k | fn function_at(&self, idx: u32, offset: usize) -> Result<ComponentFuncTypeId> { |
3527 | 118k | self.funcs.get(idx as usize).copied().ok_or_else(|| { |
3528 | 0 | format_err!( |
3529 | 0 | offset, |
3530 | 0 | "unknown function {idx}: function index out of bounds" |
3531 | 0 | ) |
3532 | 118k | }) |
3533 | 118k | } |
3534 | | |
3535 | 19.3k | fn component_at(&self, idx: u32, offset: usize) -> Result<ComponentTypeId> { |
3536 | 19.3k | self.components.get(idx as usize).copied().ok_or_else(|| { |
3537 | 0 | format_err!( |
3538 | 0 | offset, |
3539 | 0 | "unknown component {idx}: component index out of bounds" |
3540 | 0 | ) |
3541 | 19.3k | }) |
3542 | 19.3k | } |
3543 | | |
3544 | 69.9k | fn instance_at(&self, idx: u32, offset: usize) -> Result<ComponentInstanceTypeId> { |
3545 | 69.9k | self.instances.get(idx as usize).copied().ok_or_else(|| { |
3546 | 0 | format_err!( |
3547 | 0 | offset, |
3548 | 0 | "unknown instance {idx}: instance index out of bounds" |
3549 | 0 | ) |
3550 | 69.9k | }) |
3551 | 69.9k | } |
3552 | | |
3553 | 0 | fn value_at(&mut self, idx: u32, offset: usize) -> Result<&ComponentValType> { |
3554 | 0 | match self.values.get_mut(idx as usize) { |
3555 | 0 | Some((ty, used)) if !*used => { |
3556 | 0 | *used = true; |
3557 | 0 | Ok(ty) |
3558 | | } |
3559 | 0 | Some(_) => bail!(offset, "value {idx} cannot be used more than once"), |
3560 | 0 | None => bail!(offset, "unknown value {idx}: value index out of bounds"), |
3561 | | } |
3562 | 0 | } |
3563 | | |
3564 | 733k | fn defined_type_at(&self, idx: u32, offset: usize) -> Result<ComponentDefinedTypeId> { |
3565 | 733k | match self.component_type_at(idx, offset)? { |
3566 | 733k | ComponentAnyTypeId::Defined(id) => Ok(id), |
3567 | 0 | _ => bail!(offset, "type index {idx} is not a defined type"), |
3568 | | } |
3569 | 733k | } |
3570 | | |
3571 | 174k | fn core_function_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> { |
3572 | 174k | match self.core_funcs.get(idx as usize) { |
3573 | 174k | Some(id) => Ok(*id), |
3574 | 0 | None => bail!( |
3575 | 0 | offset, |
3576 | 0 | "unknown core function {idx}: function index out of bounds" |
3577 | 0 | ), |
3578 | | } |
3579 | 174k | } |
3580 | | |
3581 | 32.3k | fn module_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreModuleTypeId> { |
3582 | 32.3k | match self.core_modules.get(idx as usize) { |
3583 | 32.3k | Some(id) => Ok(*id), |
3584 | 0 | None => bail!(offset, "unknown module {idx}: module index out of bounds"), |
3585 | | } |
3586 | 32.3k | } |
3587 | | |
3588 | 166k | fn core_instance_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreInstanceTypeId> { |
3589 | 166k | match self.core_instances.get(idx as usize) { |
3590 | 166k | Some(id) => Ok(*id), |
3591 | 0 | None => bail!( |
3592 | 0 | offset, |
3593 | 0 | "unknown core instance {idx}: instance index out of bounds" |
3594 | 0 | ), |
3595 | | } |
3596 | 166k | } |
3597 | | |
3598 | 139k | fn core_instance_export<'a>( |
3599 | 139k | &self, |
3600 | 139k | instance_index: u32, |
3601 | 139k | name: &str, |
3602 | 139k | types: &'a TypeList, |
3603 | 139k | offset: usize, |
3604 | 139k | ) -> Result<&'a EntityType> { |
3605 | 139k | match types[self.core_instance_at(instance_index, offset)?] |
3606 | 139k | .internal_exports(types) |
3607 | 139k | .get(name) |
3608 | | { |
3609 | 139k | Some(export) => Ok(export), |
3610 | 0 | None => bail!( |
3611 | 0 | offset, |
3612 | 0 | "core instance {instance_index} has no export named `{name}`" |
3613 | 0 | ), |
3614 | | } |
3615 | 139k | } |
3616 | | |
3617 | 0 | fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> { |
3618 | 0 | match self.core_globals.get(idx as usize) { |
3619 | 0 | Some(t) => Ok(t), |
3620 | 0 | None => bail!(offset, "unknown global {idx}: global index out of bounds"), |
3621 | | } |
3622 | 0 | } |
3623 | | |
3624 | 3.71k | fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> { |
3625 | 3.71k | match self.core_tables.get(idx as usize) { |
3626 | 3.71k | Some(t) => Ok(t), |
3627 | 0 | None => bail!(offset, "unknown table {idx}: table index out of bounds"), |
3628 | | } |
3629 | 3.71k | } |
3630 | | |
3631 | 12.7k | fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> { |
3632 | 12.7k | match self.core_memories.get(idx as usize) { |
3633 | 12.7k | Some(t) => Ok(t), |
3634 | 0 | None => bail!(offset, "unknown memory {idx}: memory index out of bounds"), |
3635 | | } |
3636 | 12.7k | } |
3637 | | |
3638 | | /// Completes the translation of this component, performing final |
3639 | | /// validation of its structure. |
3640 | | /// |
3641 | | /// This method is required to be called for translating all components. |
3642 | | /// Internally this will convert local data structures into a |
3643 | | /// `ComponentType` which is suitable to use to describe the type of this |
3644 | | /// component. |
3645 | 126k | pub fn finish(&mut self, types: &TypeAlloc, offset: usize) -> Result<ComponentType> { |
3646 | 126k | let mut ty = ComponentType { |
3647 | 126k | // Inherit some fields based on translation of the component. |
3648 | 126k | info: self.type_info, |
3649 | 126k | imports: self.imports.clone(), |
3650 | 126k | exports: self.exports.clone(), |
3651 | 126k | |
3652 | 126k | // This is filled in as a subset of `self.defined_resources` |
3653 | 126k | // depending on what's actually used by the exports. See the |
3654 | 126k | // bottom of this function. |
3655 | 126k | defined_resources: Default::default(), |
3656 | 126k | |
3657 | 126k | // These are inherited directly from what was calculated for this |
3658 | 126k | // component. |
3659 | 126k | imported_resources: mem::take(&mut self.imported_resources) |
3660 | 126k | .into_iter() |
3661 | 126k | .collect(), |
3662 | 126k | explicit_resources: mem::take(&mut self.explicit_resources), |
3663 | 126k | }; |
3664 | 126k | |
3665 | 126k | // Collect all "free variables", or resources, from the imports of this |
3666 | 126k | // component. None of the resources defined within this component can |
3667 | 126k | // be used as part of the exports. This set is then used to reject any |
3668 | 126k | // of `self.defined_resources` which show up. |
3669 | 126k | let mut free = IndexSet::default(); |
3670 | 150k | for ty in ty.imports.values() { |
3671 | 150k | types.free_variables_component_entity(ty, &mut free); |
3672 | 150k | } |
3673 | 126k | for (resource, _path) in self.defined_resources.iter() { |
3674 | | // FIXME: this error message is quite opaque and doesn't indicate |
3675 | | // more contextual information such as: |
3676 | | // |
3677 | | // * what was the exported resource found in the imports |
3678 | | // * which import was the resource found within |
3679 | | // |
3680 | | // These are possible to calculate here if necessary, however. |
3681 | 17.5k | if free.contains(resource) { |
3682 | 0 | bail!(offset, "local resource type found in imports"); |
3683 | 17.5k | } |
3684 | | } |
3685 | | |
3686 | | // The next step in validation a component, with respect to resources, |
3687 | | // is to minimize the set of defined resources to only those that |
3688 | | // are actually used by the exports. This weeds out resources that are |
3689 | | // defined, used within a component, and never exported, for example. |
3690 | | // |
3691 | | // The free variables of all exports are inserted into the `free` set |
3692 | | // (which is reused from the imports after clearing it). The defined |
3693 | | // resources calculated for this component are then inserted into this |
3694 | | // type's list of defined resources if it's contained somewhere in |
3695 | | // the free variables. |
3696 | | // |
3697 | | // Note that at the same time all defined resources must be exported, |
3698 | | // somehow, transitively from this component. The `explicit_resources` |
3699 | | // map is consulted for this purpose which lists all explicitly |
3700 | | // exported resources in the component, regardless from whence they |
3701 | | // came. If not present in this map then it's not exported and an error |
3702 | | // is returned. |
3703 | | // |
3704 | | // NB: the "types are exported" check is probably sufficient nowadays |
3705 | | // that the check of the `explicit_resources` map is probably not |
3706 | | // necessary, but it's left here for completeness and out of an |
3707 | | // abundance of caution. |
3708 | 126k | free.clear(); |
3709 | 302k | for ty in ty.exports.values() { |
3710 | 302k | types.free_variables_component_entity(ty, &mut free); |
3711 | 302k | } |
3712 | 126k | for (id, _rep) in mem::take(&mut self.defined_resources) { |
3713 | 17.5k | if !free.contains(&id) { |
3714 | 0 | continue; |
3715 | 17.5k | } |
3716 | | |
3717 | 17.5k | let path = match ty.explicit_resources.get(&id).cloned() { |
3718 | 17.5k | Some(path) => path, |
3719 | | // FIXME: this error message is quite opaque and doesn't |
3720 | | // indicate more contextual information such as: |
3721 | | // |
3722 | | // * which resource wasn't found in an export |
3723 | | // * which export has a reference to the resource |
3724 | | // |
3725 | | // These are possible to calculate here if necessary, however. |
3726 | 0 | None => bail!( |
3727 | 0 | offset, |
3728 | 0 | "local resource type found in export but not exported itself" |
3729 | 0 | ), |
3730 | | }; |
3731 | | |
3732 | 17.5k | ty.defined_resources.push((id, path)); |
3733 | | } |
3734 | | |
3735 | 126k | Ok(ty) |
3736 | 126k | } |
3737 | | |
3738 | 0 | fn check_value_support(&self, features: &WasmFeatures, offset: usize) -> Result<()> { |
3739 | 0 | if !features.component_model_values() { |
3740 | 0 | bail!( |
3741 | 0 | offset, |
3742 | 0 | "support for component model `value`s is not enabled" |
3743 | 0 | ); |
3744 | 0 | } |
3745 | 0 | Ok(()) |
3746 | 0 | } |
3747 | | } |
3748 | | |
3749 | | impl InternRecGroup for ComponentState { |
3750 | 0 | fn add_type_id(&mut self, id: CoreTypeId) { |
3751 | 0 | self.core_types.push(ComponentCoreTypeId::Sub(id)); |
3752 | 0 | } |
3753 | | |
3754 | 0 | fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> { |
3755 | 0 | match self.core_type_at(idx, offset)? { |
3756 | 0 | ComponentCoreTypeId::Sub(id) => Ok(id), |
3757 | | ComponentCoreTypeId::Module(_) => { |
3758 | 0 | bail!(offset, "type index {idx} is a module type, not a sub type"); |
3759 | | } |
3760 | | } |
3761 | 0 | } |
3762 | | |
3763 | 0 | fn types_len(&self) -> u32 { |
3764 | 0 | u32::try_from(self.core_types.len()).unwrap() |
3765 | 0 | } |
3766 | | } |
3767 | | |
3768 | | impl ComponentNameContext { |
3769 | | /// Registers that the resource `id` is named `name` within this context. |
3770 | 46.8k | fn register(&mut self, name: &str, id: AliasableResourceId) { |
3771 | 46.8k | let idx = self.all_resource_names.len(); |
3772 | 46.8k | let prev = self.resource_name_map.insert(id, idx); |
3773 | 46.8k | assert!( |
3774 | 46.8k | prev.is_none(), |
3775 | 0 | "for {id:?}, inserted {idx:?} but already had {prev:?}" |
3776 | | ); |
3777 | 46.8k | self.all_resource_names.insert(name.to_string()); |
3778 | 46.8k | } |
3779 | | |
3780 | 925k | fn validate_extern( |
3781 | 925k | &self, |
3782 | 925k | name: &str, |
3783 | 925k | kind: ExternKind, |
3784 | 925k | ty: &ComponentEntityType, |
3785 | 925k | types: &TypeAlloc, |
3786 | 925k | offset: usize, |
3787 | 925k | kind_names: &mut IndexSet<ComponentName>, |
3788 | 925k | items: &mut IndexMap<String, ComponentEntityType>, |
3789 | 925k | info: &mut TypeInfo, |
3790 | 925k | features: &WasmFeatures, |
3791 | 925k | ) -> Result<()> { |
3792 | | // First validate that `name` is even a valid kebab name, meaning it's |
3793 | | // in kebab-case, is an ID, etc. |
3794 | 925k | let kebab = ComponentName::new_with_features(name, offset, *features) |
3795 | 925k | .with_context(|| format!("{} name `{name}` is not a valid extern name", kind.desc()))?; |
3796 | | |
3797 | 925k | if let ExternKind::Export = kind { |
3798 | 775k | match kebab.kind() { |
3799 | | ComponentNameKind::Label(_) |
3800 | | | ComponentNameKind::Method(_) |
3801 | | | ComponentNameKind::Static(_) |
3802 | | | ComponentNameKind::Constructor(_) |
3803 | 775k | | ComponentNameKind::Interface(_) => {} |
3804 | | |
3805 | | ComponentNameKind::Hash(_) |
3806 | | | ComponentNameKind::Url(_) |
3807 | | | ComponentNameKind::Dependency(_) => { |
3808 | 0 | bail!(offset, "name `{name}` is not a valid export name") |
3809 | | } |
3810 | | } |
3811 | 150k | } |
3812 | | |
3813 | | // Validate that the kebab name, if it has structure such as |
3814 | | // `[method]a.b`, is indeed valid with respect to known resources. |
3815 | 925k | self.validate(&kebab, ty, types, offset) |
3816 | 925k | .with_context(|| format!("{} name `{kebab}` is not valid", kind.desc()))?; |
3817 | | |
3818 | | // Top-level kebab-names must all be unique, even between both imports |
3819 | | // and exports ot a component. For those names consult the `kebab_names` |
3820 | | // set. |
3821 | 925k | if let Some(prev) = kind_names.replace(kebab.clone()) { |
3822 | 0 | bail!( |
3823 | 0 | offset, |
3824 | 0 | "{} name `{kebab}` conflicts with previous name `{prev}`", |
3825 | 0 | kind.desc() |
3826 | 0 | ); |
3827 | 925k | } |
3828 | 925k | |
3829 | 925k | // Otherwise all strings must be unique, regardless of their name, so |
3830 | 925k | // consult the `items` set to ensure that we're not for example |
3831 | 925k | // importing the same interface ID twice. |
3832 | 925k | match items.entry(name.to_string()) { |
3833 | 0 | Entry::Occupied(e) => { |
3834 | 0 | bail!( |
3835 | 0 | offset, |
3836 | 0 | "{kind} name `{name}` conflicts with previous name `{prev}`", |
3837 | 0 | kind = kind.desc(), |
3838 | 0 | prev = e.key(), |
3839 | 0 | ); |
3840 | | } |
3841 | 925k | Entry::Vacant(e) => { |
3842 | 925k | e.insert(*ty); |
3843 | 925k | info.combine(ty.info(types), offset)?; |
3844 | | } |
3845 | | } |
3846 | 925k | Ok(()) |
3847 | 925k | } |
3848 | | |
3849 | | /// Validates that the `name` provided is allowed to have the type `ty`. |
3850 | 925k | fn validate( |
3851 | 925k | &self, |
3852 | 925k | name: &ComponentName, |
3853 | 925k | ty: &ComponentEntityType, |
3854 | 925k | types: &TypeAlloc, |
3855 | 925k | offset: usize, |
3856 | 925k | ) -> Result<()> { |
3857 | 925k | let func = || { |
3858 | 128k | let id = match ty { |
3859 | 128k | ComponentEntityType::Func(id) => *id, |
3860 | 0 | _ => bail!(offset, "item is not a func"), |
3861 | | }; |
3862 | 128k | Ok(&types[id]) |
3863 | 128k | }; |
3864 | 925k | match name.kind() { |
3865 | | // No validation necessary for these styles of names |
3866 | | ComponentNameKind::Label(_) |
3867 | | | ComponentNameKind::Interface(_) |
3868 | | | ComponentNameKind::Url(_) |
3869 | | | ComponentNameKind::Dependency(_) |
3870 | 796k | | ComponentNameKind::Hash(_) => {} |
3871 | | |
3872 | | // Constructors must return `(own $resource)` and the `$resource` |
3873 | | // must be named within this context to match `rname` |
3874 | 6.36k | ComponentNameKind::Constructor(rname) => { |
3875 | 6.36k | let ty = func()?; |
3876 | 6.36k | if ty.results.len() != 1 { |
3877 | 0 | bail!(offset, "function should return one value"); |
3878 | 6.36k | } |
3879 | 6.36k | let ty = ty.results[0].1; |
3880 | 6.36k | let resource = match ty { |
3881 | 0 | ComponentValType::Primitive(_) => None, |
3882 | 6.36k | ComponentValType::Type(ty) => match &types[ty] { |
3883 | 6.36k | ComponentDefinedType::Own(id) => Some(id), |
3884 | 0 | _ => None, |
3885 | | }, |
3886 | | }; |
3887 | 6.36k | let resource = match resource { |
3888 | 6.36k | Some(id) => id, |
3889 | 0 | None => bail!(offset, "function should return `(own $T)`"), |
3890 | | }; |
3891 | 6.36k | self.validate_resource_name(*resource, rname, offset)?; |
3892 | | } |
3893 | | |
3894 | | // Methods must take `(param "self" (borrow $resource))` as the |
3895 | | // first argument where `$resources` matches the name `resource` as |
3896 | | // named in this context. |
3897 | 61.6k | ComponentNameKind::Method(name) => { |
3898 | 61.6k | let ty = func()?; |
3899 | 61.6k | if ty.params.len() == 0 { |
3900 | 0 | bail!(offset, "function should have at least one argument"); |
3901 | 61.6k | } |
3902 | 61.6k | let (pname, pty) = &ty.params[0]; |
3903 | 61.6k | if pname.as_str() != "self" { |
3904 | 0 | bail!( |
3905 | 0 | offset, |
3906 | 0 | "function should have a first argument called `self`", |
3907 | 0 | ); |
3908 | 61.6k | } |
3909 | 61.6k | let id = match pty { |
3910 | 0 | ComponentValType::Primitive(_) => None, |
3911 | 61.6k | ComponentValType::Type(ty) => match &types[*ty] { |
3912 | 61.6k | ComponentDefinedType::Borrow(id) => Some(id), |
3913 | 0 | _ => None, |
3914 | | }, |
3915 | | }; |
3916 | 61.6k | let id = match id { |
3917 | 61.6k | Some(id) => id, |
3918 | 0 | None => bail!( |
3919 | 0 | offset, |
3920 | 0 | "function should take a first argument of `(borrow $T)`" |
3921 | 0 | ), |
3922 | | }; |
3923 | 61.6k | self.validate_resource_name(*id, name.resource(), offset)?; |
3924 | | } |
3925 | | |
3926 | | // Static methods don't have much validation beyond that they must |
3927 | | // be a function and the resource name referred to must already be |
3928 | | // in this context. |
3929 | 60.6k | ComponentNameKind::Static(name) => { |
3930 | 60.6k | func()?; |
3931 | 60.6k | if !self.all_resource_names.contains(name.resource().as_str()) { |
3932 | 0 | bail!(offset, "static resource name is not known in this context"); |
3933 | 60.6k | } |
3934 | | } |
3935 | | } |
3936 | | |
3937 | 925k | Ok(()) |
3938 | 925k | } |
3939 | | |
3940 | 68.0k | fn validate_resource_name( |
3941 | 68.0k | &self, |
3942 | 68.0k | id: AliasableResourceId, |
3943 | 68.0k | name: &KebabStr, |
3944 | 68.0k | offset: usize, |
3945 | 68.0k | ) -> Result<()> { |
3946 | 68.0k | let expected_name_idx = match self.resource_name_map.get(&id) { |
3947 | 68.0k | Some(idx) => *idx, |
3948 | | None => { |
3949 | 0 | bail!( |
3950 | 0 | offset, |
3951 | 0 | "resource used in function does not have a name in this context" |
3952 | 0 | ) |
3953 | | } |
3954 | | }; |
3955 | 68.0k | let expected_name = &self.all_resource_names[expected_name_idx]; |
3956 | 68.0k | if name.as_str() != expected_name { |
3957 | 0 | bail!( |
3958 | 0 | offset, |
3959 | 0 | "function does not match expected \ |
3960 | 0 | resource name `{expected_name}`" |
3961 | 0 | ); |
3962 | 68.0k | } |
3963 | 68.0k | Ok(()) |
3964 | 68.0k | } |
3965 | | } |
3966 | | |
3967 | | use self::append_only::*; |
3968 | | |
3969 | | mod append_only { |
3970 | | use crate::prelude::IndexMap; |
3971 | | use core::hash::Hash; |
3972 | | use core::ops::Deref; |
3973 | | |
3974 | | pub struct IndexMapAppendOnly<K, V>(IndexMap<K, V>); |
3975 | | |
3976 | | impl<K, V> IndexMapAppendOnly<K, V> |
3977 | | where |
3978 | | K: Hash + Eq + Ord + PartialEq + Clone, |
3979 | | { |
3980 | 56.8k | pub fn insert(&mut self, key: K, value: V) { |
3981 | 56.8k | let prev = self.0.insert(key, value); |
3982 | 56.8k | assert!(prev.is_none()); |
3983 | 56.8k | } <wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, alloc::vec::Vec<usize>>>::insert Line | Count | Source | 3980 | 18.7k | pub fn insert(&mut self, key: K, value: V) { | 3981 | 18.7k | let prev = self.0.insert(key, value); | 3982 | 18.7k | assert!(prev.is_none()); | 3983 | 18.7k | } |
<wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, core::option::Option<wasmparser::readers::core::types::ValType>>>::insert Line | Count | Source | 3980 | 38.0k | pub fn insert(&mut self, key: K, value: V) { | 3981 | 38.0k | let prev = self.0.insert(key, value); | 3982 | 38.0k | assert!(prev.is_none()); | 3983 | 38.0k | } |
|
3984 | | } |
3985 | | |
3986 | | impl<K, V> Deref for IndexMapAppendOnly<K, V> { |
3987 | | type Target = IndexMap<K, V>; |
3988 | 227k | fn deref(&self) -> &IndexMap<K, V> { |
3989 | 227k | &self.0 |
3990 | 227k | } <wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, alloc::vec::Vec<usize>> as core::ops::deref::Deref>::deref Line | Count | Source | 3988 | 89.9k | fn deref(&self) -> &IndexMap<K, V> { | 3989 | 89.9k | &self.0 | 3990 | 89.9k | } |
<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 | 3988 | 137k | fn deref(&self) -> &IndexMap<K, V> { | 3989 | 137k | &self.0 | 3990 | 137k | } |
|
3991 | | } |
3992 | | |
3993 | | impl<K, V> Default for IndexMapAppendOnly<K, V> { |
3994 | 777k | fn default() -> Self { |
3995 | 777k | Self(Default::default()) |
3996 | 777k | } <wasmparser::validator::component::append_only::IndexMapAppendOnly<wasmparser::validator::component_types::ResourceId, alloc::vec::Vec<usize>> as core::default::Default>::default Line | Count | Source | 3994 | 343k | fn default() -> Self { | 3995 | 343k | Self(Default::default()) | 3996 | 343k | } |
<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 | 3994 | 433k | fn default() -> Self { | 3995 | 433k | Self(Default::default()) | 3996 | 433k | } |
|
3997 | | } |
3998 | | |
3999 | | impl<K, V> IntoIterator for IndexMapAppendOnly<K, V> { |
4000 | | type IntoIter = <IndexMap<K, V> as IntoIterator>::IntoIter; |
4001 | | type Item = <IndexMap<K, V> as IntoIterator>::Item; |
4002 | 343k | fn into_iter(self) -> Self::IntoIter { |
4003 | 343k | self.0.into_iter() |
4004 | 343k | } <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 | 4002 | 126k | fn into_iter(self) -> Self::IntoIter { | 4003 | 126k | self.0.into_iter() | 4004 | 126k | } |
<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 | 4002 | 216k | fn into_iter(self) -> Self::IntoIter { | 4003 | 216k | self.0.into_iter() | 4004 | 216k | } |
|
4005 | | } |
4006 | | } |