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 | fn to_kebab_str<'a>(s: &'a str, desc: &str, offset: usize) -> Result<&'a KebabStr> { |
29 | match KebabStr::new(s) { |
30 | Some(s: &KebabStr) => Ok(s), |
31 | None => { |
32 | if s.is_empty() { |
33 | bail!(offset, " {desc} name cannot be empty" ); |
34 | } |
35 | |
36 | bail!(offset, " {desc} name ` {s}` is not in kebab case" ); |
37 | } |
38 | } |
39 | } |
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 | pub fn desc(&self) -> &'static str { |
202 | match self { |
203 | ExternKind::Import => "import" , |
204 | ExternKind::Export => "export" , |
205 | } |
206 | } |
207 | } |
208 | |
209 | impl ComponentState { |
210 | pub fn new(kind: ComponentKind) -> Self { |
211 | Self { |
212 | kind, |
213 | core_types: Default::default(), |
214 | core_modules: Default::default(), |
215 | core_instances: Default::default(), |
216 | core_funcs: Default::default(), |
217 | core_memories: Default::default(), |
218 | core_tables: Default::default(), |
219 | core_globals: Default::default(), |
220 | core_tags: Default::default(), |
221 | types: Default::default(), |
222 | funcs: Default::default(), |
223 | values: Default::default(), |
224 | instances: Default::default(), |
225 | components: Default::default(), |
226 | imports: Default::default(), |
227 | exports: Default::default(), |
228 | import_names: Default::default(), |
229 | export_names: Default::default(), |
230 | has_start: Default::default(), |
231 | type_info: TypeInfo::new(), |
232 | imported_resources: Default::default(), |
233 | defined_resources: Default::default(), |
234 | explicit_resources: Default::default(), |
235 | exported_types: Default::default(), |
236 | imported_types: Default::default(), |
237 | toplevel_exported_resources: Default::default(), |
238 | toplevel_imported_resources: Default::default(), |
239 | } |
240 | } |
241 | |
242 | pub fn type_count(&self) -> usize { |
243 | self.core_types.len() + self.types.len() |
244 | } |
245 | |
246 | pub fn instance_count(&self) -> usize { |
247 | self.core_instances.len() + self.instances.len() |
248 | } |
249 | |
250 | pub fn function_count(&self) -> usize { |
251 | self.core_funcs.len() + self.funcs.len() |
252 | } |
253 | |
254 | pub fn add_core_type( |
255 | components: &mut [Self], |
256 | ty: crate::CoreType, |
257 | features: &WasmFeatures, |
258 | types: &mut TypeAlloc, |
259 | offset: usize, |
260 | check_limit: bool, |
261 | ) -> Result<()> { |
262 | let current = components.last_mut().unwrap(); |
263 | if check_limit { |
264 | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types" , offset)?; |
265 | } |
266 | match ty { |
267 | crate::CoreType::Rec(rec) => { |
268 | current.canonicalize_and_intern_rec_group(features, types, rec, offset)?; |
269 | } |
270 | crate::CoreType::Module(decls) => { |
271 | let mod_ty = Self::create_module_type( |
272 | components, |
273 | decls.into_vec(), |
274 | features, |
275 | types, |
276 | offset, |
277 | )?; |
278 | let id = ComponentCoreTypeId::Module(types.push_ty(mod_ty)); |
279 | components.last_mut().unwrap().core_types.push(id); |
280 | } |
281 | } |
282 | |
283 | Ok(()) |
284 | } |
285 | |
286 | pub fn add_core_module( |
287 | &mut self, |
288 | module: &Module, |
289 | types: &mut TypeAlloc, |
290 | offset: usize, |
291 | ) -> Result<()> { |
292 | 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 | let mod_ty = ModuleType { |
298 | info: TypeInfo::core(module.type_size), |
299 | imports, |
300 | exports: module.exports.clone(), |
301 | }; |
302 | |
303 | let mod_id = types.push_ty(mod_ty); |
304 | self.core_modules.push(mod_id); |
305 | |
306 | Ok(()) |
307 | } |
308 | |
309 | pub fn add_core_instance( |
310 | &mut self, |
311 | instance: crate::Instance, |
312 | types: &mut TypeAlloc, |
313 | offset: usize, |
314 | ) -> Result<()> { |
315 | let instance = match instance { |
316 | crate::Instance::Instantiate { module_index, args } => { |
317 | self.instantiate_core_module(module_index, args.into_vec(), types, offset)? |
318 | } |
319 | crate::Instance::FromExports(exports) => { |
320 | self.instantiate_core_exports(exports.into_vec(), types, offset)? |
321 | } |
322 | }; |
323 | |
324 | self.core_instances.push(instance); |
325 | |
326 | Ok(()) |
327 | } |
328 | |
329 | pub fn add_type( |
330 | components: &mut Vec<Self>, |
331 | ty: crate::ComponentType, |
332 | features: &WasmFeatures, |
333 | types: &mut TypeAlloc, |
334 | offset: usize, |
335 | check_limit: bool, |
336 | ) -> Result<()> { |
337 | assert!(!components.is_empty()); |
338 | |
339 | fn current(components: &mut Vec<ComponentState>) -> &mut ComponentState { |
340 | components.last_mut().unwrap() |
341 | } |
342 | |
343 | let id = match ty { |
344 | crate::ComponentType::Defined(ty) => { |
345 | let ty = current(components).create_defined_type(ty, types, features, offset)?; |
346 | types.push(ty).into() |
347 | } |
348 | crate::ComponentType::Func(ty) => { |
349 | let ty = current(components).create_function_type(ty, types, features, offset)?; |
350 | types.push(ty).into() |
351 | } |
352 | crate::ComponentType::Component(decls) => { |
353 | let ty = Self::create_component_type( |
354 | components, |
355 | decls.into_vec(), |
356 | features, |
357 | types, |
358 | offset, |
359 | )?; |
360 | types.push(ty).into() |
361 | } |
362 | crate::ComponentType::Instance(decls) => { |
363 | let ty = Self::create_instance_type( |
364 | components, |
365 | decls.into_vec(), |
366 | features, |
367 | types, |
368 | offset, |
369 | )?; |
370 | types.push(ty).into() |
371 | } |
372 | crate::ComponentType::Resource { rep, dtor } => { |
373 | let component = current(components); |
374 | |
375 | // Resource types cannot be declared in a type context, only |
376 | // within a component context. |
377 | if component.kind != ComponentKind::Component { |
378 | bail!( |
379 | offset, |
380 | "resources can only be defined within a concrete component" |
381 | ); |
382 | } |
383 | |
384 | // Current MVP restriction of the component model. |
385 | if rep != ValType::I32 { |
386 | bail!(offset, "resources can only be represented by `i32`" ); |
387 | } |
388 | |
389 | // If specified validate that the destructor is both a valid |
390 | // function and has the correct signature. |
391 | if let Some(dtor) = dtor { |
392 | let ty = component.core_function_at(dtor, offset)?; |
393 | let ty = types[ty].composite_type.unwrap_func(); |
394 | if ty.params() != [rep] || ty.results() != [] { |
395 | bail!( |
396 | offset, |
397 | "core function {dtor} has wrong signature for a destructor" |
398 | ); |
399 | } |
400 | } |
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 | let id = types.alloc_resource_id(); |
408 | component.defined_resources.insert(id.resource(), Some(rep)); |
409 | id.into() |
410 | } |
411 | }; |
412 | |
413 | let current = current(components); |
414 | if check_limit { |
415 | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types" , offset)?; |
416 | } |
417 | current.types.push(id); |
418 | |
419 | Ok(()) |
420 | } |
421 | |
422 | pub fn add_import( |
423 | &mut self, |
424 | import: crate::ComponentImport, |
425 | features: &WasmFeatures, |
426 | types: &mut TypeAlloc, |
427 | offset: usize, |
428 | ) -> Result<()> { |
429 | let mut entity = self.check_type_ref(&import.ty, features, types, offset)?; |
430 | self.add_entity( |
431 | &mut entity, |
432 | Some((import.name.0, ExternKind::Import)), |
433 | features, |
434 | types, |
435 | offset, |
436 | )?; |
437 | self.toplevel_imported_resources.validate_extern( |
438 | import.name.0, |
439 | ExternKind::Import, |
440 | &entity, |
441 | types, |
442 | offset, |
443 | &mut self.import_names, |
444 | &mut self.imports, |
445 | &mut self.type_info, |
446 | features, |
447 | )?; |
448 | Ok(()) |
449 | } |
450 | |
451 | fn add_entity( |
452 | &mut self, |
453 | ty: &mut ComponentEntityType, |
454 | name_and_kind: Option<(&str, ExternKind)>, |
455 | features: &WasmFeatures, |
456 | types: &mut TypeAlloc, |
457 | offset: usize, |
458 | ) -> Result<()> { |
459 | let kind = name_and_kind.map(|(_, k)| k); |
460 | let (len, max, desc) = match ty { |
461 | ComponentEntityType::Module(id) => { |
462 | self.core_modules.push(*id); |
463 | (self.core_modules.len(), MAX_WASM_MODULES, "modules" ) |
464 | } |
465 | ComponentEntityType::Component(id) => { |
466 | self.components.push(*id); |
467 | (self.components.len(), MAX_WASM_COMPONENTS, "components" ) |
468 | } |
469 | ComponentEntityType::Instance(id) => { |
470 | match kind { |
471 | Some(ExternKind::Import) => self.prepare_instance_import(id, types), |
472 | Some(ExternKind::Export) => self.prepare_instance_export(id, types), |
473 | None => {} |
474 | } |
475 | self.instances.push(*id); |
476 | (self.instance_count(), MAX_WASM_INSTANCES, "instances" ) |
477 | } |
478 | ComponentEntityType::Func(id) => { |
479 | self.funcs.push(*id); |
480 | (self.function_count(), MAX_WASM_FUNCTIONS, "functions" ) |
481 | } |
482 | ComponentEntityType::Value(ty) => { |
483 | self.check_value_support(features, offset)?; |
484 | let value_used = match kind { |
485 | Some(ExternKind::Import) | None => false, |
486 | Some(ExternKind::Export) => true, |
487 | }; |
488 | self.values.push((*ty, value_used)); |
489 | (self.values.len(), MAX_WASM_VALUES, "values" ) |
490 | } |
491 | ComponentEntityType::Type { |
492 | created, |
493 | referenced, |
494 | } => { |
495 | 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 | if let ComponentAnyTypeId::Resource(id) = *created { |
502 | 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 | if created == referenced { |
511 | self.imported_resources |
512 | .insert(id.resource(), vec![self.imports.len()]); |
513 | } |
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 | if created == referenced { |
526 | self.defined_resources.insert(id.resource(), None); |
527 | } |
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 | self.explicit_resources |
534 | .insert(id.resource(), vec![self.exports.len()]); |
535 | } |
536 | |
537 | None => {} |
538 | } |
539 | } |
540 | (self.types.len(), MAX_WASM_TYPES, "types" ) |
541 | } |
542 | }; |
543 | |
544 | 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 | if let Some((name, kind)) = name_and_kind { |
550 | if !self.validate_and_register_named_types(Some(name), kind, ty, types) { |
551 | bail!( |
552 | offset, |
553 | " {} not valid to be used as {}" , |
554 | ty.desc(), |
555 | kind.desc() |
556 | ); |
557 | } |
558 | } |
559 | Ok(()) |
560 | } |
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 | fn validate_and_register_named_types( |
576 | &mut self, |
577 | toplevel_name: Option<&str>, |
578 | kind: ExternKind, |
579 | ty: &ComponentEntityType, |
580 | types: &TypeAlloc, |
581 | ) -> bool { |
582 | 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 | if let Some(name) = toplevel_name { |
587 | if let ComponentAnyTypeId::Resource(id) = *created { |
588 | let cx = match kind { |
589 | ExternKind::Import => &mut self.toplevel_imported_resources, |
590 | ExternKind::Export => &mut self.toplevel_exported_resources, |
591 | }; |
592 | cx.register(name, id); |
593 | } |
594 | } |
595 | } |
596 | |
597 | match self.kind { |
598 | ComponentKind::Component | ComponentKind::ComponentType => {} |
599 | ComponentKind::InstanceType => return true, |
600 | } |
601 | let set = match kind { |
602 | ExternKind::Import => &self.imported_types, |
603 | ExternKind::Export => &self.exported_types, |
604 | }; |
605 | 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 | created, |
614 | referenced, |
615 | } => { |
616 | if !self.all_valtypes_named(types, *referenced, set) { |
617 | return false; |
618 | } |
619 | match kind { |
620 | // Imported types are both valid for import and valid for |
621 | // export. |
622 | ExternKind::Import => { |
623 | self.imported_types.insert(*created); |
624 | self.exported_types.insert(*created); |
625 | } |
626 | ExternKind::Export => { |
627 | self.exported_types.insert(*created); |
628 | } |
629 | } |
630 | |
631 | 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 | ComponentEntityType::Instance(i) => types[*i] |
647 | .exports |
648 | .iter() |
649 | .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 | ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set), |
653 | |
654 | 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 | ComponentEntityType::Component(_) | ComponentEntityType::Module(_) => true, |
659 | } |
660 | } |
661 | |
662 | fn all_valtypes_named( |
663 | &self, |
664 | types: &TypeAlloc, |
665 | id: ComponentAnyTypeId, |
666 | set: &Set<ComponentAnyTypeId>, |
667 | ) -> bool { |
668 | 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 | 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 | ComponentAnyTypeId::Component(_) => true, |
680 | |
681 | ComponentAnyTypeId::Defined(id) => self.all_valtypes_named_in_defined(types, id, set), |
682 | ComponentAnyTypeId::Func(id) => self.all_valtypes_named_in_func(types, id, set), |
683 | ComponentAnyTypeId::Instance(id) => self.all_valtypes_named_in_instance(types, id, set), |
684 | } |
685 | } |
686 | |
687 | fn all_valtypes_named_in_instance( |
688 | &self, |
689 | types: &TypeAlloc, |
690 | id: ComponentInstanceTypeId, |
691 | set: &Set<ComponentAnyTypeId>, |
692 | ) -> bool { |
693 | // Instances must recursively have all referenced types named. |
694 | let ty = &types[id]; |
695 | ty.exports.values().all(|ty| match ty { |
696 | ComponentEntityType::Module(_) => true, |
697 | ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set), |
698 | ComponentEntityType::Type { created: id, .. } => { |
699 | self.all_valtypes_named(types, *id, set) |
700 | } |
701 | ComponentEntityType::Value(ComponentValType::Type(id)) => { |
702 | self.all_valtypes_named_in_defined(types, *id, set) |
703 | } |
704 | ComponentEntityType::Instance(id) => { |
705 | self.all_valtypes_named_in_instance(types, *id, set) |
706 | } |
707 | ComponentEntityType::Component(_) |
708 | | ComponentEntityType::Value(ComponentValType::Primitive(_)) => return true, |
709 | }) |
710 | } |
711 | |
712 | fn all_valtypes_named_in_defined( |
713 | &self, |
714 | types: &TypeAlloc, |
715 | id: ComponentDefinedTypeId, |
716 | set: &Set<ComponentAnyTypeId>, |
717 | ) -> bool { |
718 | let ty = &types[id]; |
719 | match ty { |
720 | // These types do not contain anything which must be |
721 | // named. |
722 | ComponentDefinedType::Primitive(_) |
723 | | ComponentDefinedType::Flags(_) |
724 | | ComponentDefinedType::Enum(_) |
725 | | ComponentDefinedType::ErrorContext => true, |
726 | |
727 | // Referenced types of all these aggregates must all be |
728 | // named. |
729 | ComponentDefinedType::Record(r) => { |
730 | r.fields.values().all(|t| types.type_named_valtype(t, set)) |
731 | } |
732 | ComponentDefinedType::Tuple(r) => { |
733 | r.types.iter().all(|t| types.type_named_valtype(t, set)) |
734 | } |
735 | ComponentDefinedType::Variant(r) => r |
736 | .cases |
737 | .values() |
738 | .filter_map(|t| t.ty.as_ref()) |
739 | .all(|t| types.type_named_valtype(t, set)), |
740 | ComponentDefinedType::Result { ok, err } => { |
741 | ok.as_ref() |
742 | .map(|t| types.type_named_valtype(t, set)) |
743 | .unwrap_or(true) |
744 | && err |
745 | .as_ref() |
746 | .map(|t| types.type_named_valtype(t, set)) |
747 | .unwrap_or(true) |
748 | } |
749 | ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => { |
750 | types.type_named_valtype(ty, set) |
751 | } |
752 | |
753 | // The resource referred to by own/borrow must be named. |
754 | ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => { |
755 | set.contains(&ComponentAnyTypeId::from(*id)) |
756 | } |
757 | |
758 | ComponentDefinedType::Future(ty) => ty |
759 | .as_ref() |
760 | .map(|ty| types.type_named_valtype(ty, set)) |
761 | .unwrap_or(true), |
762 | ComponentDefinedType::Stream(ty) => types.type_named_valtype(ty, set), |
763 | } |
764 | } |
765 | |
766 | fn all_valtypes_named_in_func( |
767 | &self, |
768 | types: &TypeAlloc, |
769 | id: ComponentFuncTypeId, |
770 | set: &Set<ComponentAnyTypeId>, |
771 | ) -> bool { |
772 | let ty = &types[id]; |
773 | // Function types must have all their parameters/results named. |
774 | ty.params |
775 | .iter() |
776 | .map(|(_, ty)| ty) |
777 | .chain(ty.results.iter().map(|(_, ty)| ty)) |
778 | .all(|ty| types.type_named_valtype(ty, set)) |
779 | } |
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 | fn prepare_instance_import(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) { |
795 | let ty = &types[*id]; |
796 | |
797 | // No special treatment for imports of instances which themselves have |
798 | // no defined resources |
799 | if ty.defined_resources.is_empty() { |
800 | return; |
801 | } |
802 | |
803 | let mut new_ty = ComponentInstanceType { |
804 | // Copied from the input verbatim |
805 | info: ty.info, |
806 | |
807 | // Copied over as temporary storage for now, and both of these are |
808 | // filled out and expanded below. |
809 | exports: ty.exports.clone(), |
810 | explicit_resources: ty.explicit_resources.clone(), |
811 | |
812 | // Explicitly discard this field since the |
813 | // defined resources are lifted into `self` |
814 | defined_resources: Default::default(), |
815 | }; |
816 | |
817 | // Create brand new resources for all defined ones in the instance. |
818 | let resources = (0..ty.defined_resources.len()) |
819 | .map(|_| types.alloc_resource_id()) |
820 | .collect::<IndexSet<_>>(); |
821 | |
822 | // Build a map from the defined resources in `ty` to those in `new_ty`. |
823 | // |
824 | // As part of this same loop the new resources, which were previously |
825 | // defined in `ty`, now become imported variables in `self`. Their |
826 | // path for where they're imported is updated as well with |
827 | // `self.next_import_index` as the import-to-be soon. |
828 | let mut mapping = Remapping::default(); |
829 | let ty = &types[*id]; |
830 | for (old, new) in ty.defined_resources.iter().zip(&resources) { |
831 | let prev = mapping.resources.insert(*old, new.resource()); |
832 | assert!(prev.is_none()); |
833 | |
834 | let mut base = vec![self.imports.len()]; |
835 | base.extend(ty.explicit_resources[old].iter().copied()); |
836 | 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 | for ty in new_ty.exports.values_mut() { |
842 | types.remap_component_entity(ty, &mut mapping); |
843 | } |
844 | for (id, path) in mem::take(&mut new_ty.explicit_resources) { |
845 | let id = *mapping.resources.get(&id).unwrap_or(&id); |
846 | new_ty.explicit_resources.insert(id, path); |
847 | } |
848 | |
849 | // Now that `new_ty` is complete finish its registration and then |
850 | // update `id` on the way out. |
851 | *id = types.push_ty(new_ty); |
852 | } |
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 | fn prepare_instance_export(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) { |
861 | // Exports of an instance mean that the enclosing context |
862 | // is inheriting the resources that the instance |
863 | // encapsulates. This means that the instance type |
864 | // recorded for this export will itself have no |
865 | // defined resources. |
866 | let ty = &types[*id]; |
867 | |
868 | // Check to see if `defined_resources` is non-empty, and if so then |
869 | // "freshen" all the resources and inherit them to our own defined |
870 | // resources, updating `id` in the process. |
871 | // |
872 | // Note though that this specifically is not rewriting the resources of |
873 | // exported instances. The `defined_resources` set on instance types is |
874 | // a little subtle (see its documentation for more info), but the |
875 | // general idea is that for a concrete instance it's always empty. Only |
876 | // for instance type definitions does it ever have elements in it. |
877 | // |
878 | // That means that if this set is non-empty then what's happening is |
879 | // that we're in a type context an exporting an instance of a previously |
880 | // specified type. In this case all resources are required to be |
881 | // "freshened" to ensure that multiple exports of the same type all |
882 | // export different types of resources. |
883 | // |
884 | // And finally note that this operation empties out the |
885 | // `defined_resources` set of the type that is registered for the |
886 | // instance, as this export is modeled as producing a concrete instance. |
887 | if !ty.defined_resources.is_empty() { |
888 | let mut new_ty = ty.clone(); |
889 | let mut mapping = Remapping::default(); |
890 | for old in mem::take(&mut new_ty.defined_resources) { |
891 | let new = types.alloc_resource_id(); |
892 | mapping.resources.insert(old, new.resource()); |
893 | self.defined_resources.insert(new.resource(), None); |
894 | } |
895 | for ty in new_ty.exports.values_mut() { |
896 | types.remap_component_entity(ty, &mut mapping); |
897 | } |
898 | for (id, path) in mem::take(&mut new_ty.explicit_resources) { |
899 | let id = mapping.resources.get(&id).copied().unwrap_or(id); |
900 | new_ty.explicit_resources.insert(id, path); |
901 | } |
902 | *id = types.push_ty(new_ty); |
903 | } |
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 | let ty = &types[*id]; |
912 | for (id, path) in ty.explicit_resources.iter() { |
913 | let mut new_path = vec![self.exports.len()]; |
914 | new_path.extend(path); |
915 | self.explicit_resources.insert(*id, new_path); |
916 | } |
917 | } |
918 | |
919 | pub fn add_export( |
920 | &mut self, |
921 | name: ComponentExportName<'_>, |
922 | mut ty: ComponentEntityType, |
923 | features: &WasmFeatures, |
924 | types: &mut TypeAlloc, |
925 | offset: usize, |
926 | check_limit: bool, |
927 | ) -> Result<()> { |
928 | if check_limit { |
929 | check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports" , offset)?; |
930 | } |
931 | self.add_entity( |
932 | &mut ty, |
933 | Some((name.0, ExternKind::Export)), |
934 | features, |
935 | types, |
936 | offset, |
937 | )?; |
938 | self.toplevel_exported_resources.validate_extern( |
939 | name.0, |
940 | ExternKind::Export, |
941 | &ty, |
942 | types, |
943 | offset, |
944 | &mut self.export_names, |
945 | &mut self.exports, |
946 | &mut self.type_info, |
947 | features, |
948 | )?; |
949 | Ok(()) |
950 | } |
951 | |
952 | pub fn lift_function( |
953 | &mut self, |
954 | core_func_index: u32, |
955 | type_index: u32, |
956 | options: Vec<CanonicalOption>, |
957 | types: &TypeList, |
958 | offset: usize, |
959 | features: &WasmFeatures, |
960 | ) -> Result<()> { |
961 | let ty = self.function_type_at(type_index, types, offset)?; |
962 | 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 | let info = ty.lower( |
967 | types, |
968 | if options.contains(&CanonicalOption::Async) { |
969 | if options |
970 | .iter() |
971 | .any(|v| matches!(v, CanonicalOption::Callback(_))) |
972 | { |
973 | Abi::LiftAsync |
974 | } else { |
975 | Abi::LiftAsyncStackful |
976 | } |
977 | } else { |
978 | Abi::LiftSync |
979 | }, |
980 | ); |
981 | self.check_options( |
982 | Some(core_ty), |
983 | &info, |
984 | &options, |
985 | types, |
986 | offset, |
987 | features, |
988 | true, |
989 | )?; |
990 | |
991 | if core_ty.params() != info.params.as_slice() { |
992 | bail!( |
993 | offset, |
994 | "lowered parameter types ` {:?}` do not match parameter types \ |
995 | ` {:?}` of core function {core_func_index}" , |
996 | info.params.as_slice(), |
997 | core_ty.params(), |
998 | ); |
999 | } |
1000 | |
1001 | if core_ty.results() != info.results.as_slice() { |
1002 | bail!( |
1003 | offset, |
1004 | "lowered result types ` {:?}` do not match result types \ |
1005 | ` {:?}` of core function {core_func_index}" , |
1006 | info.results.as_slice(), |
1007 | core_ty.results() |
1008 | ); |
1009 | } |
1010 | |
1011 | self.funcs |
1012 | .push(self.types[type_index as usize].unwrap_func()); |
1013 | |
1014 | Ok(()) |
1015 | } |
1016 | |
1017 | pub fn lower_function( |
1018 | &mut self, |
1019 | func_index: u32, |
1020 | options: Vec<CanonicalOption>, |
1021 | types: &mut TypeAlloc, |
1022 | offset: usize, |
1023 | features: &WasmFeatures, |
1024 | ) -> Result<()> { |
1025 | 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 | let info = ty.lower( |
1030 | types, |
1031 | if options.contains(&CanonicalOption::Async) { |
1032 | Abi::LowerAsync |
1033 | } else { |
1034 | Abi::LowerSync |
1035 | }, |
1036 | ); |
1037 | |
1038 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1039 | |
1040 | let id = types.intern_func_type(info.into_func_type(), offset); |
1041 | self.core_funcs.push(id); |
1042 | |
1043 | Ok(()) |
1044 | } |
1045 | |
1046 | pub fn resource_new( |
1047 | &mut self, |
1048 | resource: u32, |
1049 | types: &mut TypeAlloc, |
1050 | offset: usize, |
1051 | ) -> Result<()> { |
1052 | let rep = self.check_local_resource(resource, types, offset)?; |
1053 | let id = types.intern_func_type(FuncType::new([rep], [ValType::I32]), offset); |
1054 | self.core_funcs.push(id); |
1055 | Ok(()) |
1056 | } |
1057 | |
1058 | pub fn resource_drop( |
1059 | &mut self, |
1060 | resource: u32, |
1061 | types: &mut TypeAlloc, |
1062 | offset: usize, |
1063 | ) -> Result<()> { |
1064 | self.resource_at(resource, types, offset)?; |
1065 | let id = types.intern_func_type(FuncType::new([ValType::I32], []), offset); |
1066 | self.core_funcs.push(id); |
1067 | Ok(()) |
1068 | } |
1069 | |
1070 | pub fn resource_rep( |
1071 | &mut self, |
1072 | resource: u32, |
1073 | types: &mut TypeAlloc, |
1074 | offset: usize, |
1075 | ) -> Result<()> { |
1076 | let rep = self.check_local_resource(resource, types, offset)?; |
1077 | let id = types.intern_func_type(FuncType::new([ValType::I32], [rep]), offset); |
1078 | self.core_funcs.push(id); |
1079 | Ok(()) |
1080 | } |
1081 | |
1082 | pub fn task_backpressure( |
1083 | &mut self, |
1084 | types: &mut TypeAlloc, |
1085 | offset: usize, |
1086 | features: &WasmFeatures, |
1087 | ) -> Result<()> { |
1088 | if !features.component_model_async() { |
1089 | bail!( |
1090 | offset, |
1091 | "`task.backpressure` requires the component model async feature" |
1092 | ) |
1093 | } |
1094 | |
1095 | self.core_funcs |
1096 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1097 | Ok(()) |
1098 | } |
1099 | |
1100 | pub fn task_return( |
1101 | &mut self, |
1102 | type_index: u32, |
1103 | types: &mut TypeAlloc, |
1104 | offset: usize, |
1105 | features: &WasmFeatures, |
1106 | ) -> Result<()> { |
1107 | if !features.component_model_async() { |
1108 | bail!( |
1109 | offset, |
1110 | "`task.return` requires the component model async feature" |
1111 | ) |
1112 | } |
1113 | |
1114 | 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 | }) = types.get(id) |
1123 | else { |
1124 | bail!(offset, "invalid `task.return` type index" ); |
1125 | }; |
1126 | |
1127 | self.core_funcs.push(id); |
1128 | Ok(()) |
1129 | } |
1130 | |
1131 | pub fn task_wait( |
1132 | &mut self, |
1133 | _async_: bool, |
1134 | memory: u32, |
1135 | types: &mut TypeAlloc, |
1136 | offset: usize, |
1137 | features: &WasmFeatures, |
1138 | ) -> Result<()> { |
1139 | if !features.component_model_async() { |
1140 | bail!( |
1141 | offset, |
1142 | "`task.wait` requires the component model async feature" |
1143 | ) |
1144 | } |
1145 | |
1146 | self.memory_at(memory, offset)?; |
1147 | |
1148 | self.core_funcs |
1149 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1150 | Ok(()) |
1151 | } |
1152 | |
1153 | pub fn task_poll( |
1154 | &mut self, |
1155 | _async_: bool, |
1156 | memory: u32, |
1157 | types: &mut TypeAlloc, |
1158 | offset: usize, |
1159 | features: &WasmFeatures, |
1160 | ) -> Result<()> { |
1161 | if !features.component_model_async() { |
1162 | bail!( |
1163 | offset, |
1164 | "`task.poll` requires the component model async feature" |
1165 | ) |
1166 | } |
1167 | |
1168 | self.memory_at(memory, offset)?; |
1169 | |
1170 | self.core_funcs |
1171 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1172 | Ok(()) |
1173 | } |
1174 | |
1175 | pub fn task_yield( |
1176 | &mut self, |
1177 | _async_: bool, |
1178 | types: &mut TypeAlloc, |
1179 | offset: usize, |
1180 | features: &WasmFeatures, |
1181 | ) -> Result<()> { |
1182 | if !features.component_model_async() { |
1183 | bail!( |
1184 | offset, |
1185 | "`task.yield` requires the component model async feature" |
1186 | ) |
1187 | } |
1188 | |
1189 | self.core_funcs |
1190 | .push(types.intern_func_type(FuncType::new([], []), offset)); |
1191 | Ok(()) |
1192 | } |
1193 | |
1194 | pub fn subtask_drop( |
1195 | &mut self, |
1196 | types: &mut TypeAlloc, |
1197 | offset: usize, |
1198 | features: &WasmFeatures, |
1199 | ) -> Result<()> { |
1200 | if !features.component_model_async() { |
1201 | bail!( |
1202 | offset, |
1203 | "`subtask.drop` requires the component model async feature" |
1204 | ) |
1205 | } |
1206 | |
1207 | self.core_funcs |
1208 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1209 | Ok(()) |
1210 | } |
1211 | |
1212 | pub fn stream_new( |
1213 | &mut self, |
1214 | ty: u32, |
1215 | types: &mut TypeAlloc, |
1216 | offset: usize, |
1217 | features: &WasmFeatures, |
1218 | ) -> Result<()> { |
1219 | if !features.component_model_async() { |
1220 | bail!( |
1221 | offset, |
1222 | "`stream.new` requires the component model async feature" |
1223 | ) |
1224 | } |
1225 | |
1226 | let ty = self.defined_type_at(ty, offset)?; |
1227 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1228 | bail!(offset, "`stream.new` requires a stream type" ) |
1229 | }; |
1230 | |
1231 | self.core_funcs |
1232 | .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); |
1233 | Ok(()) |
1234 | } |
1235 | |
1236 | pub fn stream_read( |
1237 | &mut self, |
1238 | ty: u32, |
1239 | options: Vec<CanonicalOption>, |
1240 | types: &mut TypeAlloc, |
1241 | offset: usize, |
1242 | features: &WasmFeatures, |
1243 | ) -> Result<()> { |
1244 | if !features.component_model_async() { |
1245 | bail!( |
1246 | offset, |
1247 | "`stream.read` requires the component model async feature" |
1248 | ) |
1249 | } |
1250 | |
1251 | let ty = self.defined_type_at(ty, offset)?; |
1252 | let ComponentDefinedType::Stream(payload_type) = &types[ty] else { |
1253 | bail!(offset, "`stream.read` requires a stream type" ) |
1254 | }; |
1255 | |
1256 | let mut info = LoweringInfo::default(); |
1257 | info.requires_memory = true; |
1258 | info.requires_realloc = payload_type.contains_ptr(types); |
1259 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1260 | |
1261 | self.core_funcs |
1262 | .push(types.intern_func_type(FuncType::new([ValType::I32; 3], [ValType::I32]), offset)); |
1263 | Ok(()) |
1264 | } |
1265 | |
1266 | pub fn stream_write( |
1267 | &mut self, |
1268 | ty: u32, |
1269 | options: Vec<CanonicalOption>, |
1270 | types: &mut TypeAlloc, |
1271 | offset: usize, |
1272 | features: &WasmFeatures, |
1273 | ) -> Result<()> { |
1274 | if !features.component_model_async() { |
1275 | bail!( |
1276 | offset, |
1277 | "`stream.write` requires the component model async feature" |
1278 | ) |
1279 | } |
1280 | |
1281 | let ty = self.defined_type_at(ty, offset)?; |
1282 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1283 | bail!(offset, "`stream.write` requires a stream type" ) |
1284 | }; |
1285 | |
1286 | let mut info = LoweringInfo::default(); |
1287 | info.requires_memory = true; |
1288 | info.requires_realloc = false; |
1289 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1290 | |
1291 | self.core_funcs |
1292 | .push(types.intern_func_type(FuncType::new([ValType::I32; 3], [ValType::I32]), offset)); |
1293 | Ok(()) |
1294 | } |
1295 | |
1296 | pub fn stream_cancel_read( |
1297 | &mut self, |
1298 | ty: u32, |
1299 | _async_: bool, |
1300 | types: &mut TypeAlloc, |
1301 | offset: usize, |
1302 | features: &WasmFeatures, |
1303 | ) -> Result<()> { |
1304 | if !features.component_model_async() { |
1305 | bail!( |
1306 | offset, |
1307 | "`stream.cancel-read` requires the component model async feature" |
1308 | ) |
1309 | } |
1310 | |
1311 | let ty = self.defined_type_at(ty, offset)?; |
1312 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1313 | bail!(offset, "`stream.cancel-read` requires a stream type" ) |
1314 | }; |
1315 | |
1316 | self.core_funcs |
1317 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1318 | Ok(()) |
1319 | } |
1320 | |
1321 | pub fn stream_cancel_write( |
1322 | &mut self, |
1323 | ty: u32, |
1324 | _async_: bool, |
1325 | types: &mut TypeAlloc, |
1326 | offset: usize, |
1327 | features: &WasmFeatures, |
1328 | ) -> Result<()> { |
1329 | if !features.component_model_async() { |
1330 | bail!( |
1331 | offset, |
1332 | "`stream.cancel-write` requires the component model async feature" |
1333 | ) |
1334 | } |
1335 | |
1336 | let ty = self.defined_type_at(ty, offset)?; |
1337 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1338 | bail!(offset, "`stream.cancel-write` requires a stream type" ) |
1339 | }; |
1340 | |
1341 | self.core_funcs |
1342 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1343 | Ok(()) |
1344 | } |
1345 | |
1346 | pub fn stream_close_readable( |
1347 | &mut self, |
1348 | ty: u32, |
1349 | types: &mut TypeAlloc, |
1350 | offset: usize, |
1351 | features: &WasmFeatures, |
1352 | ) -> Result<()> { |
1353 | if !features.component_model_async() { |
1354 | bail!( |
1355 | offset, |
1356 | "`stream.close-readable` requires the component model async feature" |
1357 | ) |
1358 | } |
1359 | |
1360 | let ty = self.defined_type_at(ty, offset)?; |
1361 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1362 | bail!(offset, "`stream.close-readable` requires a stream type" ) |
1363 | }; |
1364 | |
1365 | self.core_funcs |
1366 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1367 | Ok(()) |
1368 | } |
1369 | |
1370 | pub fn stream_close_writable( |
1371 | &mut self, |
1372 | ty: u32, |
1373 | types: &mut TypeAlloc, |
1374 | offset: usize, |
1375 | features: &WasmFeatures, |
1376 | ) -> Result<()> { |
1377 | if !features.component_model_async() { |
1378 | bail!( |
1379 | offset, |
1380 | "`stream.close-writable` requires the component model async feature" |
1381 | ) |
1382 | } |
1383 | |
1384 | let ty = self.defined_type_at(ty, offset)?; |
1385 | let ComponentDefinedType::Stream(_) = &types[ty] else { |
1386 | bail!(offset, "`stream.close-writable` requires a stream type" ) |
1387 | }; |
1388 | |
1389 | self.core_funcs |
1390 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset)); |
1391 | Ok(()) |
1392 | } |
1393 | |
1394 | pub fn future_new( |
1395 | &mut self, |
1396 | ty: u32, |
1397 | types: &mut TypeAlloc, |
1398 | offset: usize, |
1399 | features: &WasmFeatures, |
1400 | ) -> Result<()> { |
1401 | if !features.component_model_async() { |
1402 | bail!( |
1403 | offset, |
1404 | "`future.new` requires the component model async feature" |
1405 | ) |
1406 | } |
1407 | |
1408 | let ty = self.defined_type_at(ty, offset)?; |
1409 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1410 | bail!(offset, "`future.new` requires a future type" ) |
1411 | }; |
1412 | |
1413 | self.core_funcs |
1414 | .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); |
1415 | Ok(()) |
1416 | } |
1417 | |
1418 | pub fn future_read( |
1419 | &mut self, |
1420 | ty: u32, |
1421 | options: Vec<CanonicalOption>, |
1422 | types: &mut TypeAlloc, |
1423 | offset: usize, |
1424 | features: &WasmFeatures, |
1425 | ) -> Result<()> { |
1426 | if !features.component_model_async() { |
1427 | bail!( |
1428 | offset, |
1429 | "`future.read` requires the component model async feature" |
1430 | ) |
1431 | } |
1432 | |
1433 | let ty = self.defined_type_at(ty, offset)?; |
1434 | let ComponentDefinedType::Future(payload_type) = &types[ty] else { |
1435 | bail!(offset, "`future.read` requires a future type" ) |
1436 | }; |
1437 | |
1438 | let mut info = LoweringInfo::default(); |
1439 | info.requires_memory = true; |
1440 | info.requires_realloc = payload_type |
1441 | .map(|ty| ty.contains_ptr(types)) |
1442 | .unwrap_or(false); |
1443 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1444 | |
1445 | self.core_funcs |
1446 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset)); |
1447 | Ok(()) |
1448 | } |
1449 | |
1450 | pub fn future_write( |
1451 | &mut self, |
1452 | ty: u32, |
1453 | options: Vec<CanonicalOption>, |
1454 | types: &mut TypeAlloc, |
1455 | offset: usize, |
1456 | features: &WasmFeatures, |
1457 | ) -> Result<()> { |
1458 | if !features.component_model_async() { |
1459 | bail!( |
1460 | offset, |
1461 | "`future.write` requires the component model async feature" |
1462 | ) |
1463 | } |
1464 | |
1465 | let ty = self.defined_type_at(ty, offset)?; |
1466 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1467 | bail!(offset, "`future.write` requires a future type" ) |
1468 | }; |
1469 | |
1470 | let mut info = LoweringInfo::default(); |
1471 | info.requires_memory = true; |
1472 | info.requires_realloc = false; |
1473 | self.check_options(None, &info, &options, types, offset, features, true)?; |
1474 | |
1475 | self.core_funcs |
1476 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset)); |
1477 | Ok(()) |
1478 | } |
1479 | |
1480 | pub fn future_cancel_read( |
1481 | &mut self, |
1482 | ty: u32, |
1483 | _async_: bool, |
1484 | types: &mut TypeAlloc, |
1485 | offset: usize, |
1486 | features: &WasmFeatures, |
1487 | ) -> Result<()> { |
1488 | if !features.component_model_async() { |
1489 | bail!( |
1490 | offset, |
1491 | "`future.cancel-read` requires the component model async feature" |
1492 | ) |
1493 | } |
1494 | |
1495 | let ty = self.defined_type_at(ty, offset)?; |
1496 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1497 | bail!(offset, "`future.cancel-read` requires a future type" ) |
1498 | }; |
1499 | |
1500 | self.core_funcs |
1501 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1502 | Ok(()) |
1503 | } |
1504 | |
1505 | pub fn future_cancel_write( |
1506 | &mut self, |
1507 | ty: u32, |
1508 | _async_: bool, |
1509 | types: &mut TypeAlloc, |
1510 | offset: usize, |
1511 | features: &WasmFeatures, |
1512 | ) -> Result<()> { |
1513 | if !features.component_model_async() { |
1514 | bail!( |
1515 | offset, |
1516 | "`future.cancel-write` requires the component model async feature" |
1517 | ) |
1518 | } |
1519 | |
1520 | let ty = self.defined_type_at(ty, offset)?; |
1521 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1522 | bail!(offset, "`future.cancel-write` requires a future type" ) |
1523 | }; |
1524 | |
1525 | self.core_funcs |
1526 | .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset)); |
1527 | Ok(()) |
1528 | } |
1529 | |
1530 | pub fn future_close_readable( |
1531 | &mut self, |
1532 | ty: u32, |
1533 | types: &mut TypeAlloc, |
1534 | offset: usize, |
1535 | features: &WasmFeatures, |
1536 | ) -> Result<()> { |
1537 | if !features.component_model_async() { |
1538 | bail!( |
1539 | offset, |
1540 | "`future.close-readable` requires the component model async feature" |
1541 | ) |
1542 | } |
1543 | |
1544 | let ty = self.defined_type_at(ty, offset)?; |
1545 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1546 | bail!(offset, "`future.close-readable` requires a future type" ) |
1547 | }; |
1548 | |
1549 | self.core_funcs |
1550 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1551 | Ok(()) |
1552 | } |
1553 | |
1554 | pub fn future_close_writable( |
1555 | &mut self, |
1556 | ty: u32, |
1557 | types: &mut TypeAlloc, |
1558 | offset: usize, |
1559 | features: &WasmFeatures, |
1560 | ) -> Result<()> { |
1561 | if !features.component_model_async() { |
1562 | bail!( |
1563 | offset, |
1564 | "`future.close-writable` requires the component model async feature" |
1565 | ) |
1566 | } |
1567 | |
1568 | let ty = self.defined_type_at(ty, offset)?; |
1569 | let ComponentDefinedType::Future(_) = &types[ty] else { |
1570 | bail!(offset, "`future.close-writable` requires a future type" ) |
1571 | }; |
1572 | |
1573 | self.core_funcs |
1574 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset)); |
1575 | Ok(()) |
1576 | } |
1577 | |
1578 | pub fn error_context_new( |
1579 | &mut self, |
1580 | options: Vec<CanonicalOption>, |
1581 | types: &mut TypeAlloc, |
1582 | offset: usize, |
1583 | features: &WasmFeatures, |
1584 | ) -> Result<()> { |
1585 | if !features.component_model_async() { |
1586 | bail!( |
1587 | offset, |
1588 | "`error-context.new` requires the component model async feature" |
1589 | ) |
1590 | } |
1591 | |
1592 | let mut info = LoweringInfo::default(); |
1593 | info.requires_memory = true; |
1594 | info.requires_realloc = false; |
1595 | self.check_options(None, &info, &options, types, offset, features, false)?; |
1596 | |
1597 | self.core_funcs |
1598 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset)); |
1599 | Ok(()) |
1600 | } |
1601 | |
1602 | pub fn error_context_debug_message( |
1603 | &mut self, |
1604 | options: Vec<CanonicalOption>, |
1605 | types: &mut TypeAlloc, |
1606 | offset: usize, |
1607 | features: &WasmFeatures, |
1608 | ) -> Result<()> { |
1609 | if !features.component_model_async() { |
1610 | bail!( |
1611 | offset, |
1612 | "`error-context.debug-message` requires the component model async feature" |
1613 | ) |
1614 | } |
1615 | |
1616 | let mut info = LoweringInfo::default(); |
1617 | info.requires_memory = true; |
1618 | info.requires_realloc = true; |
1619 | self.check_options(None, &info, &options, types, offset, features, false)?; |
1620 | |
1621 | self.core_funcs |
1622 | .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset)); |
1623 | Ok(()) |
1624 | } |
1625 | |
1626 | pub fn error_context_drop( |
1627 | &mut self, |
1628 | types: &mut TypeAlloc, |
1629 | offset: usize, |
1630 | features: &WasmFeatures, |
1631 | ) -> Result<()> { |
1632 | if !features.component_model_async() { |
1633 | bail!( |
1634 | offset, |
1635 | "`error-context.drop` requires the component model async feature" |
1636 | ) |
1637 | } |
1638 | |
1639 | self.core_funcs |
1640 | .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); |
1641 | Ok(()) |
1642 | } |
1643 | |
1644 | fn check_local_resource(&self, idx: u32, types: &TypeList, offset: usize) -> Result<ValType> { |
1645 | let resource = self.resource_at(idx, types, offset)?; |
1646 | match self |
1647 | .defined_resources |
1648 | .get(&resource.resource()) |
1649 | .and_then(|rep| *rep) |
1650 | { |
1651 | Some(ty) => Ok(ty), |
1652 | None => bail!(offset, "type {idx} is not a local resource" ), |
1653 | } |
1654 | } |
1655 | |
1656 | fn resource_at<'a>( |
1657 | &self, |
1658 | idx: u32, |
1659 | _types: &'a TypeList, |
1660 | offset: usize, |
1661 | ) -> Result<AliasableResourceId> { |
1662 | if let ComponentAnyTypeId::Resource(id) = self.component_type_at(idx, offset)? { |
1663 | return Ok(id); |
1664 | } |
1665 | bail!(offset, "type index {} is not a resource type" , idx) |
1666 | } |
1667 | |
1668 | pub fn thread_spawn( |
1669 | &mut self, |
1670 | func_ty_index: u32, |
1671 | types: &mut TypeAlloc, |
1672 | offset: usize, |
1673 | features: &WasmFeatures, |
1674 | ) -> Result<()> { |
1675 | if !features.shared_everything_threads() { |
1676 | bail!( |
1677 | offset, |
1678 | "`thread.spawn` requires the shared-everything-threads proposal" |
1679 | ) |
1680 | } |
1681 | |
1682 | // Validate the type accepted by `thread.spawn`. |
1683 | let core_type_id = match self.core_type_at(func_ty_index, offset)? { |
1684 | ComponentCoreTypeId::Sub(c) => c, |
1685 | ComponentCoreTypeId::Module(_) => bail!(offset, "expected a core function type" ), |
1686 | }; |
1687 | let sub_ty = &types[core_type_id]; |
1688 | if !sub_ty.composite_type.shared { |
1689 | bail!(offset, "spawn type must be shared" ); |
1690 | } |
1691 | match &sub_ty.composite_type.inner { |
1692 | CompositeInnerType::Func(func_ty) => { |
1693 | if func_ty.params() != [ValType::I32] { |
1694 | bail!( |
1695 | offset, |
1696 | "spawn function must take a single `i32` argument (currently)" |
1697 | ); |
1698 | } |
1699 | if func_ty.results() != [] { |
1700 | bail!(offset, "spawn function must not return any values" ); |
1701 | } |
1702 | } |
1703 | _ => bail!(offset, "spawn type must be a function" ), |
1704 | } |
1705 | |
1706 | // Insert the core function. |
1707 | let packed_index = PackedIndex::from_id(core_type_id).ok_or_else(|| { |
1708 | format_err!(offset, "implementation limit: too many types in `TypeList`" ) |
1709 | })?; |
1710 | let start_func_ref = RefType::concrete(true, packed_index); |
1711 | let func_ty = FuncType::new([ValType::Ref(start_func_ref), ValType::I32], [ValType::I32]); |
1712 | let core_ty = SubType::func(func_ty, true); |
1713 | let id = types.intern_sub_type(core_ty, offset); |
1714 | self.core_funcs.push(id); |
1715 | |
1716 | Ok(()) |
1717 | } |
1718 | |
1719 | pub fn thread_hw_concurrency( |
1720 | &mut self, |
1721 | types: &mut TypeAlloc, |
1722 | offset: usize, |
1723 | features: &WasmFeatures, |
1724 | ) -> Result<()> { |
1725 | if !features.shared_everything_threads() { |
1726 | bail!( |
1727 | offset, |
1728 | "`thread.hw_concurrency` requires the shared-everything-threads proposal" |
1729 | ) |
1730 | } |
1731 | |
1732 | let func_ty = FuncType::new([], [ValType::I32]); |
1733 | let core_ty = SubType::func(func_ty, true); |
1734 | let id = types.intern_sub_type(core_ty, offset); |
1735 | self.core_funcs.push(id); |
1736 | |
1737 | Ok(()) |
1738 | } |
1739 | |
1740 | pub fn add_component(&mut self, component: ComponentType, types: &mut TypeAlloc) -> Result<()> { |
1741 | let id = types.push_ty(component); |
1742 | self.components.push(id); |
1743 | Ok(()) |
1744 | } |
1745 | |
1746 | pub fn add_instance( |
1747 | &mut self, |
1748 | instance: crate::ComponentInstance, |
1749 | features: &WasmFeatures, |
1750 | types: &mut TypeAlloc, |
1751 | offset: usize, |
1752 | ) -> Result<()> { |
1753 | let instance = match instance { |
1754 | crate::ComponentInstance::Instantiate { |
1755 | component_index, |
1756 | args, |
1757 | } => self.instantiate_component( |
1758 | component_index, |
1759 | args.into_vec(), |
1760 | features, |
1761 | types, |
1762 | offset, |
1763 | )?, |
1764 | crate::ComponentInstance::FromExports(exports) => { |
1765 | self.instantiate_component_exports(exports.into_vec(), features, types, offset)? |
1766 | } |
1767 | }; |
1768 | |
1769 | self.instances.push(instance); |
1770 | |
1771 | Ok(()) |
1772 | } |
1773 | |
1774 | pub fn add_alias( |
1775 | components: &mut [Self], |
1776 | alias: crate::ComponentAlias, |
1777 | features: &WasmFeatures, |
1778 | types: &mut TypeAlloc, |
1779 | offset: usize, |
1780 | ) -> Result<()> { |
1781 | match alias { |
1782 | crate::ComponentAlias::InstanceExport { |
1783 | instance_index, |
1784 | kind, |
1785 | name, |
1786 | } => components.last_mut().unwrap().alias_instance_export( |
1787 | instance_index, |
1788 | kind, |
1789 | name, |
1790 | features, |
1791 | types, |
1792 | offset, |
1793 | ), |
1794 | crate::ComponentAlias::CoreInstanceExport { |
1795 | instance_index, |
1796 | kind, |
1797 | name, |
1798 | } => components.last_mut().unwrap().alias_core_instance_export( |
1799 | instance_index, |
1800 | kind, |
1801 | name, |
1802 | types, |
1803 | offset, |
1804 | ), |
1805 | crate::ComponentAlias::Outer { kind, count, index } => match kind { |
1806 | ComponentOuterAliasKind::CoreModule => { |
1807 | Self::alias_module(components, count, index, offset) |
1808 | } |
1809 | ComponentOuterAliasKind::CoreType => { |
1810 | Self::alias_core_type(components, count, index, offset) |
1811 | } |
1812 | ComponentOuterAliasKind::Type => { |
1813 | Self::alias_type(components, count, index, types, offset) |
1814 | } |
1815 | ComponentOuterAliasKind::Component => { |
1816 | Self::alias_component(components, count, index, offset) |
1817 | } |
1818 | }, |
1819 | } |
1820 | } |
1821 | |
1822 | pub fn add_start( |
1823 | &mut self, |
1824 | func_index: u32, |
1825 | args: &[u32], |
1826 | results: u32, |
1827 | features: &WasmFeatures, |
1828 | types: &mut TypeList, |
1829 | offset: usize, |
1830 | ) -> Result<()> { |
1831 | if !features.component_model_values() { |
1832 | bail!( |
1833 | offset, |
1834 | "support for component model `value`s is not enabled" |
1835 | ); |
1836 | } |
1837 | if self.has_start { |
1838 | return Err(BinaryReaderError::new( |
1839 | "component cannot have more than one start function" , |
1840 | offset, |
1841 | )); |
1842 | } |
1843 | |
1844 | let ft = &types[self.function_at(func_index, offset)?]; |
1845 | |
1846 | if ft.params.len() != args.len() { |
1847 | bail!( |
1848 | offset, |
1849 | "component start function requires {} arguments but was given {}" , |
1850 | ft.params.len(), |
1851 | args.len() |
1852 | ); |
1853 | } |
1854 | |
1855 | if ft.results.len() as u32 != results { |
1856 | bail!( |
1857 | offset, |
1858 | "component start function has a result count of {results} \ |
1859 | but the function type has a result count of {type_results}" , |
1860 | type_results = ft.results.len(), |
1861 | ); |
1862 | } |
1863 | |
1864 | let cx = SubtypeCx::new(types, types); |
1865 | 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 | cx.component_val_type(self.value_at(*arg, offset)?, ty, offset) |
1868 | .with_context(|| { |
1869 | format!("value type mismatch for component start function argument {i}" ) |
1870 | })?; |
1871 | } |
1872 | |
1873 | for (_, ty) in ft.results.iter() { |
1874 | self.values.push((*ty, false)); |
1875 | } |
1876 | |
1877 | self.has_start = true; |
1878 | |
1879 | Ok(()) |
1880 | } |
1881 | |
1882 | fn check_options( |
1883 | &self, |
1884 | core_ty: Option<&FuncType>, |
1885 | info: &LoweringInfo, |
1886 | options: &[CanonicalOption], |
1887 | types: &TypeList, |
1888 | offset: usize, |
1889 | features: &WasmFeatures, |
1890 | allow_async: bool, |
1891 | ) -> Result<()> { |
1892 | fn display(option: CanonicalOption) -> &'static str { |
1893 | match option { |
1894 | CanonicalOption::UTF8 => "utf8" , |
1895 | CanonicalOption::UTF16 => "utf16" , |
1896 | CanonicalOption::CompactUTF16 => "latin1-utf16" , |
1897 | CanonicalOption::Memory(_) => "memory" , |
1898 | CanonicalOption::Realloc(_) => "realloc" , |
1899 | CanonicalOption::PostReturn(_) => "post-return" , |
1900 | CanonicalOption::Async => "async" , |
1901 | CanonicalOption::Callback(_) => "callback" , |
1902 | } |
1903 | } |
1904 | |
1905 | let mut encoding = None; |
1906 | let mut memory = None; |
1907 | let mut realloc = None; |
1908 | let mut post_return = None; |
1909 | let mut async_ = false; |
1910 | let mut callback = None; |
1911 | |
1912 | for option in options { |
1913 | match option { |
1914 | CanonicalOption::UTF8 | CanonicalOption::UTF16 | CanonicalOption::CompactUTF16 => { |
1915 | match encoding { |
1916 | Some(existing) => { |
1917 | bail!( |
1918 | offset, |
1919 | "canonical encoding option ` {}` conflicts with option ` {}`" , |
1920 | display(existing), |
1921 | display(*option), |
1922 | ) |
1923 | } |
1924 | None => encoding = Some(*option), |
1925 | } |
1926 | } |
1927 | CanonicalOption::Memory(idx) => { |
1928 | memory = match memory { |
1929 | None => { |
1930 | self.memory_at(*idx, offset)?; |
1931 | Some(*idx) |
1932 | } |
1933 | Some(_) => { |
1934 | return Err(BinaryReaderError::new( |
1935 | "canonical option `memory` is specified more than once" , |
1936 | offset, |
1937 | )) |
1938 | } |
1939 | } |
1940 | } |
1941 | CanonicalOption::Realloc(idx) => { |
1942 | realloc = match realloc { |
1943 | None => { |
1944 | let ty = types[self.core_function_at(*idx, offset)?].unwrap_func(); |
1945 | if ty.params() |
1946 | != [ValType::I32, ValType::I32, ValType::I32, ValType::I32] |
1947 | || ty.results() != [ValType::I32] |
1948 | { |
1949 | return Err(BinaryReaderError::new( |
1950 | "canonical option `realloc` uses a core function with an incorrect signature" , |
1951 | offset, |
1952 | )); |
1953 | } |
1954 | Some(*idx) |
1955 | } |
1956 | Some(_) => { |
1957 | return Err(BinaryReaderError::new( |
1958 | "canonical option `realloc` is specified more than once" , |
1959 | offset, |
1960 | )) |
1961 | } |
1962 | } |
1963 | } |
1964 | CanonicalOption::PostReturn(idx) => { |
1965 | post_return = match post_return { |
1966 | None => { |
1967 | let core_ty = core_ty.ok_or_else(|| { |
1968 | BinaryReaderError::new( |
1969 | "canonical option `post-return` cannot be specified for lowerings" , |
1970 | offset, |
1971 | ) |
1972 | })?; |
1973 | |
1974 | let ty = types[self.core_function_at(*idx, offset)?].unwrap_func(); |
1975 | |
1976 | if ty.params() != core_ty.results() || !ty.results().is_empty() { |
1977 | return Err(BinaryReaderError::new( |
1978 | "canonical option `post-return` uses a core function with an incorrect signature" , |
1979 | offset, |
1980 | )); |
1981 | } |
1982 | Some(*idx) |
1983 | } |
1984 | Some(_) => { |
1985 | return Err(BinaryReaderError::new( |
1986 | "canonical option `post-return` is specified more than once" , |
1987 | offset, |
1988 | )) |
1989 | } |
1990 | } |
1991 | } |
1992 | CanonicalOption::Async => { |
1993 | if async_ { |
1994 | return Err(BinaryReaderError::new( |
1995 | "canonical option `async` is specified more than once" , |
1996 | offset, |
1997 | )); |
1998 | } else { |
1999 | if !features.component_model_async() { |
2000 | bail!( |
2001 | offset, |
2002 | "canonical option `async` requires the component model async feature" |
2003 | ); |
2004 | } |
2005 | |
2006 | async_ = true; |
2007 | } |
2008 | } |
2009 | CanonicalOption::Callback(idx) => { |
2010 | callback = match callback { |
2011 | None => { |
2012 | if core_ty.is_none() { |
2013 | return Err(BinaryReaderError::new( |
2014 | "canonical option `callback` cannot be specified for lowerings" , |
2015 | offset, |
2016 | )); |
2017 | } |
2018 | |
2019 | let ty = types[self.core_function_at(*idx, offset)?].unwrap_func(); |
2020 | |
2021 | if ty.params() != [ValType::I32; 4] && ty.params() != [ValType::I32] { |
2022 | return Err(BinaryReaderError::new( |
2023 | "canonical option `callback` uses a core function with an incorrect signature" , |
2024 | offset, |
2025 | )); |
2026 | } |
2027 | Some(*idx) |
2028 | } |
2029 | Some(_) => { |
2030 | return Err(BinaryReaderError::new( |
2031 | "canonical option `callback` is specified more than once" , |
2032 | offset, |
2033 | )) |
2034 | } |
2035 | } |
2036 | } |
2037 | } |
2038 | } |
2039 | |
2040 | if async_ && !allow_async { |
2041 | bail!(offset, "async option not allowed here" ) |
2042 | } |
2043 | |
2044 | if callback.is_some() && !async_ { |
2045 | bail!(offset, "cannot specify callback without lifting async" ) |
2046 | } |
2047 | |
2048 | if info.requires_memory && memory.is_none() { |
2049 | return Err(BinaryReaderError::new( |
2050 | "canonical option `memory` is required" , |
2051 | offset, |
2052 | )); |
2053 | } |
2054 | |
2055 | if info.requires_realloc && realloc.is_none() { |
2056 | return Err(BinaryReaderError::new( |
2057 | "canonical option `realloc` is required" , |
2058 | offset, |
2059 | )); |
2060 | } |
2061 | |
2062 | Ok(()) |
2063 | } |
2064 | |
2065 | fn check_type_ref( |
2066 | &mut self, |
2067 | ty: &ComponentTypeRef, |
2068 | features: &WasmFeatures, |
2069 | types: &mut TypeAlloc, |
2070 | offset: usize, |
2071 | ) -> Result<ComponentEntityType> { |
2072 | Ok(match ty { |
2073 | ComponentTypeRef::Module(index) => { |
2074 | let id = self.core_type_at(*index, offset)?; |
2075 | match id { |
2076 | ComponentCoreTypeId::Sub(_) => { |
2077 | bail!(offset, "core type index {index} is not a module type" ) |
2078 | } |
2079 | ComponentCoreTypeId::Module(id) => ComponentEntityType::Module(id), |
2080 | } |
2081 | } |
2082 | ComponentTypeRef::Func(index) => { |
2083 | let id = self.component_type_at(*index, offset)?; |
2084 | match id { |
2085 | ComponentAnyTypeId::Func(id) => ComponentEntityType::Func(id), |
2086 | _ => bail!(offset, "type index {index} is not a function type" ), |
2087 | } |
2088 | } |
2089 | ComponentTypeRef::Value(ty) => { |
2090 | self.check_value_support(features, offset)?; |
2091 | let ty = match ty { |
2092 | crate::ComponentValType::Primitive(ty) => ComponentValType::Primitive(*ty), |
2093 | crate::ComponentValType::Type(index) => { |
2094 | ComponentValType::Type(self.defined_type_at(*index, offset)?) |
2095 | } |
2096 | }; |
2097 | ComponentEntityType::Value(ty) |
2098 | } |
2099 | ComponentTypeRef::Type(TypeBounds::Eq(index)) => { |
2100 | let referenced = self.component_type_at(*index, offset)?; |
2101 | let created = types.with_unique(referenced); |
2102 | ComponentEntityType::Type { |
2103 | referenced, |
2104 | created, |
2105 | } |
2106 | } |
2107 | ComponentTypeRef::Type(TypeBounds::SubResource) => { |
2108 | let id = types.alloc_resource_id(); |
2109 | ComponentEntityType::Type { |
2110 | referenced: id.into(), |
2111 | created: id.into(), |
2112 | } |
2113 | } |
2114 | ComponentTypeRef::Instance(index) => { |
2115 | let id = self.component_type_at(*index, offset)?; |
2116 | match id { |
2117 | ComponentAnyTypeId::Instance(id) => ComponentEntityType::Instance(id), |
2118 | _ => bail!(offset, "type index {index} is not an instance type" ), |
2119 | } |
2120 | } |
2121 | ComponentTypeRef::Component(index) => { |
2122 | let id = self.component_type_at(*index, offset)?; |
2123 | match id { |
2124 | ComponentAnyTypeId::Component(id) => ComponentEntityType::Component(id), |
2125 | _ => bail!(offset, "type index {index} is not a component type" ), |
2126 | } |
2127 | } |
2128 | }) |
2129 | } |
2130 | |
2131 | pub fn export_to_entity_type( |
2132 | &mut self, |
2133 | export: &crate::ComponentExport, |
2134 | features: &WasmFeatures, |
2135 | types: &mut TypeAlloc, |
2136 | offset: usize, |
2137 | ) -> Result<ComponentEntityType> { |
2138 | let actual = match export.kind { |
2139 | ComponentExternalKind::Module => { |
2140 | ComponentEntityType::Module(self.module_at(export.index, offset)?) |
2141 | } |
2142 | ComponentExternalKind::Func => { |
2143 | ComponentEntityType::Func(self.function_at(export.index, offset)?) |
2144 | } |
2145 | ComponentExternalKind::Value => { |
2146 | self.check_value_support(features, offset)?; |
2147 | ComponentEntityType::Value(*self.value_at(export.index, offset)?) |
2148 | } |
2149 | ComponentExternalKind::Type => { |
2150 | let referenced = self.component_type_at(export.index, offset)?; |
2151 | let created = types.with_unique(referenced); |
2152 | ComponentEntityType::Type { |
2153 | referenced, |
2154 | created, |
2155 | } |
2156 | } |
2157 | ComponentExternalKind::Instance => { |
2158 | ComponentEntityType::Instance(self.instance_at(export.index, offset)?) |
2159 | } |
2160 | ComponentExternalKind::Component => { |
2161 | ComponentEntityType::Component(self.component_at(export.index, offset)?) |
2162 | } |
2163 | }; |
2164 | |
2165 | let ascribed = match &export.ty { |
2166 | Some(ty) => self.check_type_ref(ty, features, types, offset)?, |
2167 | None => return Ok(actual), |
2168 | }; |
2169 | |
2170 | SubtypeCx::new(types, types) |
2171 | .component_entity_type(&actual, &ascribed, offset) |
2172 | .with_context(|| "ascribed type of export is not compatible with item's type" )?; |
2173 | |
2174 | Ok(ascribed) |
2175 | } |
2176 | |
2177 | fn create_module_type( |
2178 | components: &[Self], |
2179 | decls: Vec<crate::ModuleTypeDeclaration>, |
2180 | features: &WasmFeatures, |
2181 | types: &mut TypeAlloc, |
2182 | offset: usize, |
2183 | ) -> Result<ModuleType> { |
2184 | let mut state = Module::default(); |
2185 | |
2186 | for decl in decls { |
2187 | match decl { |
2188 | crate::ModuleTypeDeclaration::Type(rec) => { |
2189 | state.add_types(rec, features, types, offset, true)?; |
2190 | } |
2191 | crate::ModuleTypeDeclaration::Export { name, mut ty } => { |
2192 | let ty = state.check_type_ref(&mut ty, features, types, offset)?; |
2193 | state.add_export(name, ty, features, offset, true, types)?; |
2194 | } |
2195 | crate::ModuleTypeDeclaration::OuterAlias { kind, count, index } => { |
2196 | match kind { |
2197 | crate::OuterAliasKind::Type => { |
2198 | let ty = if count == 0 { |
2199 | // Local alias, check the local module state |
2200 | ComponentCoreTypeId::Sub(state.type_id_at(index, offset)?) |
2201 | } else { |
2202 | // Otherwise, check the enclosing component state |
2203 | let component = |
2204 | Self::check_alias_count(components, count - 1, offset)?; |
2205 | component.core_type_at(index, offset)? |
2206 | }; |
2207 | |
2208 | check_max(state.types.len(), 1, MAX_WASM_TYPES, "types" , offset)?; |
2209 | |
2210 | match ty { |
2211 | ComponentCoreTypeId::Sub(ty) => state.types.push(ty), |
2212 | // TODO https://github.com/WebAssembly/component-model/issues/265 |
2213 | ComponentCoreTypeId::Module(_) => bail!( |
2214 | offset, |
2215 | "not implemented: aliasing core module types into a core \ |
2216 | module's types index space" |
2217 | ), |
2218 | } |
2219 | } |
2220 | } |
2221 | } |
2222 | crate::ModuleTypeDeclaration::Import(import) => { |
2223 | state.add_import(import, features, types, offset)?; |
2224 | } |
2225 | } |
2226 | } |
2227 | |
2228 | let imports = state.imports_for_module_type(offset)?; |
2229 | |
2230 | Ok(ModuleType { |
2231 | info: TypeInfo::core(state.type_size), |
2232 | imports, |
2233 | exports: state.exports, |
2234 | }) |
2235 | } |
2236 | |
2237 | fn create_component_type( |
2238 | components: &mut Vec<Self>, |
2239 | decls: Vec<crate::ComponentTypeDeclaration>, |
2240 | features: &WasmFeatures, |
2241 | types: &mut TypeAlloc, |
2242 | offset: usize, |
2243 | ) -> Result<ComponentType> { |
2244 | components.push(ComponentState::new(ComponentKind::ComponentType)); |
2245 | |
2246 | for decl in decls { |
2247 | match decl { |
2248 | crate::ComponentTypeDeclaration::CoreType(ty) => { |
2249 | Self::add_core_type(components, ty, features, types, offset, true)?; |
2250 | } |
2251 | crate::ComponentTypeDeclaration::Type(ty) => { |
2252 | Self::add_type(components, ty, features, types, offset, true)?; |
2253 | } |
2254 | crate::ComponentTypeDeclaration::Export { name, ty } => { |
2255 | let current = components.last_mut().unwrap(); |
2256 | let ty = current.check_type_ref(&ty, features, types, offset)?; |
2257 | current.add_export(name, ty, features, types, offset, true)?; |
2258 | } |
2259 | crate::ComponentTypeDeclaration::Import(import) => { |
2260 | components |
2261 | .last_mut() |
2262 | .unwrap() |
2263 | .add_import(import, features, types, offset)?; |
2264 | } |
2265 | crate::ComponentTypeDeclaration::Alias(alias) => { |
2266 | Self::add_alias(components, alias, features, types, offset)?; |
2267 | } |
2268 | }; |
2269 | } |
2270 | |
2271 | components.pop().unwrap().finish(types, offset) |
2272 | } |
2273 | |
2274 | fn create_instance_type( |
2275 | components: &mut Vec<Self>, |
2276 | decls: Vec<crate::InstanceTypeDeclaration>, |
2277 | features: &WasmFeatures, |
2278 | types: &mut TypeAlloc, |
2279 | offset: usize, |
2280 | ) -> Result<ComponentInstanceType> { |
2281 | components.push(ComponentState::new(ComponentKind::InstanceType)); |
2282 | |
2283 | for decl in decls { |
2284 | match decl { |
2285 | crate::InstanceTypeDeclaration::CoreType(ty) => { |
2286 | Self::add_core_type(components, ty, features, types, offset, true)?; |
2287 | } |
2288 | crate::InstanceTypeDeclaration::Type(ty) => { |
2289 | Self::add_type(components, ty, features, types, offset, true)?; |
2290 | } |
2291 | crate::InstanceTypeDeclaration::Export { name, ty } => { |
2292 | let current = components.last_mut().unwrap(); |
2293 | let ty = current.check_type_ref(&ty, features, types, offset)?; |
2294 | current.add_export(name, ty, features, types, offset, true)?; |
2295 | } |
2296 | crate::InstanceTypeDeclaration::Alias(alias) => { |
2297 | Self::add_alias(components, alias, features, types, offset)?; |
2298 | } |
2299 | }; |
2300 | } |
2301 | |
2302 | let mut state = components.pop().unwrap(); |
2303 | |
2304 | assert!(state.imported_resources.is_empty()); |
2305 | |
2306 | Ok(ComponentInstanceType { |
2307 | info: state.type_info, |
2308 | |
2309 | // The defined resources for this instance type are those listed on |
2310 | // the component state. The path to each defined resource is |
2311 | // guaranteed to live within the `explicit_resources` map since, |
2312 | // when in the type context, the introduction of any defined |
2313 | // resource must have been done with `(export "x" (type (sub |
2314 | // resource)))` which, in a sense, "fuses" the introduction of the |
2315 | // variable with the export. This means that all defined resources, |
2316 | // if any, should be guaranteed to have an `explicit_resources` path |
2317 | // listed. |
2318 | defined_resources: mem::take(&mut state.defined_resources) |
2319 | .into_iter() |
2320 | .map(|(id, rep)| { |
2321 | assert!(rep.is_none()); |
2322 | id |
2323 | }) |
2324 | .collect(), |
2325 | |
2326 | // The map of what resources are explicitly exported and where |
2327 | // they're exported is plumbed through as-is. |
2328 | explicit_resources: mem::take(&mut state.explicit_resources), |
2329 | |
2330 | exports: mem::take(&mut state.exports), |
2331 | }) |
2332 | } |
2333 | |
2334 | fn create_function_type( |
2335 | &self, |
2336 | ty: crate::ComponentFuncType, |
2337 | types: &TypeList, |
2338 | features: &WasmFeatures, |
2339 | offset: usize, |
2340 | ) -> Result<ComponentFuncType> { |
2341 | let mut info = TypeInfo::new(); |
2342 | |
2343 | if ty.results.type_count() > 1 && !features.component_model_multiple_returns() { |
2344 | bail!( |
2345 | offset, |
2346 | "multiple returns on a function is now a gated feature \ |
2347 | -- https://github.com/WebAssembly/component-model/pull/368" |
2348 | ); |
2349 | } |
2350 | |
2351 | let mut set = Set::default(); |
2352 | set.reserve(core::cmp::max(ty.params.len(), ty.results.type_count())); |
2353 | |
2354 | let params = ty |
2355 | .params |
2356 | .iter() |
2357 | .map(|(name, ty)| { |
2358 | let name: &KebabStr = to_kebab_str(name, "function parameter" , offset)?; |
2359 | if !set.insert(name) { |
2360 | bail!( |
2361 | offset, |
2362 | "function parameter name ` {name}` conflicts with previous parameter name ` {prev}`" , |
2363 | prev = set.get(&name).unwrap(), |
2364 | ); |
2365 | } |
2366 | |
2367 | let ty = self.create_component_val_type(*ty, offset)?; |
2368 | info.combine(ty.info(types), offset)?; |
2369 | Ok((name.to_owned(), ty)) |
2370 | }) |
2371 | .collect::<Result<_>>()?; |
2372 | |
2373 | set.clear(); |
2374 | |
2375 | let results = ty |
2376 | .results |
2377 | .iter() |
2378 | .map(|(name, ty)| { |
2379 | let name = name |
2380 | .map(|name| { |
2381 | let name = to_kebab_str(name, "function result" , offset)?; |
2382 | if !set.insert(name) { |
2383 | bail!( |
2384 | offset, |
2385 | "function result name ` {name}` conflicts with previous result name ` {prev}`" , |
2386 | prev = set.get(name).unwrap(), |
2387 | ); |
2388 | } |
2389 | |
2390 | Ok(name.to_owned()) |
2391 | }) |
2392 | .transpose()?; |
2393 | |
2394 | let ty = self.create_component_val_type(*ty, offset)?; |
2395 | let ty_info = ty.info(types); |
2396 | if ty_info.contains_borrow() { |
2397 | bail!(offset, "function result cannot contain a `borrow` type" ); |
2398 | } |
2399 | info.combine(ty.info(types), offset)?; |
2400 | Ok((name, ty)) |
2401 | }) |
2402 | .collect::<Result<_>>()?; |
2403 | |
2404 | Ok(ComponentFuncType { |
2405 | info, |
2406 | params, |
2407 | results, |
2408 | }) |
2409 | } |
2410 | |
2411 | fn instantiate_core_module( |
2412 | &self, |
2413 | module_index: u32, |
2414 | module_args: Vec<crate::InstantiationArg>, |
2415 | types: &mut TypeAlloc, |
2416 | offset: usize, |
2417 | ) -> Result<ComponentCoreInstanceTypeId> { |
2418 | fn insert_arg<'a>( |
2419 | name: &'a str, |
2420 | arg: &'a InstanceType, |
2421 | args: &mut IndexMap<&'a str, &'a InstanceType>, |
2422 | offset: usize, |
2423 | ) -> Result<()> { |
2424 | if args.insert(name, arg).is_some() { |
2425 | bail!( |
2426 | offset, |
2427 | "duplicate module instantiation argument named ` {name}`" |
2428 | ); |
2429 | } |
2430 | |
2431 | Ok(()) |
2432 | } |
2433 | |
2434 | let module_type_id = self.module_at(module_index, offset)?; |
2435 | let mut args = IndexMap::default(); |
2436 | |
2437 | // Populate the arguments |
2438 | for module_arg in module_args { |
2439 | match module_arg.kind { |
2440 | InstantiationArgKind::Instance => { |
2441 | let instance_type = &types[self.core_instance_at(module_arg.index, offset)?]; |
2442 | insert_arg(module_arg.name, instance_type, &mut args, offset)?; |
2443 | } |
2444 | } |
2445 | } |
2446 | |
2447 | // Validate the arguments |
2448 | let module_type = &types[module_type_id]; |
2449 | let cx = SubtypeCx::new(types, types); |
2450 | for ((module, name), expected) in module_type.imports.iter() { |
2451 | let instance = args.get(module.as_str()).ok_or_else(|| { |
2452 | format_err!( |
2453 | offset, |
2454 | "missing module instantiation argument named ` {module}`" |
2455 | ) |
2456 | })?; |
2457 | |
2458 | let arg = instance |
2459 | .internal_exports(types) |
2460 | .get(name.as_str()) |
2461 | .ok_or_else(|| { |
2462 | format_err!( |
2463 | offset, |
2464 | "module instantiation argument ` {module}` does not \ |
2465 | export an item named ` {name}`" , |
2466 | ) |
2467 | })?; |
2468 | |
2469 | cx.entity_type(arg, expected, offset).with_context(|| { |
2470 | format!( |
2471 | "type mismatch for export ` {name}` of module \ |
2472 | instantiation argument ` {module}`" |
2473 | ) |
2474 | })?; |
2475 | } |
2476 | |
2477 | let mut info = TypeInfo::new(); |
2478 | for (_, ty) in module_type.exports.iter() { |
2479 | info.combine(ty.info(types), offset)?; |
2480 | } |
2481 | |
2482 | Ok(types.push_ty(InstanceType { |
2483 | info, |
2484 | kind: CoreInstanceTypeKind::Instantiated(module_type_id), |
2485 | })) |
2486 | } |
2487 | |
2488 | fn instantiate_component( |
2489 | &mut self, |
2490 | component_index: u32, |
2491 | component_args: Vec<crate::ComponentInstantiationArg>, |
2492 | features: &WasmFeatures, |
2493 | types: &mut TypeAlloc, |
2494 | offset: usize, |
2495 | ) -> Result<ComponentInstanceTypeId> { |
2496 | let component_type_id = self.component_at(component_index, offset)?; |
2497 | let mut args = IndexMap::default(); |
2498 | |
2499 | // Populate the arguments |
2500 | for component_arg in component_args { |
2501 | let ty = match component_arg.kind { |
2502 | ComponentExternalKind::Module => { |
2503 | ComponentEntityType::Module(self.module_at(component_arg.index, offset)?) |
2504 | } |
2505 | ComponentExternalKind::Component => { |
2506 | ComponentEntityType::Component(self.component_at(component_arg.index, offset)?) |
2507 | } |
2508 | ComponentExternalKind::Instance => { |
2509 | ComponentEntityType::Instance(self.instance_at(component_arg.index, offset)?) |
2510 | } |
2511 | ComponentExternalKind::Func => { |
2512 | ComponentEntityType::Func(self.function_at(component_arg.index, offset)?) |
2513 | } |
2514 | ComponentExternalKind::Value => { |
2515 | self.check_value_support(features, offset)?; |
2516 | ComponentEntityType::Value(*self.value_at(component_arg.index, offset)?) |
2517 | } |
2518 | ComponentExternalKind::Type => { |
2519 | let ty = self.component_type_at(component_arg.index, offset)?; |
2520 | ComponentEntityType::Type { |
2521 | referenced: ty, |
2522 | created: ty, |
2523 | } |
2524 | } |
2525 | }; |
2526 | match args.entry(component_arg.name.to_string()) { |
2527 | Entry::Occupied(e) => { |
2528 | bail!( |
2529 | offset, |
2530 | "instantiation argument ` {name}` conflicts with previous argument ` {prev}`" , |
2531 | prev = e.key(), |
2532 | name = component_arg.name |
2533 | ); |
2534 | } |
2535 | Entry::Vacant(e) => { |
2536 | e.insert(ty); |
2537 | } |
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 | let component_type = &types[component_type_id]; |
2621 | let mut exports = component_type.exports.clone(); |
2622 | let mut info = TypeInfo::new(); |
2623 | for (_, ty) in component_type.exports.iter() { |
2624 | 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 | let mut mapping = SubtypeCx::new(types, types).open_instance_type( |
2635 | &args, |
2636 | component_type_id, |
2637 | ExternKind::Import, |
2638 | offset, |
2639 | )?; |
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 | let fresh_defined_resources = (0..component_type.defined_resources.len()) |
2663 | .map(|_| types.alloc_resource_id().resource()) |
2664 | .collect::<IndexSet<_>>(); |
2665 | let component_type = &types[component_type_id]; |
2666 | for ((old, _path), new) in component_type |
2667 | .defined_resources |
2668 | .iter() |
2669 | .zip(&fresh_defined_resources) |
2670 | { |
2671 | let prev = mapping.resources.insert(*old, *new); |
2672 | 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 | for entity in exports.values_mut() { |
2686 | types.remap_component_entity(entity, &mut mapping); |
2687 | } |
2688 | let component_type = &types[component_type_id]; |
2689 | let explicit_resources = component_type |
2690 | .explicit_resources |
2691 | .iter() |
2692 | .map(|(id, path)| { |
2693 | ( |
2694 | mapping.resources.get(id).copied().unwrap_or(*id), |
2695 | path.clone(), |
2696 | ) |
2697 | }) |
2698 | .collect::<IndexMap<_, _>>(); |
2699 | |
2700 | // Technically in the last formalism that was consulted in writing this |
2701 | // implementation there are two further steps that are part of the |
2702 | // instantiation process: |
2703 | // |
2704 | // 1. The set of defined resources from the instance created, which are |
2705 | // added to the outer component, is the subset of the instance's |
2706 | // original defined resources and the free variables of the exports. |
2707 | // |
2708 | // 2. Each element of this subset is required to be "explicit in" the |
2709 | // instance, or otherwise explicitly exported somewhere within the |
2710 | // instance. |
2711 | // |
2712 | // With the syntactic structure of the component model, however, neither |
2713 | // of these conditions should be necessary. The main reason for this is |
2714 | // that this function is specifically dealing with instantiation of |
2715 | // components which should already have these properties validated |
2716 | // about them. Subsequently we shouldn't have to re-check them. |
2717 | // |
2718 | // In debug mode, however, do a sanity check. |
2719 | if cfg!(debug_assertions) { |
2720 | let mut free = IndexSet::default(); |
2721 | for ty in exports.values() { |
2722 | types.free_variables_component_entity(ty, &mut free); |
2723 | } |
2724 | assert!(fresh_defined_resources.is_subset(&free)); |
2725 | for resource in fresh_defined_resources.iter() { |
2726 | assert!(explicit_resources.contains_key(resource)); |
2727 | } |
2728 | } |
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 | for resource in fresh_defined_resources { |
2746 | self.defined_resources.insert(resource, None); |
2747 | } |
2748 | |
2749 | Ok(types.push_ty(ComponentInstanceType { |
2750 | info, |
2751 | defined_resources: Default::default(), |
2752 | explicit_resources, |
2753 | exports, |
2754 | })) |
2755 | } |
2756 | |
2757 | fn instantiate_component_exports( |
2758 | &mut self, |
2759 | exports: Vec<crate::ComponentExport>, |
2760 | features: &WasmFeatures, |
2761 | types: &mut TypeAlloc, |
2762 | offset: usize, |
2763 | ) -> Result<ComponentInstanceTypeId> { |
2764 | let mut info = TypeInfo::new(); |
2765 | let mut inst_exports = IndexMap::default(); |
2766 | let mut explicit_resources = IndexMap::default(); |
2767 | let mut export_names = IndexSet::default(); |
2768 | |
2769 | // NB: It's intentional that this context is empty since no indices are |
2770 | // introduced in the bag-of-exports construct which means there's no |
2771 | // way syntactically to register something inside of this. |
2772 | let names = ComponentNameContext::default(); |
2773 | |
2774 | for export in exports { |
2775 | assert!(export.ty.is_none()); |
2776 | let ty = match export.kind { |
2777 | ComponentExternalKind::Module => { |
2778 | ComponentEntityType::Module(self.module_at(export.index, offset)?) |
2779 | } |
2780 | ComponentExternalKind::Component => { |
2781 | ComponentEntityType::Component(self.component_at(export.index, offset)?) |
2782 | } |
2783 | ComponentExternalKind::Instance => { |
2784 | 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 | explicit_resources.extend(types[ty].explicit_resources.iter().map( |
2791 | |(id, path)| { |
2792 | let mut new_path = vec![inst_exports.len()]; |
2793 | new_path.extend(path); |
2794 | (*id, new_path) |
2795 | }, |
2796 | )); |
2797 | ComponentEntityType::Instance(ty) |
2798 | } |
2799 | ComponentExternalKind::Func => { |
2800 | ComponentEntityType::Func(self.function_at(export.index, offset)?) |
2801 | } |
2802 | ComponentExternalKind::Value => { |
2803 | self.check_value_support(features, offset)?; |
2804 | ComponentEntityType::Value(*self.value_at(export.index, offset)?) |
2805 | } |
2806 | ComponentExternalKind::Type => { |
2807 | 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 | if let ComponentAnyTypeId::Resource(id) = ty { |
2813 | explicit_resources.insert(id.resource(), vec![inst_exports.len()]); |
2814 | } |
2815 | ComponentEntityType::Type { |
2816 | referenced: ty, |
2817 | // The created type index here isn't used anywhere |
2818 | // in index spaces because a "bag of exports" |
2819 | // doesn't build up its own index spaces. Just fill |
2820 | // in the same index here in this case as what's |
2821 | // referenced. |
2822 | created: ty, |
2823 | } |
2824 | } |
2825 | }; |
2826 | |
2827 | names.validate_extern( |
2828 | export.name.0, |
2829 | ExternKind::Export, |
2830 | &ty, |
2831 | types, |
2832 | offset, |
2833 | &mut export_names, |
2834 | &mut inst_exports, |
2835 | &mut info, |
2836 | features, |
2837 | )?; |
2838 | } |
2839 | |
2840 | Ok(types.push_ty(ComponentInstanceType { |
2841 | info, |
2842 | explicit_resources, |
2843 | exports: inst_exports, |
2844 | |
2845 | // NB: the list of defined resources for this instance itself |
2846 | // is always empty. Even if this instance exports resources, |
2847 | // it's empty. |
2848 | // |
2849 | // The reason for this is a bit subtle. The general idea, though, is |
2850 | // that the defined resources list here is only used for instance |
2851 | // types that are sort of "floating around" and haven't actually |
2852 | // been attached to something yet. For example when an instance type |
2853 | // is simply declared it can have defined resources introduced |
2854 | // through `(export "name" (type (sub resource)))`. These |
2855 | // definitions, however, are local to the instance itself and aren't |
2856 | // defined elsewhere. |
2857 | // |
2858 | // Here, though, no new definitions were introduced. The instance |
2859 | // created here is a "bag of exports" which could only refer to |
2860 | // preexisting items. This means that inherently no new resources |
2861 | // were created so there's nothing to put in this list. Any |
2862 | // resources referenced by the instance must be bound by the outer |
2863 | // component context or further above. |
2864 | // |
2865 | // Furthermore, however, actual instances of instances, which this |
2866 | // is, aren't allowed to have defined resources. Instead the |
2867 | // resources would have to be injected into the outer component |
2868 | // enclosing the instance. That means that even if bag-of-exports |
2869 | // could declare a new resource then the resource would be moved |
2870 | // from here to `self.defined_resources`. This doesn't exist at this |
2871 | // time, though, so this still remains empty and |
2872 | // `self.defined_resources` remains unperturbed. |
2873 | defined_resources: Default::default(), |
2874 | })) |
2875 | } |
2876 | |
2877 | fn instantiate_core_exports( |
2878 | &mut self, |
2879 | exports: Vec<crate::Export>, |
2880 | types: &mut TypeAlloc, |
2881 | offset: usize, |
2882 | ) -> Result<ComponentCoreInstanceTypeId> { |
2883 | fn insert_export( |
2884 | types: &TypeList, |
2885 | name: &str, |
2886 | export: EntityType, |
2887 | exports: &mut IndexMap<String, EntityType>, |
2888 | info: &mut TypeInfo, |
2889 | offset: usize, |
2890 | ) -> Result<()> { |
2891 | info.combine(export.info(types), offset)?; |
2892 | |
2893 | if exports.insert(name.to_string(), export).is_some() { |
2894 | bail!( |
2895 | offset, |
2896 | "duplicate instantiation export name ` {name}` already defined" , |
2897 | ) |
2898 | } |
2899 | |
2900 | Ok(()) |
2901 | } |
2902 | |
2903 | let mut info = TypeInfo::new(); |
2904 | let mut inst_exports = IndexMap::default(); |
2905 | for export in exports { |
2906 | match export.kind { |
2907 | ExternalKind::Func => { |
2908 | insert_export( |
2909 | types, |
2910 | export.name, |
2911 | EntityType::Func(self.core_function_at(export.index, offset)?), |
2912 | &mut inst_exports, |
2913 | &mut info, |
2914 | offset, |
2915 | )?; |
2916 | } |
2917 | ExternalKind::Table => insert_export( |
2918 | types, |
2919 | export.name, |
2920 | EntityType::Table(*self.table_at(export.index, offset)?), |
2921 | &mut inst_exports, |
2922 | &mut info, |
2923 | offset, |
2924 | )?, |
2925 | ExternalKind::Memory => insert_export( |
2926 | types, |
2927 | export.name, |
2928 | EntityType::Memory(*self.memory_at(export.index, offset)?), |
2929 | &mut inst_exports, |
2930 | &mut info, |
2931 | offset, |
2932 | )?, |
2933 | ExternalKind::Global => { |
2934 | insert_export( |
2935 | types, |
2936 | export.name, |
2937 | EntityType::Global(*self.global_at(export.index, offset)?), |
2938 | &mut inst_exports, |
2939 | &mut info, |
2940 | offset, |
2941 | )?; |
2942 | } |
2943 | ExternalKind::Tag => insert_export( |
2944 | types, |
2945 | export.name, |
2946 | EntityType::Tag(self.core_function_at(export.index, offset)?), |
2947 | &mut inst_exports, |
2948 | &mut info, |
2949 | offset, |
2950 | )?, |
2951 | } |
2952 | } |
2953 | |
2954 | Ok(types.push_ty(InstanceType { |
2955 | info, |
2956 | kind: CoreInstanceTypeKind::Exports(inst_exports), |
2957 | })) |
2958 | } |
2959 | |
2960 | fn alias_core_instance_export( |
2961 | &mut self, |
2962 | instance_index: u32, |
2963 | kind: ExternalKind, |
2964 | name: &str, |
2965 | types: &TypeList, |
2966 | offset: usize, |
2967 | ) -> 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 | match kind { |
2986 | ExternalKind::Func => { |
2987 | check_max( |
2988 | self.function_count(), |
2989 | 1, |
2990 | MAX_WASM_FUNCTIONS, |
2991 | "functions" , |
2992 | offset, |
2993 | )?; |
2994 | push_module_export!(EntityType::Func, core_funcs, "function" ); |
2995 | } |
2996 | ExternalKind::Table => { |
2997 | check_max( |
2998 | self.core_tables.len(), |
2999 | 1, |
3000 | MAX_CORE_INDEX_SPACE_ITEMS, |
3001 | "tables" , |
3002 | offset, |
3003 | )?; |
3004 | push_module_export!(EntityType::Table, core_tables, "table" ); |
3005 | |
3006 | let ty = self.core_tables.last().unwrap(); |
3007 | if ty.table64 { |
3008 | bail!( |
3009 | offset, |
3010 | "64-bit tables are not compatible with components yet" |
3011 | ); |
3012 | } |
3013 | if ty.shared { |
3014 | bail!( |
3015 | offset, |
3016 | "shared tables are not compatible with components yet" |
3017 | ); |
3018 | } |
3019 | } |
3020 | ExternalKind::Memory => { |
3021 | check_max( |
3022 | self.core_memories.len(), |
3023 | 1, |
3024 | MAX_CORE_INDEX_SPACE_ITEMS, |
3025 | "memories" , |
3026 | offset, |
3027 | )?; |
3028 | push_module_export!(EntityType::Memory, core_memories, "memory" ); |
3029 | |
3030 | let ty = self.core_memories.last().unwrap(); |
3031 | if ty.memory64 { |
3032 | bail!( |
3033 | offset, |
3034 | "64-bit linear memories are not compatible with components yet" |
3035 | ); |
3036 | } |
3037 | if ty.shared { |
3038 | bail!( |
3039 | offset, |
3040 | "shared linear memories are not compatible with components yet" |
3041 | ); |
3042 | } |
3043 | } |
3044 | ExternalKind::Global => { |
3045 | check_max( |
3046 | self.core_globals.len(), |
3047 | 1, |
3048 | MAX_CORE_INDEX_SPACE_ITEMS, |
3049 | "globals" , |
3050 | offset, |
3051 | )?; |
3052 | push_module_export!(EntityType::Global, core_globals, "global" ); |
3053 | } |
3054 | ExternalKind::Tag => { |
3055 | check_max( |
3056 | self.core_tags.len(), |
3057 | 1, |
3058 | MAX_CORE_INDEX_SPACE_ITEMS, |
3059 | "tags" , |
3060 | offset, |
3061 | )?; |
3062 | push_module_export!(EntityType::Tag, core_tags, "tag" ); |
3063 | } |
3064 | } |
3065 | |
3066 | Ok(()) |
3067 | } |
3068 | |
3069 | fn alias_instance_export( |
3070 | &mut self, |
3071 | instance_index: u32, |
3072 | kind: ComponentExternalKind, |
3073 | name: &str, |
3074 | features: &WasmFeatures, |
3075 | types: &mut TypeAlloc, |
3076 | offset: usize, |
3077 | ) -> Result<()> { |
3078 | if let ComponentExternalKind::Value = kind { |
3079 | self.check_value_support(features, offset)?; |
3080 | } |
3081 | let mut ty = match types[self.instance_at(instance_index, offset)?] |
3082 | .exports |
3083 | .get(name) |
3084 | { |
3085 | Some(ty) => *ty, |
3086 | None => bail!( |
3087 | offset, |
3088 | "instance {instance_index} has no export named ` {name}`" |
3089 | ), |
3090 | }; |
3091 | |
3092 | let ok = match (&ty, kind) { |
3093 | (ComponentEntityType::Module(_), ComponentExternalKind::Module) => true, |
3094 | (ComponentEntityType::Module(_), _) => false, |
3095 | (ComponentEntityType::Component(_), ComponentExternalKind::Component) => true, |
3096 | (ComponentEntityType::Component(_), _) => false, |
3097 | (ComponentEntityType::Func(_), ComponentExternalKind::Func) => true, |
3098 | (ComponentEntityType::Func(_), _) => false, |
3099 | (ComponentEntityType::Instance(_), ComponentExternalKind::Instance) => true, |
3100 | (ComponentEntityType::Instance(_), _) => false, |
3101 | (ComponentEntityType::Value(_), ComponentExternalKind::Value) => true, |
3102 | (ComponentEntityType::Value(_), _) => false, |
3103 | (ComponentEntityType::Type { .. }, ComponentExternalKind::Type) => true, |
3104 | (ComponentEntityType::Type { .. }, _) => false, |
3105 | }; |
3106 | if !ok { |
3107 | bail!( |
3108 | offset, |
3109 | "export ` {name}` for instance {instance_index} is not a {}" , |
3110 | kind.desc(), |
3111 | ); |
3112 | } |
3113 | |
3114 | self.add_entity(&mut ty, None, features, types, offset)?; |
3115 | Ok(()) |
3116 | } |
3117 | |
3118 | fn alias_module(components: &mut [Self], count: u32, index: u32, offset: usize) -> Result<()> { |
3119 | let component = Self::check_alias_count(components, count, offset)?; |
3120 | let ty = component.module_at(index, offset)?; |
3121 | |
3122 | let current = components.last_mut().unwrap(); |
3123 | check_max( |
3124 | current.core_modules.len(), |
3125 | 1, |
3126 | MAX_WASM_MODULES, |
3127 | "modules" , |
3128 | offset, |
3129 | )?; |
3130 | |
3131 | current.core_modules.push(ty); |
3132 | Ok(()) |
3133 | } |
3134 | |
3135 | fn alias_component( |
3136 | components: &mut [Self], |
3137 | count: u32, |
3138 | index: u32, |
3139 | offset: usize, |
3140 | ) -> Result<()> { |
3141 | let component = Self::check_alias_count(components, count, offset)?; |
3142 | let ty = component.component_at(index, offset)?; |
3143 | |
3144 | let current = components.last_mut().unwrap(); |
3145 | check_max( |
3146 | current.components.len(), |
3147 | 1, |
3148 | MAX_WASM_COMPONENTS, |
3149 | "components" , |
3150 | offset, |
3151 | )?; |
3152 | |
3153 | current.components.push(ty); |
3154 | Ok(()) |
3155 | } |
3156 | |
3157 | fn alias_core_type( |
3158 | components: &mut [Self], |
3159 | count: u32, |
3160 | index: u32, |
3161 | offset: usize, |
3162 | ) -> Result<()> { |
3163 | let component = Self::check_alias_count(components, count, offset)?; |
3164 | let ty = component.core_type_at(index, offset)?; |
3165 | |
3166 | let current = components.last_mut().unwrap(); |
3167 | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types" , offset)?; |
3168 | |
3169 | current.core_types.push(ty); |
3170 | |
3171 | Ok(()) |
3172 | } |
3173 | |
3174 | fn alias_type( |
3175 | components: &mut [Self], |
3176 | count: u32, |
3177 | index: u32, |
3178 | types: &mut TypeAlloc, |
3179 | offset: usize, |
3180 | ) -> Result<()> { |
3181 | let component = Self::check_alias_count(components, count, offset)?; |
3182 | 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 | let pos_after_component = components.len() - (count as usize); |
3207 | if let Some(component) = components.get(pos_after_component) { |
3208 | if component.kind == ComponentKind::Component { |
3209 | let mut free = IndexSet::default(); |
3210 | types.free_variables_any_type_id(ty, &mut free); |
3211 | if !free.is_empty() { |
3212 | bail!( |
3213 | offset, |
3214 | "cannot alias outer type which transitively refers \ |
3215 | to resources not defined in the current component" |
3216 | ); |
3217 | } |
3218 | } |
3219 | } |
3220 | |
3221 | let current = components.last_mut().unwrap(); |
3222 | check_max(current.type_count(), 1, MAX_WASM_TYPES, "types" , offset)?; |
3223 | |
3224 | current.types.push(ty); |
3225 | |
3226 | Ok(()) |
3227 | } |
3228 | |
3229 | fn check_alias_count(components: &[Self], count: u32, offset: usize) -> Result<&Self> { |
3230 | let count = count as usize; |
3231 | if count >= components.len() { |
3232 | bail!(offset, "invalid outer alias count of {count}" ); |
3233 | } |
3234 | |
3235 | Ok(&components[components.len() - count - 1]) |
3236 | } |
3237 | |
3238 | fn create_defined_type( |
3239 | &self, |
3240 | ty: crate::ComponentDefinedType, |
3241 | types: &TypeList, |
3242 | features: &WasmFeatures, |
3243 | offset: usize, |
3244 | ) -> Result<ComponentDefinedType> { |
3245 | match ty { |
3246 | crate::ComponentDefinedType::Primitive(ty) => Ok(ComponentDefinedType::Primitive(ty)), |
3247 | crate::ComponentDefinedType::Record(fields) => { |
3248 | self.create_record_type(fields.as_ref(), types, offset) |
3249 | } |
3250 | crate::ComponentDefinedType::Variant(cases) => { |
3251 | self.create_variant_type(cases.as_ref(), types, offset) |
3252 | } |
3253 | crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List( |
3254 | self.create_component_val_type(ty, offset)?, |
3255 | )), |
3256 | crate::ComponentDefinedType::Tuple(tys) => { |
3257 | self.create_tuple_type(tys.as_ref(), types, offset) |
3258 | } |
3259 | crate::ComponentDefinedType::Flags(names) => { |
3260 | self.create_flags_type(names.as_ref(), features, offset) |
3261 | } |
3262 | crate::ComponentDefinedType::Enum(cases) => { |
3263 | self.create_enum_type(cases.as_ref(), offset) |
3264 | } |
3265 | crate::ComponentDefinedType::Option(ty) => Ok(ComponentDefinedType::Option( |
3266 | self.create_component_val_type(ty, offset)?, |
3267 | )), |
3268 | crate::ComponentDefinedType::Result { ok, err } => Ok(ComponentDefinedType::Result { |
3269 | ok: ok |
3270 | .map(|ty| self.create_component_val_type(ty, offset)) |
3271 | .transpose()?, |
3272 | err: err |
3273 | .map(|ty| self.create_component_val_type(ty, offset)) |
3274 | .transpose()?, |
3275 | }), |
3276 | crate::ComponentDefinedType::Own(idx) => Ok(ComponentDefinedType::Own( |
3277 | self.resource_at(idx, types, offset)?, |
3278 | )), |
3279 | crate::ComponentDefinedType::Borrow(idx) => Ok(ComponentDefinedType::Borrow( |
3280 | self.resource_at(idx, types, offset)?, |
3281 | )), |
3282 | crate::ComponentDefinedType::Future(ty) => Ok(ComponentDefinedType::Future( |
3283 | ty.map(|ty| self.create_component_val_type(ty, offset)) |
3284 | .transpose()?, |
3285 | )), |
3286 | crate::ComponentDefinedType::Stream(ty) => Ok(ComponentDefinedType::Stream( |
3287 | self.create_component_val_type(ty, offset)?, |
3288 | )), |
3289 | crate::ComponentDefinedType::ErrorContext => Ok(ComponentDefinedType::ErrorContext), |
3290 | } |
3291 | } |
3292 | |
3293 | fn create_record_type( |
3294 | &self, |
3295 | fields: &[(&str, crate::ComponentValType)], |
3296 | types: &TypeList, |
3297 | offset: usize, |
3298 | ) -> Result<ComponentDefinedType> { |
3299 | let mut info = TypeInfo::new(); |
3300 | let mut field_map = IndexMap::default(); |
3301 | field_map.reserve(fields.len()); |
3302 | |
3303 | if fields.is_empty() { |
3304 | bail!(offset, "record type must have at least one field" ); |
3305 | } |
3306 | |
3307 | for (name, ty) in fields { |
3308 | let name = to_kebab_str(name, "record field" , offset)?; |
3309 | let ty = self.create_component_val_type(*ty, offset)?; |
3310 | |
3311 | match field_map.entry(name.to_owned()) { |
3312 | Entry::Occupied(e) => bail!( |
3313 | offset, |
3314 | "record field name ` {name}` conflicts with previous field name ` {prev}`" , |
3315 | prev = e.key() |
3316 | ), |
3317 | Entry::Vacant(e) => { |
3318 | info.combine(ty.info(types), offset)?; |
3319 | e.insert(ty); |
3320 | } |
3321 | } |
3322 | } |
3323 | |
3324 | Ok(ComponentDefinedType::Record(RecordType { |
3325 | info, |
3326 | fields: field_map, |
3327 | })) |
3328 | } |
3329 | |
3330 | fn create_variant_type( |
3331 | &self, |
3332 | cases: &[crate::VariantCase], |
3333 | types: &TypeList, |
3334 | offset: usize, |
3335 | ) -> Result<ComponentDefinedType> { |
3336 | let mut info = TypeInfo::new(); |
3337 | let mut case_map: IndexMap<KebabString, VariantCase> = IndexMap::default(); |
3338 | case_map.reserve(cases.len()); |
3339 | |
3340 | if cases.is_empty() { |
3341 | bail!(offset, "variant type must have at least one case" ); |
3342 | } |
3343 | |
3344 | if cases.len() > u32::MAX as usize { |
3345 | return Err(BinaryReaderError::new( |
3346 | "variant type cannot be represented with a 32-bit discriminant value" , |
3347 | offset, |
3348 | )); |
3349 | } |
3350 | |
3351 | for (i, case) in cases.iter().enumerate() { |
3352 | if let Some(refines) = case.refines { |
3353 | if refines >= i as u32 { |
3354 | return Err(BinaryReaderError::new( |
3355 | "variant case can only refine a previously defined case" , |
3356 | offset, |
3357 | )); |
3358 | } |
3359 | } |
3360 | |
3361 | let name = to_kebab_str(case.name, "variant case" , offset)?; |
3362 | |
3363 | let ty = case |
3364 | .ty |
3365 | .map(|ty| self.create_component_val_type(ty, offset)) |
3366 | .transpose()?; |
3367 | |
3368 | match case_map.entry(name.to_owned()) { |
3369 | Entry::Occupied(e) => bail!( |
3370 | offset, |
3371 | "variant case name ` {name}` conflicts with previous case name ` {prev}`" , |
3372 | name = case.name, |
3373 | prev = e.key() |
3374 | ), |
3375 | Entry::Vacant(e) => { |
3376 | if let Some(ty) = ty { |
3377 | info.combine(ty.info(types), offset)?; |
3378 | } |
3379 | |
3380 | // Safety: the use of `KebabStr::new_unchecked` here is safe because the string |
3381 | // was already verified to be kebab case. |
3382 | e.insert(VariantCase { |
3383 | ty, |
3384 | refines: case |
3385 | .refines |
3386 | .map(|i| KebabStr::new_unchecked(cases[i as usize].name).to_owned()), |
3387 | }); |
3388 | } |
3389 | } |
3390 | } |
3391 | |
3392 | Ok(ComponentDefinedType::Variant(VariantType { |
3393 | info, |
3394 | cases: case_map, |
3395 | })) |
3396 | } |
3397 | |
3398 | fn create_tuple_type( |
3399 | &self, |
3400 | tys: &[crate::ComponentValType], |
3401 | types: &TypeList, |
3402 | offset: usize, |
3403 | ) -> Result<ComponentDefinedType> { |
3404 | let mut info = TypeInfo::new(); |
3405 | if tys.is_empty() { |
3406 | bail!(offset, "tuple type must have at least one type" ); |
3407 | } |
3408 | let types = tys |
3409 | .iter() |
3410 | .map(|ty| { |
3411 | let ty = self.create_component_val_type(*ty, offset)?; |
3412 | info.combine(ty.info(types), offset)?; |
3413 | Ok(ty) |
3414 | }) |
3415 | .collect::<Result<_>>()?; |
3416 | |
3417 | Ok(ComponentDefinedType::Tuple(TupleType { info, types })) |
3418 | } |
3419 | |
3420 | fn create_flags_type( |
3421 | &self, |
3422 | names: &[&str], |
3423 | features: &WasmFeatures, |
3424 | offset: usize, |
3425 | ) -> Result<ComponentDefinedType> { |
3426 | let mut names_set = IndexSet::default(); |
3427 | names_set.reserve(names.len()); |
3428 | |
3429 | if names.is_empty() { |
3430 | bail!(offset, "flags must have at least one entry" ); |
3431 | } |
3432 | |
3433 | if names.len() > 32 && !features.component_model_more_flags() { |
3434 | bail!( |
3435 | offset, |
3436 | "cannot have more than 32 flags; this was previously \ |
3437 | accepted and if this is required for your project please \ |
3438 | leave a comment on \ |
3439 | https://github.com/WebAssembly/component-model/issues/370" |
3440 | ); |
3441 | } |
3442 | |
3443 | for name in names { |
3444 | let name = to_kebab_str(name, "flag" , offset)?; |
3445 | if !names_set.insert(name.to_owned()) { |
3446 | bail!( |
3447 | offset, |
3448 | "flag name ` {name}` conflicts with previous flag name ` {prev}`" , |
3449 | prev = names_set.get(name).unwrap() |
3450 | ); |
3451 | } |
3452 | } |
3453 | |
3454 | Ok(ComponentDefinedType::Flags(names_set)) |
3455 | } |
3456 | |
3457 | fn create_enum_type(&self, cases: &[&str], offset: usize) -> Result<ComponentDefinedType> { |
3458 | if cases.len() > u32::MAX as usize { |
3459 | return Err(BinaryReaderError::new( |
3460 | "enumeration type cannot be represented with a 32-bit discriminant value" , |
3461 | offset, |
3462 | )); |
3463 | } |
3464 | |
3465 | if cases.is_empty() { |
3466 | bail!(offset, "enum type must have at least one variant" ); |
3467 | } |
3468 | |
3469 | let mut tags = IndexSet::default(); |
3470 | tags.reserve(cases.len()); |
3471 | |
3472 | for tag in cases { |
3473 | let tag = to_kebab_str(tag, "enum tag" , offset)?; |
3474 | if !tags.insert(tag.to_owned()) { |
3475 | bail!( |
3476 | offset, |
3477 | "enum tag name ` {tag}` conflicts with previous tag name ` {prev}`" , |
3478 | prev = tags.get(tag).unwrap() |
3479 | ); |
3480 | } |
3481 | } |
3482 | |
3483 | Ok(ComponentDefinedType::Enum(tags)) |
3484 | } |
3485 | |
3486 | fn create_component_val_type( |
3487 | &self, |
3488 | ty: crate::ComponentValType, |
3489 | offset: usize, |
3490 | ) -> Result<ComponentValType> { |
3491 | Ok(match ty { |
3492 | crate::ComponentValType::Primitive(pt) => ComponentValType::Primitive(pt), |
3493 | crate::ComponentValType::Type(idx) => { |
3494 | ComponentValType::Type(self.defined_type_at(idx, offset)?) |
3495 | } |
3496 | }) |
3497 | } |
3498 | |
3499 | pub fn core_type_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreTypeId> { |
3500 | self.core_types |
3501 | .get(idx as usize) |
3502 | .copied() |
3503 | .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds" )) |
3504 | } |
3505 | |
3506 | pub fn component_type_at(&self, idx: u32, offset: usize) -> Result<ComponentAnyTypeId> { |
3507 | self.types |
3508 | .get(idx as usize) |
3509 | .copied() |
3510 | .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds" )) |
3511 | } |
3512 | |
3513 | fn function_type_at<'a>( |
3514 | &self, |
3515 | idx: u32, |
3516 | types: &'a TypeList, |
3517 | offset: usize, |
3518 | ) -> Result<&'a ComponentFuncType> { |
3519 | let id = self.component_type_at(idx, offset)?; |
3520 | match id { |
3521 | ComponentAnyTypeId::Func(id) => Ok(&types[id]), |
3522 | _ => bail!(offset, "type index {idx} is not a function type" ), |
3523 | } |
3524 | } |
3525 | |
3526 | fn function_at(&self, idx: u32, offset: usize) -> Result<ComponentFuncTypeId> { |
3527 | self.funcs.get(idx as usize).copied().ok_or_else(|| { |
3528 | format_err!( |
3529 | offset, |
3530 | "unknown function {idx}: function index out of bounds" |
3531 | ) |
3532 | }) |
3533 | } |
3534 | |
3535 | fn component_at(&self, idx: u32, offset: usize) -> Result<ComponentTypeId> { |
3536 | self.components.get(idx as usize).copied().ok_or_else(|| { |
3537 | format_err!( |
3538 | offset, |
3539 | "unknown component {idx}: component index out of bounds" |
3540 | ) |
3541 | }) |
3542 | } |
3543 | |
3544 | fn instance_at(&self, idx: u32, offset: usize) -> Result<ComponentInstanceTypeId> { |
3545 | self.instances.get(idx as usize).copied().ok_or_else(|| { |
3546 | format_err!( |
3547 | offset, |
3548 | "unknown instance {idx}: instance index out of bounds" |
3549 | ) |
3550 | }) |
3551 | } |
3552 | |
3553 | fn value_at(&mut self, idx: u32, offset: usize) -> Result<&ComponentValType> { |
3554 | match self.values.get_mut(idx as usize) { |
3555 | Some((ty, used)) if !*used => { |
3556 | *used = true; |
3557 | Ok(ty) |
3558 | } |
3559 | Some(_) => bail!(offset, "value {idx} cannot be used more than once" ), |
3560 | None => bail!(offset, "unknown value {idx}: value index out of bounds" ), |
3561 | } |
3562 | } |
3563 | |
3564 | fn defined_type_at(&self, idx: u32, offset: usize) -> Result<ComponentDefinedTypeId> { |
3565 | match self.component_type_at(idx, offset)? { |
3566 | ComponentAnyTypeId::Defined(id) => Ok(id), |
3567 | _ => bail!(offset, "type index {idx} is not a defined type" ), |
3568 | } |
3569 | } |
3570 | |
3571 | fn core_function_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> { |
3572 | match self.core_funcs.get(idx as usize) { |
3573 | Some(id) => Ok(*id), |
3574 | None => bail!( |
3575 | offset, |
3576 | "unknown core function {idx}: function index out of bounds" |
3577 | ), |
3578 | } |
3579 | } |
3580 | |
3581 | fn module_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreModuleTypeId> { |
3582 | match self.core_modules.get(idx as usize) { |
3583 | Some(id) => Ok(*id), |
3584 | None => bail!(offset, "unknown module {idx}: module index out of bounds" ), |
3585 | } |
3586 | } |
3587 | |
3588 | fn core_instance_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreInstanceTypeId> { |
3589 | match self.core_instances.get(idx as usize) { |
3590 | Some(id) => Ok(*id), |
3591 | None => bail!( |
3592 | offset, |
3593 | "unknown core instance {idx}: instance index out of bounds" |
3594 | ), |
3595 | } |
3596 | } |
3597 | |
3598 | fn core_instance_export<'a>( |
3599 | &self, |
3600 | instance_index: u32, |
3601 | name: &str, |
3602 | types: &'a TypeList, |
3603 | offset: usize, |
3604 | ) -> Result<&'a EntityType> { |
3605 | match types[self.core_instance_at(instance_index, offset)?] |
3606 | .internal_exports(types) |
3607 | .get(name) |
3608 | { |
3609 | Some(export) => Ok(export), |
3610 | None => bail!( |
3611 | offset, |
3612 | "core instance {instance_index} has no export named ` {name}`" |
3613 | ), |
3614 | } |
3615 | } |
3616 | |
3617 | fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> { |
3618 | match self.core_globals.get(idx as usize) { |
3619 | Some(t) => Ok(t), |
3620 | None => bail!(offset, "unknown global {idx}: global index out of bounds" ), |
3621 | } |
3622 | } |
3623 | |
3624 | fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> { |
3625 | match self.core_tables.get(idx as usize) { |
3626 | Some(t) => Ok(t), |
3627 | None => bail!(offset, "unknown table {idx}: table index out of bounds" ), |
3628 | } |
3629 | } |
3630 | |
3631 | fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> { |
3632 | match self.core_memories.get(idx as usize) { |
3633 | Some(t) => Ok(t), |
3634 | None => bail!(offset, "unknown memory {idx}: memory index out of bounds" ), |
3635 | } |
3636 | } |
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 | pub fn finish(&mut self, types: &TypeAlloc, offset: usize) -> Result<ComponentType> { |
3646 | let mut ty = ComponentType { |
3647 | // Inherit some fields based on translation of the component. |
3648 | info: self.type_info, |
3649 | imports: self.imports.clone(), |
3650 | exports: self.exports.clone(), |
3651 | |
3652 | // This is filled in as a subset of `self.defined_resources` |
3653 | // depending on what's actually used by the exports. See the |
3654 | // bottom of this function. |
3655 | defined_resources: Default::default(), |
3656 | |
3657 | // These are inherited directly from what was calculated for this |
3658 | // component. |
3659 | imported_resources: mem::take(&mut self.imported_resources) |
3660 | .into_iter() |
3661 | .collect(), |
3662 | explicit_resources: mem::take(&mut self.explicit_resources), |
3663 | }; |
3664 | |
3665 | // Collect all "free variables", or resources, from the imports of this |
3666 | // component. None of the resources defined within this component can |
3667 | // be used as part of the exports. This set is then used to reject any |
3668 | // of `self.defined_resources` which show up. |
3669 | let mut free = IndexSet::default(); |
3670 | for ty in ty.imports.values() { |
3671 | types.free_variables_component_entity(ty, &mut free); |
3672 | } |
3673 | 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 | if free.contains(resource) { |
3682 | bail!(offset, "local resource type found in imports" ); |
3683 | } |
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 | free.clear(); |
3709 | for ty in ty.exports.values() { |
3710 | types.free_variables_component_entity(ty, &mut free); |
3711 | } |
3712 | for (id, _rep) in mem::take(&mut self.defined_resources) { |
3713 | if !free.contains(&id) { |
3714 | continue; |
3715 | } |
3716 | |
3717 | let path = match ty.explicit_resources.get(&id).cloned() { |
3718 | 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 | None => bail!( |
3727 | offset, |
3728 | "local resource type found in export but not exported itself" |
3729 | ), |
3730 | }; |
3731 | |
3732 | ty.defined_resources.push((id, path)); |
3733 | } |
3734 | |
3735 | Ok(ty) |
3736 | } |
3737 | |
3738 | fn check_value_support(&self, features: &WasmFeatures, offset: usize) -> Result<()> { |
3739 | if !features.component_model_values() { |
3740 | bail!( |
3741 | offset, |
3742 | "support for component model `value`s is not enabled" |
3743 | ); |
3744 | } |
3745 | Ok(()) |
3746 | } |
3747 | } |
3748 | |
3749 | impl InternRecGroup for ComponentState { |
3750 | fn add_type_id(&mut self, id: CoreTypeId) { |
3751 | self.core_types.push(ComponentCoreTypeId::Sub(id)); |
3752 | } |
3753 | |
3754 | fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> { |
3755 | match self.core_type_at(idx, offset)? { |
3756 | ComponentCoreTypeId::Sub(id: CoreTypeId) => Ok(id), |
3757 | ComponentCoreTypeId::Module(_) => { |
3758 | bail!(offset, "type index {idx} is a module type, not a sub type" ); |
3759 | } |
3760 | } |
3761 | } |
3762 | |
3763 | fn types_len(&self) -> u32 { |
3764 | u32::try_from(self.core_types.len()).unwrap() |
3765 | } |
3766 | } |
3767 | |
3768 | impl ComponentNameContext { |
3769 | /// Registers that the resource `id` is named `name` within this context. |
3770 | fn register(&mut self, name: &str, id: AliasableResourceId) { |
3771 | let idx = self.all_resource_names.len(); |
3772 | let prev = self.resource_name_map.insert(id, idx); |
3773 | assert!( |
3774 | prev.is_none(), |
3775 | "for {id:?}, inserted {idx:?} but already had {prev:?}" |
3776 | ); |
3777 | self.all_resource_names.insert(name.to_string()); |
3778 | } |
3779 | |
3780 | fn validate_extern( |
3781 | &self, |
3782 | name: &str, |
3783 | kind: ExternKind, |
3784 | ty: &ComponentEntityType, |
3785 | types: &TypeAlloc, |
3786 | offset: usize, |
3787 | kind_names: &mut IndexSet<ComponentName>, |
3788 | items: &mut IndexMap<String, ComponentEntityType>, |
3789 | info: &mut TypeInfo, |
3790 | features: &WasmFeatures, |
3791 | ) -> Result<()> { |
3792 | // First validate that `name` is even a valid kebab name, meaning it's |
3793 | // in kebab-case, is an ID, etc. |
3794 | let kebab = ComponentName::new_with_features(name, offset, *features) |
3795 | .with_context(|| format!(" {} name ` {name}` is not a valid extern name" , kind.desc()))?; |
3796 | |
3797 | if let ExternKind::Export = kind { |
3798 | match kebab.kind() { |
3799 | ComponentNameKind::Label(_) |
3800 | | ComponentNameKind::Method(_) |
3801 | | ComponentNameKind::Static(_) |
3802 | | ComponentNameKind::Constructor(_) |
3803 | | ComponentNameKind::Interface(_) => {} |
3804 | |
3805 | ComponentNameKind::Hash(_) |
3806 | | ComponentNameKind::Url(_) |
3807 | | ComponentNameKind::Dependency(_) => { |
3808 | bail!(offset, "name ` {name}` is not a valid export name" ) |
3809 | } |
3810 | } |
3811 | } |
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 | self.validate(&kebab, ty, types, offset) |
3816 | .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 | if let Some(prev) = kind_names.replace(kebab.clone()) { |
3822 | bail!( |
3823 | offset, |
3824 | " {} name ` {kebab}` conflicts with previous name ` {prev}`" , |
3825 | kind.desc() |
3826 | ); |
3827 | } |
3828 | |
3829 | // Otherwise all strings must be unique, regardless of their name, so |
3830 | // consult the `items` set to ensure that we're not for example |
3831 | // importing the same interface ID twice. |
3832 | match items.entry(name.to_string()) { |
3833 | Entry::Occupied(e) => { |
3834 | bail!( |
3835 | offset, |
3836 | " {kind} name ` {name}` conflicts with previous name ` {prev}`" , |
3837 | kind = kind.desc(), |
3838 | prev = e.key(), |
3839 | ); |
3840 | } |
3841 | Entry::Vacant(e) => { |
3842 | e.insert(*ty); |
3843 | info.combine(ty.info(types), offset)?; |
3844 | } |
3845 | } |
3846 | Ok(()) |
3847 | } |
3848 | |
3849 | /// Validates that the `name` provided is allowed to have the type `ty`. |
3850 | fn validate( |
3851 | &self, |
3852 | name: &ComponentName, |
3853 | ty: &ComponentEntityType, |
3854 | types: &TypeAlloc, |
3855 | offset: usize, |
3856 | ) -> Result<()> { |
3857 | let func = || { |
3858 | let id = match ty { |
3859 | ComponentEntityType::Func(id) => *id, |
3860 | _ => bail!(offset, "item is not a func" ), |
3861 | }; |
3862 | Ok(&types[id]) |
3863 | }; |
3864 | 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 | | ComponentNameKind::Hash(_) => {} |
3871 | |
3872 | // Constructors must return `(own $resource)` and the `$resource` |
3873 | // must be named within this context to match `rname` |
3874 | ComponentNameKind::Constructor(rname) => { |
3875 | let ty = func()?; |
3876 | if ty.results.len() != 1 { |
3877 | bail!(offset, "function should return one value" ); |
3878 | } |
3879 | let ty = ty.results[0].1; |
3880 | let resource = match ty { |
3881 | ComponentValType::Primitive(_) => None, |
3882 | ComponentValType::Type(ty) => match &types[ty] { |
3883 | ComponentDefinedType::Own(id) => Some(id), |
3884 | _ => None, |
3885 | }, |
3886 | }; |
3887 | let resource = match resource { |
3888 | Some(id) => id, |
3889 | None => bail!(offset, "function should return `(own $T)`" ), |
3890 | }; |
3891 | 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 | ComponentNameKind::Method(name) => { |
3898 | let ty = func()?; |
3899 | if ty.params.len() == 0 { |
3900 | bail!(offset, "function should have at least one argument" ); |
3901 | } |
3902 | let (pname, pty) = &ty.params[0]; |
3903 | if pname.as_str() != "self" { |
3904 | bail!( |
3905 | offset, |
3906 | "function should have a first argument called `self`" , |
3907 | ); |
3908 | } |
3909 | let id = match pty { |
3910 | ComponentValType::Primitive(_) => None, |
3911 | ComponentValType::Type(ty) => match &types[*ty] { |
3912 | ComponentDefinedType::Borrow(id) => Some(id), |
3913 | _ => None, |
3914 | }, |
3915 | }; |
3916 | let id = match id { |
3917 | Some(id) => id, |
3918 | None => bail!( |
3919 | offset, |
3920 | "function should take a first argument of `(borrow $T)`" |
3921 | ), |
3922 | }; |
3923 | 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 | ComponentNameKind::Static(name) => { |
3930 | func()?; |
3931 | if !self.all_resource_names.contains(name.resource().as_str()) { |
3932 | bail!(offset, "static resource name is not known in this context" ); |
3933 | } |
3934 | } |
3935 | } |
3936 | |
3937 | Ok(()) |
3938 | } |
3939 | |
3940 | fn validate_resource_name( |
3941 | &self, |
3942 | id: AliasableResourceId, |
3943 | name: &KebabStr, |
3944 | offset: usize, |
3945 | ) -> Result<()> { |
3946 | let expected_name_idx = match self.resource_name_map.get(&id) { |
3947 | Some(idx) => *idx, |
3948 | None => { |
3949 | bail!( |
3950 | offset, |
3951 | "resource used in function does not have a name in this context" |
3952 | ) |
3953 | } |
3954 | }; |
3955 | let expected_name = &self.all_resource_names[expected_name_idx]; |
3956 | if name.as_str() != expected_name { |
3957 | bail!( |
3958 | offset, |
3959 | "function does not match expected \ |
3960 | resource name ` {expected_name}`" |
3961 | ); |
3962 | } |
3963 | Ok(()) |
3964 | } |
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 | pub fn insert(&mut self, key: K, value: V) { |
3981 | let prev = self.0.insert(key, value); |
3982 | assert!(prev.is_none()); |
3983 | } |
3984 | } |
3985 | |
3986 | impl<K, V> Deref for IndexMapAppendOnly<K, V> { |
3987 | type Target = IndexMap<K, V>; |
3988 | fn deref(&self) -> &IndexMap<K, V> { |
3989 | &self.0 |
3990 | } |
3991 | } |
3992 | |
3993 | impl<K, V> Default for IndexMapAppendOnly<K, V> { |
3994 | fn default() -> Self { |
3995 | Self(Default::default()) |
3996 | } |
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 | fn into_iter(self) -> Self::IntoIter { |
4003 | self.0.into_iter() |
4004 | } |
4005 | } |
4006 | } |
4007 | |