1use crate::encoding::{CompositionGraphEncoder, TypeEncoder};
3use anyhow::{Context, Result, anyhow, bail};
4use indexmap::{IndexMap, IndexSet};
5use petgraph::{EdgeDirection, algo::toposort, graphmap::DiGraphMap};
6use std::{
7 borrow::Cow,
8 cell::RefCell,
9 collections::{HashMap, HashSet, hash_map::Entry},
10 path::{Path, PathBuf},
11 sync::atomic::{AtomicUsize, Ordering},
12};
13use wasmparser::{
14 Chunk, ComponentExternalKind, ComponentTypeRef, Encoding, Parser, Payload, ValidPayload,
15 Validator,
16 component_types::{
17 ComponentAnyTypeId, ComponentEntityType, ComponentInstanceTypeId, Remap, Remapping,
18 ResourceId, SubtypeCx,
19 },
20 names::ComponentName,
21 types::{Types, TypesRef},
22};
23
24pub(crate) fn type_desc(item: ComponentEntityType) -> &'static str {
25 match item {
26 ComponentEntityType::Instance(_) => "instance",
27 ComponentEntityType::Module(_) => "module",
28 ComponentEntityType::Func(_) => "function",
29 ComponentEntityType::Value(_) => "value",
30 ComponentEntityType::Type { .. } => "type",
31 ComponentEntityType::Component(_) => "component",
32 }
33}
34
35pub struct Component<'a> {
37 pub(crate) name: String,
39 pub(crate) path: Option<PathBuf>,
41 pub(crate) bytes: Cow<'a, [u8]>,
43 pub(crate) types: Types,
45 pub(crate) imports: IndexMap<String, ComponentTypeRef>,
47 pub(crate) exports: IndexMap<String, (ComponentExternalKind, u32)>,
49}
50
51impl<'a> Component<'a> {
52 pub fn from_file(
54 validator: &mut Validator,
55 name: &str,
56 path: impl AsRef<Path>,
57 ) -> Result<Self> {
58 let path = path.as_ref();
59 log::info!("parsing WebAssembly component file `{}`", path.display());
60 let component = Self::parse(
61 validator,
62 ComponentName::new(name, 0)?.to_string(),
63 Some(path.to_owned()),
64 wat::parse_file(path)
65 .with_context(|| {
66 format!("failed to parse component `{path}`", path = path.display())
67 })?
68 .into(),
69 )
70 .with_context(|| format!("failed to parse component `{path}`", path = path.display()))?;
71
72 log::debug!(
73 "WebAssembly component `{path}` parsed:\n{component:#?}",
74 path = path.display()
75 );
76
77 Ok(component)
78 }
79
80 pub fn from_bytes(
82 validator: &mut Validator,
83 name: impl Into<String>,
84 bytes: impl Into<Cow<'a, [u8]>>,
85 ) -> Result<Self> {
86 let mut bytes = bytes.into();
87
88 match wat::parse_bytes(bytes.as_ref()).context("failed to parse component")? {
89 Cow::Borrowed(_) => {
90 }
92 Cow::Owned(v) => bytes = v.into(),
93 }
94
95 log::info!("parsing WebAssembly component from bytes");
96 let component = Self::parse(
97 validator,
98 ComponentName::new(&name.into(), 0)?.to_string(),
99 None,
100 bytes,
101 )
102 .context("failed to parse component")?;
103
104 log::debug!("WebAssembly component parsed:\n{component:#?}",);
105
106 Ok(component)
107 }
108
109 fn parse(
110 validator: &mut Validator,
111 name: String,
112 path: Option<PathBuf>,
113 bytes: Cow<'a, [u8]>,
114 ) -> Result<Self> {
115 let mut parser = Parser::new(0);
116 let mut parsers = Vec::new();
117 let mut imports = IndexMap::new();
118 let mut exports = IndexMap::new();
119
120 validator.reset();
121
122 let mut cur = bytes.as_ref();
123 loop {
124 match parser.parse(cur, true)? {
125 Chunk::Parsed { payload, consumed } => {
126 cur = &cur[consumed..];
127
128 match validator.payload(&payload)? {
129 ValidPayload::Ok => {
130 if !parsers.is_empty() {
132 continue;
133 }
134
135 match payload {
136 Payload::Version { encoding, .. } => {
137 if encoding != Encoding::Component {
138 bail!(
139 "the {} is not a WebAssembly component",
140 if path.is_none() { "given data" } else { "file" }
141 );
142 }
143 }
144 Payload::ComponentImportSection(s) => {
145 for import in s {
146 let import = import?;
147 let name = import.name.name.to_string();
148 imports.insert(name, import.ty);
149 }
150 }
151 Payload::ComponentExportSection(s) => {
152 for export in s {
153 let export = export?;
154 let name = export.name.name.to_string();
155 exports.insert(name, (export.kind, export.index));
156 }
157 }
158 _ => {}
159 }
160 }
161 ValidPayload::Func(_, _) => {}
162 ValidPayload::Parser(next) => {
163 parsers.push(parser);
164 parser = next;
165 }
166 ValidPayload::End(types) => match parsers.pop() {
167 Some(parent) => parser = parent,
168 None => {
169 return Ok(Component {
170 name,
171 path,
172 bytes,
173 types,
174 imports,
175 exports,
176 });
177 }
178 },
179 }
180 }
181 Chunk::NeedMoreData(_) => unreachable!(),
182 }
183 }
184 }
185
186 pub fn name(&self) -> &str {
190 &self.name
191 }
192
193 pub fn path(&self) -> Option<&Path> {
197 self.path.as_deref()
198 }
199
200 pub fn bytes(&self) -> &[u8] {
202 self.bytes.as_ref()
203 }
204
205 pub fn types(&self) -> TypesRef<'_> {
207 self.types.as_ref()
208 }
209
210 pub fn export(
212 &self,
213 index: impl Into<ExportIndex>,
214 ) -> Option<(&str, ComponentExternalKind, u32)> {
215 let index = index.into();
216 self.exports
217 .get_index(index.0)
218 .map(|(name, (kind, index))| (name.as_str(), *kind, *index))
219 }
220
221 pub fn export_by_name(&self, name: &str) -> Option<(ExportIndex, ComponentExternalKind, u32)> {
223 self.exports
224 .get_full(name)
225 .map(|(i, _, (kind, index))| (ExportIndex(i), *kind, *index))
226 }
227
228 pub fn exports(
230 &self,
231 ) -> impl ExactSizeIterator<Item = (ExportIndex, &str, ComponentExternalKind, u32)> {
232 self.exports
233 .iter()
234 .enumerate()
235 .map(|(i, (name, (kind, index)))| (ExportIndex(i), name.as_str(), *kind, *index))
236 }
237
238 pub fn import(&self, index: impl Into<ImportIndex>) -> Option<(&str, ComponentTypeRef)> {
240 let index = index.into();
241 self.imports
242 .get_index(index.0)
243 .map(|(name, ty)| (name.as_str(), *ty))
244 }
245
246 pub fn import_by_name(&self, name: &str) -> Option<(ImportIndex, ComponentTypeRef)> {
248 self.imports
249 .get_full(name)
250 .map(|(i, _, ty)| (ImportIndex(i), *ty))
251 }
252
253 pub fn imports(&self) -> impl ExactSizeIterator<Item = (ImportIndex, &str, ComponentTypeRef)> {
255 self.imports
256 .iter()
257 .enumerate()
258 .map(|(i, (name, ty))| (ImportIndex(i), name.as_str(), *ty))
259 }
260
261 pub(crate) fn ty(&self) -> wasm_encoder::ComponentType {
262 let encoder = TypeEncoder::new(self);
263
264 encoder.component(
265 &mut Default::default(),
266 self.imports()
267 .map(|(i, ..)| self.import_entity_type(i).unwrap()),
268 self.exports()
269 .map(|(i, ..)| self.export_entity_type(i).unwrap()),
270 )
271 }
272
273 pub(crate) fn export_entity_type(
274 &self,
275 index: ExportIndex,
276 ) -> Option<(&str, ComponentEntityType)> {
277 let (name, _kind, _index) = self.export(index)?;
278 Some((
279 name,
280 self.types.as_ref().component_item_for_export(name)?.ty,
281 ))
282 }
283
284 pub(crate) fn import_entity_type(
285 &self,
286 index: ImportIndex,
287 ) -> Option<(&str, ComponentEntityType)> {
288 let (name, _ty) = self.import(index)?;
289 Some((
290 name,
291 self.types.as_ref().component_item_for_import(name)?.ty,
292 ))
293 }
294
295 pub(crate) fn find_compatible_export(
297 &self,
298 ty: ComponentInstanceTypeId,
299 types: TypesRef,
300 export_component_id: ComponentId,
301 graph: &CompositionGraph,
302 ) -> Option<ExportIndex> {
303 self.exports
304 .iter()
305 .position(|(_, (kind, index))| {
306 if *kind != ComponentExternalKind::Instance {
307 return false;
308 }
309
310 graph.try_connection(
311 export_component_id,
312 ComponentEntityType::Instance(
313 self.types.as_ref().component_instance_at(*index),
314 ),
315 self.types(),
316 ComponentEntityType::Instance(ty),
317 types,
318 )
319 })
320 .map(ExportIndex)
321 }
322
323 pub(crate) fn is_instance_subtype_of(
326 &self,
327 ty: ComponentInstanceTypeId,
328 types: TypesRef,
329 ) -> bool {
330 let exports = types[ty].exports.iter();
331
332 for (k, b) in exports {
333 match self.exports.get_full(k.as_str()) {
334 Some((ai, _, _)) => {
335 let (_, a) = self.export_entity_type(ExportIndex(ai)).unwrap();
336 if !ComponentEntityType::is_subtype_of(&a, self.types(), &b.ty, types) {
337 return false;
338 }
339 }
340 None => return false,
341 }
342 }
343
344 true
345 }
346}
347
348impl std::fmt::Debug for Component<'_> {
349 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
350 f.debug_struct("Component")
351 .field("imports", &self.imports)
352 .field("exports", &self.exports)
353 .finish_non_exhaustive()
354 }
355}
356
357static NEXT_COMPONENT_ID: AtomicUsize = AtomicUsize::new(0);
358
359#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
361pub struct ComponentId(pub usize);
362
363impl ComponentId {
364 fn next() -> Result<Self> {
365 let next = NEXT_COMPONENT_ID.fetch_add(1, Ordering::SeqCst);
366 if next == usize::MAX {
367 bail!("component limit reached");
368 }
369 Ok(Self(next))
370 }
371}
372
373impl std::fmt::Display for ComponentId {
374 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
375 write!(f, "{}", self.0)
376 }
377}
378
379impl From<usize> for ComponentId {
380 fn from(id: usize) -> Self {
381 Self(id)
382 }
383}
384
385static NEXT_INSTANCE_ID: AtomicUsize = AtomicUsize::new(0);
386
387#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
389pub struct InstanceId(pub usize);
390
391impl std::fmt::Display for InstanceId {
392 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
393 write!(f, "{}", self.0)
394 }
395}
396
397impl InstanceId {
398 fn next() -> Result<Self> {
399 let next = NEXT_INSTANCE_ID.fetch_add(1, Ordering::SeqCst);
400 if next == usize::MAX {
401 bail!("instance limit reached");
402 }
403 Ok(Self(next))
404 }
405}
406
407impl From<usize> for InstanceId {
408 fn from(id: usize) -> Self {
409 Self(id)
410 }
411}
412
413#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
415pub struct ImportIndex(pub usize);
416
417impl std::fmt::Display for ImportIndex {
418 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
419 write!(f, "{}", self.0)
420 }
421}
422
423impl From<usize> for ImportIndex {
424 fn from(id: usize) -> Self {
425 Self(id)
426 }
427}
428
429#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
431pub struct ExportIndex(pub usize);
432
433impl std::fmt::Display for ExportIndex {
434 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
435 write!(f, "{}", self.0)
436 }
437}
438
439impl From<usize> for ExportIndex {
440 fn from(id: usize) -> Self {
441 Self(id)
442 }
443}
444
445#[derive(Debug)]
446pub(crate) struct ComponentEntry<'a> {
447 pub(crate) component: Component<'a>,
448 pub(crate) instances: HashSet<InstanceId>,
449}
450
451#[derive(Debug)]
452pub(crate) struct Instance {
453 pub(crate) component: ComponentId,
454 pub(crate) connected: IndexSet<ImportIndex>,
455}
456
457#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
459pub struct EncodeOptions {
460 pub define_components: bool,
464
465 pub export: Option<InstanceId>,
470
471 pub validate: bool,
473}
474
475#[derive(Clone, Debug, Default)]
476pub(crate) struct ResourceMapping {
477 map: HashMap<ResourceId, (ComponentId, ResourceId)>,
478}
479
480impl ResourceMapping {
481 fn add_pairs(
482 mut self,
483 export_component: ComponentId,
484 export_type: ComponentEntityType,
485 export_types: TypesRef,
486 import_type: ComponentEntityType,
487 import_types: TypesRef,
488 ) -> Option<Self> {
489 if let (
490 ComponentEntityType::Instance(export_type),
491 ComponentEntityType::Instance(import_type),
492 ) = (export_type, import_type)
493 {
494 let mut exports = HashMap::new();
495 for (export_name, ty) in &export_types[export_type].exports {
496 if let ComponentEntityType::Type {
498 referenced: ComponentAnyTypeId::Resource(resource_id),
499 ..
500 } = ty.ty
501 {
502 exports.insert(export_name, (export_component, resource_id.resource()));
503 }
504 }
505
506 for (export_name, ty) in &import_types[import_type].exports {
507 if let ComponentEntityType::Type {
509 referenced: ComponentAnyTypeId::Resource(resource_id),
510 ..
511 } = ty.ty
512 {
513 let import_resource = resource_id.resource();
514 if let Some((export_component, export_resource)) =
515 exports.get(&export_name).copied()
516 {
517 let value = self
518 .map
519 .get(&export_resource)
520 .copied()
521 .or_else(|| self.map.get(&import_resource).copied())
522 .unwrap_or((export_component, export_resource));
523
524 if value.1 == export_resource {
525 self.map.insert(export_resource, value);
526 self.map.insert(import_resource, value);
527 }
528 } else {
529 return None;
532 }
533 }
534 }
535 }
536
537 Some(self)
538 }
539
540 pub(crate) fn remapping(&self) -> Remapping {
541 let mut remapping = Remapping::default();
542 for (old, (_, new)) in &self.map {
543 if old != new {
544 remapping.add(*old, *new)
545 }
546 }
547 remapping
548 }
549}
550
551#[derive(Debug, Default)]
554pub struct CompositionGraph<'a> {
555 names: HashMap<String, ComponentId>,
556 pub(crate) components: IndexMap<ComponentId, ComponentEntry<'a>>,
557 pub(crate) instances: IndexMap<InstanceId, Instance>,
558 pub(crate) graph: DiGraphMap<InstanceId, IndexMap<ImportIndex, Option<ExportIndex>>>,
562 pub(crate) resource_mapping: RefCell<ResourceMapping>,
563}
564
565impl<'a> CompositionGraph<'a> {
566 pub fn new() -> Self {
568 Self::default()
569 }
570
571 pub(crate) fn unify_imported_resources(&self) {
585 let mut resource_mapping = self.resource_mapping.borrow_mut();
586
587 let mut resource_imports = IndexMap::<_, IndexSet<_>>::new();
588 for (component_id, component) in &self.components {
589 let component = &component.component;
590 for import_name in component.imports.keys() {
591 let ty = component
592 .types
593 .as_ref()
594 .component_item_for_import(import_name)
595 .unwrap()
596 .ty;
597
598 if let ComponentEntityType::Instance(instance_id) = ty {
599 for (export_name, ty) in &component.types[instance_id].exports {
600 if let ComponentEntityType::Type {
602 referenced: ComponentAnyTypeId::Resource(resource_id),
603 ..
604 } = ty.ty
605 {
606 let set = resource_imports
607 .entry(vec![import_name.to_string(), export_name.to_string()])
608 .or_default();
609
610 if let Some(pair) = resource_mapping.map.get(&resource_id.resource()) {
611 set.insert(*pair);
612 }
613 set.insert((*component_id, resource_id.resource()));
614 }
615 }
616 }
617 }
618 }
619
620 for resources in resource_imports.values() {
621 match &resources.iter().copied().collect::<Vec<_>>()[..] {
622 [] => unreachable!(),
623 [_] => {}
624 [first, rest @ ..] => {
625 resource_mapping.map.insert(first.1, *first);
626 for resource in rest {
627 resource_mapping.map.insert(resource.1, *first);
628 }
629 }
630 }
631 }
632 }
633
634 pub(crate) fn try_connection(
648 &self,
649 export_component: ComponentId,
650 mut export_type: ComponentEntityType,
651 export_types: TypesRef,
652 mut import_type: ComponentEntityType,
653 import_types: TypesRef,
654 ) -> bool {
655 let resource_mapping = self.resource_mapping.borrow().clone();
656
657 if let Some(resource_mapping) = resource_mapping.add_pairs(
658 export_component,
659 export_type,
660 export_types,
661 import_type,
662 import_types,
663 ) {
664 let remapping = &mut resource_mapping.remapping();
665 let mut context = SubtypeCx::new_with_refs(export_types, import_types);
666
667 context
668 .a
669 .remap_component_entity(&mut export_type, remapping);
670 remapping.reset_type_cache();
671
672 context
673 .b
674 .remap_component_entity(&mut import_type, remapping);
675 remapping.reset_type_cache();
676
677 let v = context.component_entity_type(&export_type, &import_type, 0);
678 if v.is_ok() {
679 *self.resource_mapping.borrow_mut() = resource_mapping;
680 true
681 } else {
682 false
683 }
684 } else {
685 false
686 }
687 }
688
689 pub(crate) fn remapping_map<'b>(
690 &'b self,
691 ) -> HashMap<ResourceId, (&'b Component<'a>, ResourceId)> {
692 let mut map = HashMap::new();
693 for (old, (component, new)) in &self.resource_mapping.borrow().map {
694 if old != new {
695 let component = &self.components.get(component).unwrap().component;
696 map.insert(*old, (component, *new));
697 }
698 }
699 map
700 }
701
702 pub fn add_component(&mut self, component: Component<'a>) -> Result<ComponentId> {
706 let id = match self.names.entry(component.name.clone()) {
707 Entry::Occupied(e) => {
708 bail!(
709 "a component with name `{name}` already exists",
710 name = e.key()
711 )
712 }
713 Entry::Vacant(e) => *e.insert(ComponentId::next()?),
714 };
715
716 log::info!(
717 "adding WebAssembly component `{name}` ({id}) to the graph",
718 name = component.name(),
719 );
720
721 let entry = ComponentEntry {
722 component,
723 instances: HashSet::new(),
724 };
725
726 assert!(self.components.insert(id, entry).is_none());
727
728 if self.components.len() > 1 {
729 self.unify_imported_resources();
730 }
731
732 Ok(id)
733 }
734
735 pub fn get_component(&self, id: impl Into<ComponentId>) -> Option<&Component<'a>> {
737 self.components.get(&id.into()).map(|e| &e.component)
738 }
739
740 pub fn get_component_by_name(&self, name: &str) -> Option<(ComponentId, &Component<'a>)> {
742 let id = self.names.get(name)?;
743 let entry = &self.components[id];
744 Some((*id, &entry.component))
745 }
746
747 pub fn remove_component(&mut self, id: impl Into<ComponentId>) {
752 let id = id.into();
753 if let Some(entry) = self.components.swap_remove(&id) {
754 log::info!(
755 "removing WebAssembly component `{name}` ({id}) from the graph",
756 name = entry.component.name(),
757 );
758
759 assert!(self.names.remove(&entry.component.name).is_some());
760
761 for instance_id in entry.instances.iter().copied() {
762 self.instances.swap_remove(&instance_id);
763
764 for (_, target_id, map) in self
766 .graph
767 .edges_directed(instance_id, EdgeDirection::Outgoing)
768 {
769 let target = self.instances.get_mut(&target_id).unwrap();
770 for index in map.keys() {
771 target.connected.swap_remove(index);
772 }
773 }
774
775 self.graph.remove_node(instance_id);
776 }
777 }
778 }
779
780 pub fn instantiate(&mut self, id: impl Into<ComponentId>) -> Result<InstanceId> {
782 let id = id.into();
783 let entry = self
784 .components
785 .get_mut(&id)
786 .ok_or_else(|| anyhow!("component does not exist in the graph"))?;
787
788 let instance_id = InstanceId::next()?;
789
790 log::info!(
791 "instantiating WebAssembly component `{name}` ({id}) with instance identifier {instance_id}",
792 name = entry.component.name(),
793 );
794
795 self.instances.insert(
796 instance_id,
797 Instance {
798 component: id,
799 connected: Default::default(),
800 },
801 );
802
803 entry.instances.insert(instance_id);
804
805 Ok(instance_id)
806 }
807
808 pub fn get_component_of_instance(
810 &self,
811 id: impl Into<InstanceId>,
812 ) -> Option<(ComponentId, &Component<'_>)> {
813 let id = id.into();
814 let instance = self.instances.get(&id)?;
815
816 Some((
817 instance.component,
818 self.get_component(instance.component).unwrap(),
819 ))
820 }
821
822 pub fn remove_instance(&mut self, id: impl Into<InstanceId>) {
826 let id = id.into();
827 if let Some(instance) = self.instances.swap_remove(&id) {
828 let entry = self.components.get_mut(&instance.component).unwrap();
829
830 log::info!(
831 "removing instance ({id}) of component `{name}` ({cid}) from the graph",
832 name = entry.component.name(),
833 cid = instance.component.0,
834 );
835
836 entry.instances.remove(&id);
837
838 for (_, target, map) in self.graph.edges_directed(id, EdgeDirection::Outgoing) {
840 let target = self.instances.get_mut(&target).unwrap();
841 for index in map.keys() {
842 target.connected.swap_remove(index);
843 }
844 }
845
846 self.graph.remove_node(id);
847 }
848 }
849
850 pub fn connect(
857 &mut self,
858 source: impl Into<InstanceId> + Copy,
859 source_export: Option<impl Into<ExportIndex> + Copy>,
860 target: impl Into<InstanceId> + Copy,
861 target_import: impl Into<ImportIndex> + Copy,
862 ) -> Result<()> {
863 self.validate_connection(source, source_export, target, target_import)?;
864
865 let source = source.into();
866 let source_export = source_export.map(Into::into);
867 let target = target.into();
868 let target_import = target_import.into();
869
870 match source_export {
871 Some(export) => log::info!(
872 "connecting export {export} of instance {source} to import `{target_import}` of instance {target}"
873 ),
874 None => log::info!(
875 "connecting instance {source} to import {target_import} of instance {target}"
876 ),
877 }
878
879 self.instances
880 .get_mut(&target)
881 .unwrap()
882 .connected
883 .insert(target_import);
884
885 if let Some(map) = self.graph.edge_weight_mut(source, target) {
886 assert!(map.insert(target_import, source_export).is_none());
887 } else {
888 let mut map = IndexMap::new();
889 map.insert(target_import, source_export);
890 self.graph.add_edge(source, target, map);
891 }
892
893 Ok(())
894 }
895
896 pub fn disconnect(
903 &mut self,
904 source: impl Into<InstanceId>,
905 target: impl Into<InstanceId>,
906 target_import: impl Into<ImportIndex>,
907 ) -> Result<()> {
908 let source = source.into();
909 let target = target.into();
910 let target_import = target_import.into();
911
912 log::info!("disconnecting import {target_import} of instance {target}");
913
914 if !self.instances.contains_key(&source) {
915 bail!("the source instance does not exist in the graph");
916 }
917
918 let target_instance = self
919 .instances
920 .get_mut(&target)
921 .ok_or_else(|| anyhow!("the target instance does not exist in the graph"))?;
922
923 target_instance.connected.swap_remove(&target_import);
924
925 let remove_edge = if let Some(set) = self.graph.edge_weight_mut(source, target) {
926 set.swap_remove(&target_import);
927 set.is_empty()
928 } else {
929 false
930 };
931
932 if remove_edge {
933 self.graph.remove_edge(source, target);
934 }
935
936 Ok(())
937 }
938
939 pub fn validate_connection(
946 &self,
947 source: impl Into<InstanceId>,
948 source_export: Option<impl Into<ExportIndex>>,
949 target: impl Into<InstanceId>,
950 target_import: impl Into<ImportIndex>,
951 ) -> Result<()> {
952 let source = source.into();
953 let source_export = source_export.map(Into::into);
954 let target = target.into();
955 let target_import = target_import.into();
956
957 if source == target {
958 bail!("an instance cannot be connected to itself");
959 }
960
961 let source_instance = self
962 .instances
963 .get(&source)
964 .ok_or_else(|| anyhow!("the source instance does not exist in the graph"))?;
965
966 let source_component = &self.components[&source_instance.component].component;
967
968 let target_instance = self
969 .instances
970 .get(&target)
971 .ok_or_else(|| anyhow!("the target instance does not exist in the graph"))?;
972
973 let target_component = &self.components[&target_instance.component].component;
974 let (import_name, import_ty) = target_component
975 .import_entity_type(target_import)
976 .ok_or_else(|| anyhow!("the target import index is invalid"))?;
977
978 if target_instance.connected.contains(&target_import) {
979 bail!(
980 "{import_ty} import `{import_name}` is already connected",
981 import_ty = type_desc(import_ty)
982 );
983 }
984
985 if let Some(export_index) = source_export {
986 let (export_name, export_ty) = source_component
987 .export_entity_type(export_index)
988 .ok_or_else(|| anyhow!("the source export index is invalid"))?;
989
990 if !self.try_connection(
991 source_instance.component,
992 export_ty,
993 source_component.types(),
994 import_ty,
995 target_component.types(),
996 ) {
997 bail!(
998 "source {export_ty} export `{export_name}` is not compatible with target \
999 {import_ty} import `{import_name}`",
1000 export_ty = type_desc(export_ty),
1001 import_ty = type_desc(import_ty),
1002 );
1003 }
1004 } else {
1005 let ty = match import_ty {
1006 ComponentEntityType::Instance(id) => id,
1007 _ => bail!(
1008 "source instance is not compatible with target {import_ty} import `{import_name}`",
1009 import_ty = type_desc(import_ty)
1010 ),
1011 };
1012
1013 if !source_component.is_instance_subtype_of(ty, target_component.types()) {
1014 bail!(
1015 "source instance is not compatible with target {import_ty} import `{import_name}`",
1016 import_ty = type_desc(import_ty)
1017 );
1018 }
1019 };
1020
1021 Ok(())
1022 }
1023
1024 pub fn encode(&self, options: EncodeOptions) -> Result<Vec<u8>> {
1026 let bytes = CompositionGraphEncoder::new(options, self).encode()?;
1027
1028 if options.validate {
1029 Validator::new()
1030 .validate_all(&bytes)
1031 .context("failed to validate encoded graph bytes")?;
1032 }
1033
1034 Ok(bytes)
1035 }
1036
1037 pub(crate) fn instantiation_order(&self) -> Result<Vec<InstanceId>> {
1042 toposort(&self.graph, None).map_err(|e| {
1043 let id = e.node_id();
1044 let instance = &self.instances[&id];
1045 anyhow!(
1046 "an instantiation of component `{name}` and its dependencies form a cycle in the instantiation graph",
1047 name = self.components[&instance.component].component.name,
1048 )
1049 })
1050 }
1051}
1052
1053#[cfg(test)]
1054mod test {
1055 use super::*;
1056 use pretty_assertions::assert_eq;
1057
1058 #[test]
1059 fn it_rejects_modules() -> Result<()> {
1060 let mut validator = Validator::new();
1061 match Component::from_bytes(&mut validator, "a", b"(module)".as_ref()) {
1062 Ok(_) => panic!("expected a failure to parse"),
1063 Err(e) => assert_eq!(
1064 format!("{e:#}"),
1065 "failed to parse component: the given data is not a WebAssembly component"
1066 ),
1067 }
1068
1069 Ok(())
1070 }
1071
1072 #[test]
1073 fn it_rejects_invalid_components() -> Result<()> {
1074 let mut validator = Validator::new();
1075 match Component::from_bytes(
1076 &mut validator,
1077 "a",
1078 b"(component (export \"x\" (func 0)))".as_ref(),
1079 ) {
1080 Ok(_) => panic!("expected a failure to parse"),
1081 Err(e) => assert_eq!(
1082 format!("{e:#}"),
1083 "failed to parse component: unknown function 0: function index out of bounds (at offset 0xb)"
1084 ),
1085 }
1086
1087 Ok(())
1088 }
1089
1090 #[test]
1091 fn it_ensures_unique_component_names() -> Result<()> {
1092 let mut graph = CompositionGraph::new();
1093 let mut validator = Validator::new();
1094 graph.add_component(Component::from_bytes(
1095 &mut validator,
1096 "a",
1097 b"(component)".as_ref(),
1098 )?)?;
1099
1100 match graph.add_component(Component::from_bytes(
1101 &mut validator,
1102 "a",
1103 b"(component)".as_ref(),
1104 )?) {
1105 Ok(_) => panic!("expected a failure to add component"),
1106 Err(e) => assert_eq!(format!("{e:#}"), "a component with name `a` already exists"),
1107 }
1108
1109 Ok(())
1110 }
1111
1112 #[test]
1113 fn it_fails_to_instantiate_a_missing_component() -> Result<()> {
1114 let mut graph = CompositionGraph::new();
1115 match graph.instantiate(ComponentId(0)) {
1116 Ok(_) => panic!("expected a failure to instantiate"),
1117 Err(e) => assert_eq!(format!("{e:#}"), "component does not exist in the graph"),
1118 }
1119
1120 Ok(())
1121 }
1122
1123 #[test]
1124 fn it_instantiates_a_component() -> Result<()> {
1125 let mut graph = CompositionGraph::new();
1126 let mut validator = Validator::new();
1127 let id = graph.add_component(Component::from_bytes(
1128 &mut validator,
1129 "a",
1130 b"(component)".as_ref(),
1131 )?)?;
1132 let id = graph.instantiate(id)?;
1133 assert_eq!(graph.get_component_of_instance(id).unwrap().1.name(), "a");
1134 Ok(())
1135 }
1136
1137 #[test]
1138 fn it_cannot_get_a_component_of_missing_instance() -> Result<()> {
1139 let graph = CompositionGraph::new();
1140 assert!(graph.get_component_of_instance(InstanceId(0)).is_none());
1141 Ok(())
1142 }
1143
1144 #[test]
1145 fn it_gets_a_component() -> Result<()> {
1146 let mut graph = CompositionGraph::new();
1147 let mut validator = Validator::new();
1148 let id = graph.add_component(Component::from_bytes(
1149 &mut validator,
1150 "a",
1151 b"(component)".as_ref(),
1152 )?)?;
1153 assert_eq!(graph.get_component(id).unwrap().name(), "a");
1154 assert_eq!(graph.get_component_by_name("a").unwrap().1.name(), "a");
1155 Ok(())
1156 }
1157
1158 #[test]
1159 fn it_removes_a_component() -> Result<()> {
1160 let mut graph = CompositionGraph::new();
1161 let mut validator = Validator::new();
1162 let a = graph.add_component(Component::from_bytes(
1163 &mut validator,
1164 "a",
1165 b"(component (import \"x\" (func)))".as_ref(),
1166 )?)?;
1167 let b = graph.add_component(Component::from_bytes(
1168 &mut validator,
1169 "b",
1170 b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1171 )?)?;
1172 let ai = graph.instantiate(a)?;
1173 let bi = graph.instantiate(b)?;
1174 graph.connect(bi, Some(0), ai, 0)?;
1175
1176 assert!(graph.get_component(a).is_some());
1177 assert!(graph.get_component(b).is_some());
1178 assert_eq!(graph.components.len(), 2);
1179 assert_eq!(graph.instances.len(), 2);
1180 assert_eq!(graph.graph.node_count(), 2);
1181 assert_eq!(graph.graph.edge_count(), 1);
1182
1183 graph.remove_component(b);
1184
1185 assert!(graph.get_component(a).is_some());
1186 assert!(graph.get_component(b).is_none());
1187 assert_eq!(graph.components.len(), 1);
1188 assert_eq!(graph.instances.len(), 1);
1189 assert_eq!(graph.graph.node_count(), 1);
1190 assert_eq!(graph.graph.edge_count(), 0);
1191 Ok(())
1192 }
1193
1194 #[test]
1195 fn it_removes_a_connection() -> Result<()> {
1196 let mut graph = CompositionGraph::new();
1197 let mut validator = Validator::new();
1198 let a = graph.add_component(Component::from_bytes(
1199 &mut validator,
1200 "a",
1201 b"(component (import \"x\" (func)))".as_ref(),
1202 )?)?;
1203 let b = graph.add_component(Component::from_bytes(
1204 &mut validator,
1205 "b",
1206 b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1207 )?)?;
1208 let ai = graph.instantiate(a)?;
1209 let bi = graph.instantiate(b)?;
1210 graph.connect(bi, Some(0), ai, 0)?;
1211
1212 assert_eq!(graph.graph.node_count(), 2);
1213 assert_eq!(graph.graph.edge_count(), 1);
1214
1215 graph.disconnect(bi, ai, 0)?;
1216
1217 assert_eq!(graph.graph.node_count(), 2);
1218 assert_eq!(graph.graph.edge_count(), 0);
1219 Ok(())
1220 }
1221
1222 #[test]
1223 fn it_requires_source_to_disconnect() -> Result<()> {
1224 let mut graph = CompositionGraph::new();
1225 let mut validator = Validator::new();
1226 let a = graph.add_component(Component::from_bytes(
1227 &mut validator,
1228 "a",
1229 b"(component (import \"x\" (func)))".as_ref(),
1230 )?)?;
1231 let b = graph.add_component(Component::from_bytes(
1232 &mut validator,
1233 "b",
1234 b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1235 )?)?;
1236 let ai = graph.instantiate(a)?;
1237 let bi = graph.instantiate(b)?;
1238 graph.connect(bi, Some(0), ai, 0)?;
1239
1240 match graph.disconnect(101, ai, 0) {
1241 Ok(_) => panic!("expected a failure to disconnect"),
1242 Err(e) => assert_eq!(
1243 format!("{e:#}"),
1244 "the source instance does not exist in the graph"
1245 ),
1246 }
1247
1248 Ok(())
1249 }
1250
1251 #[test]
1252 fn it_requires_a_target_to_disconnect() -> Result<()> {
1253 let mut graph = CompositionGraph::new();
1254 let mut validator = Validator::new();
1255 let a = graph.add_component(Component::from_bytes(
1256 &mut validator,
1257 "a",
1258 b"(component (import \"x\" (func)))".as_ref(),
1259 )?)?;
1260 let b = graph.add_component(Component::from_bytes(
1261 &mut validator,
1262 "b",
1263 b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1264 )?)?;
1265 let ai = graph.instantiate(a)?;
1266 let bi = graph.instantiate(b)?;
1267 graph.connect(bi, Some(0), ai, 0)?;
1268
1269 match graph.disconnect(bi, 101, 0) {
1270 Ok(_) => panic!("expected a failure to disconnect"),
1271 Err(e) => assert_eq!(
1272 format!("{e:#}"),
1273 "the target instance does not exist in the graph"
1274 ),
1275 }
1276
1277 Ok(())
1278 }
1279
1280 #[test]
1281 fn it_validates_connections() -> Result<()> {
1282 let mut graph = CompositionGraph::new();
1283 let mut validator = Validator::new();
1284 let a = graph.add_component(Component::from_bytes(
1285 &mut validator,
1286 "a",
1287 b"(component (import \"i1\" (func)) (import \"i2\" (instance (export \"no\" (func)))))"
1288 .as_ref(),
1289 )?)?;
1290 let b = graph.add_component(Component::from_bytes(
1291 &mut validator,
1292 "b",
1293 b"(component (import \"i1\" (func)) (import \"i2\" (core module)) (export \"e1\" (func 0)) (export \"e2\" (core module 0)))".as_ref(),
1294 )?)?;
1295 let ai = graph.instantiate(a)?;
1296 let bi = graph.instantiate(b)?;
1297
1298 match graph.connect(ai, None::<ExportIndex>, ai, 0) {
1299 Ok(_) => panic!("expected a failure to connect"),
1300 Err(e) => assert_eq!(
1301 format!("{e:#}"),
1302 "an instance cannot be connected to itself"
1303 ),
1304 }
1305
1306 match graph.connect(ai, Some(0), bi, 0) {
1307 Ok(_) => panic!("expected a failure to connect"),
1308 Err(e) => assert_eq!(format!("{e:#}"), "the source export index is invalid"),
1309 }
1310
1311 match graph.connect(101, Some(0), ai, 0) {
1312 Ok(_) => panic!("expected a failure to connect"),
1313 Err(e) => assert_eq!(
1314 format!("{e:#}"),
1315 "the source instance does not exist in the graph"
1316 ),
1317 }
1318
1319 match graph.connect(bi, Some(0), 101, 0) {
1320 Ok(_) => panic!("expected a failure to connect"),
1321 Err(e) => assert_eq!(
1322 format!("{e:#}"),
1323 "the target instance does not exist in the graph"
1324 ),
1325 }
1326
1327 match graph.connect(bi, Some(101), ai, 0) {
1328 Ok(_) => panic!("expected a failure to connect"),
1329 Err(e) => assert_eq!(format!("{e:#}"), "the source export index is invalid"),
1330 }
1331
1332 match graph.connect(bi, Some(0), ai, 101) {
1333 Ok(_) => panic!("expected a failure to connect"),
1334 Err(e) => assert_eq!(format!("{e:#}"), "the target import index is invalid"),
1335 }
1336
1337 match graph.connect(bi, Some(1), ai, 0) {
1338 Ok(_) => panic!("expected a failure to connect"),
1339 Err(e) => assert_eq!(
1340 format!("{e:#}"),
1341 "source module export `e2` is not compatible with target function import `i1`"
1342 ),
1343 }
1344
1345 match graph.connect(bi, None::<ExportIndex>, ai, 0) {
1346 Ok(_) => panic!("expected a failure to connect"),
1347 Err(e) => assert_eq!(
1348 format!("{e:#}"),
1349 "source instance is not compatible with target function import `i1`"
1350 ),
1351 }
1352
1353 match graph.connect(bi, None::<ExportIndex>, ai, 1) {
1354 Ok(_) => panic!("expected a failure to connect"),
1355 Err(e) => assert_eq!(
1356 format!("{e:#}"),
1357 "source instance is not compatible with target instance import `i2`"
1358 ),
1359 }
1360
1361 Ok(())
1362 }
1363
1364 #[test]
1365 fn it_cannot_encode_a_cycle() -> Result<()> {
1366 let mut graph = CompositionGraph::new();
1367 let mut validator = Validator::new();
1368 let a = graph.add_component(Component::from_bytes(
1369 &mut validator,
1370 "a",
1371 b"(component (import \"i1\" (func)) (export \"e1\" (func 0)))".as_ref(),
1372 )?)?;
1373 let b = graph.add_component(Component::from_bytes(
1374 &mut validator,
1375 "b",
1376 b"(component (import \"i1\" (func)) (export \"e1\" (func 0)))".as_ref(),
1377 )?)?;
1378 let ai = graph.instantiate(a)?;
1379 let bi = graph.instantiate(b)?;
1380
1381 graph.connect(ai, Some(0), bi, 0)?;
1382 graph.connect(bi, Some(0), ai, 0)?;
1383
1384 match graph.encode(EncodeOptions {
1385 define_components: false,
1386 export: None,
1387 validate: true,
1388 }) {
1389 Ok(_) => panic!("graph should not encode"),
1390 Err(e) => assert_eq!(
1391 format!("{e:#}"),
1392 "an instantiation of component `b` and its dependencies form a cycle in the instantiation graph"
1393 ),
1394 }
1395
1396 Ok(())
1397 }
1398
1399 #[test]
1400 fn it_encodes_an_empty_component() -> Result<()> {
1401 let mut graph = CompositionGraph::new();
1402 let mut validator = Validator::new();
1403 graph.add_component(Component::from_bytes(
1404 &mut validator,
1405 "a",
1406 b"(component)".as_ref(),
1407 )?)?;
1408 graph.add_component(Component::from_bytes(
1409 &mut validator,
1410 "b",
1411 b"(component)".as_ref(),
1412 )?)?;
1413
1414 let encoded = graph.encode(EncodeOptions {
1415 define_components: false,
1416 export: None,
1417 validate: true,
1418 })?;
1419
1420 let wat = wasmprinter::print_bytes(encoded)?;
1421 assert_eq!(
1422 r#"(component)
1423"#,
1424 wat
1425 );
1426
1427 Ok(())
1428 }
1429
1430 #[test]
1431 fn it_encodes_component_imports() -> Result<()> {
1432 let mut graph = CompositionGraph::new();
1433 let mut validator = Validator::new();
1434 graph.add_component(Component::from_bytes(
1436 &mut validator,
1437 "a",
1438 b"(component)".as_ref(),
1439 )?)?;
1440 let b = graph.add_component(Component::from_bytes(
1441 &mut validator,
1442 "b",
1443 b"(component)".as_ref(),
1444 )?)?;
1445 graph.instantiate(b)?;
1446
1447 let encoded = graph.encode(EncodeOptions {
1448 define_components: false,
1449 export: None,
1450 validate: true,
1451 })?;
1452
1453 let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1454 assert_eq!(
1455 r#"(component
1456 (type (;0;)
1457 (component)
1458 )
1459 (import "b" (component (;0;) (type 0)))
1460 (instance (;0;) (instantiate 0))
1461)
1462"#,
1463 wat
1464 );
1465
1466 Ok(())
1467 }
1468
1469 #[test]
1470 fn it_encodes_defined_components() -> Result<()> {
1471 let mut graph = CompositionGraph::new();
1472 let mut validator = Validator::new();
1473 graph.add_component(Component::from_bytes(
1475 &mut validator,
1476 "a",
1477 b"(component)".as_ref(),
1478 )?)?;
1479 let b = graph.add_component(Component::from_bytes(
1480 &mut validator,
1481 "b",
1482 b"(component)".as_ref(),
1483 )?)?;
1484 graph.instantiate(b)?;
1485
1486 let encoded = graph.encode(EncodeOptions {
1487 define_components: true,
1488 export: None,
1489 validate: true,
1490 })?;
1491
1492 let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1493 assert_eq!(
1494 r#"(component
1495 (component (;0;))
1496 (instance (;0;) (instantiate 0))
1497)
1498"#,
1499 wat
1500 );
1501
1502 Ok(())
1503 }
1504
1505 #[test]
1506 fn it_encodes_a_simple_composition() -> Result<()> {
1507 let mut graph = CompositionGraph::new();
1508 let mut validator = Validator::new();
1509 let a = graph.add_component(Component::from_bytes(
1510 &mut validator,
1511 "a",
1512 b"(component
1513 (type (tuple u32 u32))
1514 (import \"i1\" (instance (export \"e1\" (func)) (export \"e3\" (func (param \"a\" u32)))))
1515 (import \"i2\" (func))
1516 (import \"i3\" (component))
1517 (import \"i4\" (core module))
1518 (import \"i5\" (type (eq 0)))
1519 (export \"e1\" (instance 0))
1520 (export \"e2\" (func 0))
1521 (export \"e3\" (component 0))
1522 (export \"e4\" (core module 0))
1523 (export \"e5\" (type 1))
1524)"
1525 .as_ref(),
1526 )?)?;
1527 let b = graph.add_component(Component::from_bytes(
1528 &mut validator,
1529 "b",
1530 b"(component
1531 (type (tuple u32 u32))
1532 (import \"i1\" (instance (export \"e2\" (func)) (export \"e3\" (func (param \"a\" u32)))))
1533 (import \"i2\" (func))
1534 (import \"i3\" (component))
1535 (import \"i4\" (core module))
1536 (import \"i5\" (type (eq 0)))
1537)"
1538 .as_ref(),
1539 )?)?;
1540
1541 let ai = graph.instantiate(a)?;
1542 let bi1 = graph.instantiate(b)?;
1543 let bi2 = graph.instantiate(b)?;
1544 let bi3 = graph.instantiate(b)?;
1545
1546 for i in 1..=3 {
1548 graph.connect(ai, Some(i), bi1, i)?;
1549 graph.connect(ai, Some(i), bi2, i)?;
1550 graph.connect(ai, Some(i), bi3, i)?;
1551 }
1552
1553 let encoded = graph.encode(EncodeOptions {
1554 define_components: true,
1555 export: None,
1556 validate: true,
1557 })?;
1558
1559 let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1560 assert_eq!(
1561 r#"(component
1562 (type (;0;)
1563 (instance
1564 (type (;0;) (func))
1565 (export (;0;) "e1" (func (type 0)))
1566 (type (;1;) (func (param "a" u32)))
1567 (export (;1;) "e3" (func (type 1)))
1568 (type (;2;) (func))
1569 (export (;2;) "e2" (func (type 2)))
1570 )
1571 )
1572 (import "i1" (instance (;0;) (type 0)))
1573 (type (;1;) (func))
1574 (import "i2" (func (;0;) (type 1)))
1575 (type (;2;)
1576 (component)
1577 )
1578 (import "i3" (component (;0;) (type 2)))
1579 (core type (;0;)
1580 (module)
1581 )
1582 (import "i4" (core module (;0;) (type 0)))
1583 (type (;3;) (tuple u32 u32))
1584 (import "i5" (type (;4;) (eq 3)))
1585 (component (;1;)
1586 (type (;0;) (tuple u32 u32))
1587 (type (;1;)
1588 (instance
1589 (type (;0;) (func))
1590 (export (;0;) "e1" (func (type 0)))
1591 (type (;1;) (func (param "a" u32)))
1592 (export (;1;) "e3" (func (type 1)))
1593 )
1594 )
1595 (import "i1" (instance (;0;) (type 1)))
1596 (type (;2;) (func))
1597 (import "i2" (func (;0;) (type 2)))
1598 (type (;3;)
1599 (component)
1600 )
1601 (import "i3" (component (;0;) (type 3)))
1602 (core type (;0;)
1603 (module)
1604 )
1605 (import "i4" (core module (;0;) (type 0)))
1606 (import "i5" (type (;4;) (eq 0)))
1607 (export (;1;) "e1" (instance 0))
1608 (export (;1;) "e2" (func 0))
1609 (export (;1;) "e3" (component 0))
1610 (export (;1;) "e4" (core module 0))
1611 (export (;5;) "e5" (type 1))
1612 )
1613 (component (;2;)
1614 (type (;0;) (tuple u32 u32))
1615 (type (;1;)
1616 (instance
1617 (type (;0;) (func))
1618 (export (;0;) "e2" (func (type 0)))
1619 (type (;1;) (func (param "a" u32)))
1620 (export (;1;) "e3" (func (type 1)))
1621 )
1622 )
1623 (import "i1" (instance (;0;) (type 1)))
1624 (type (;2;) (func))
1625 (import "i2" (func (;0;) (type 2)))
1626 (type (;3;)
1627 (component)
1628 )
1629 (import "i3" (component (;0;) (type 3)))
1630 (core type (;0;)
1631 (module)
1632 )
1633 (import "i4" (core module (;0;) (type 0)))
1634 (import "i5" (type (;4;) (eq 0)))
1635 )
1636 (instance (;1;) (instantiate 1
1637 (with "i1" (instance 0))
1638 (with "i2" (func 0))
1639 (with "i3" (component 0))
1640 (with "i4" (core module 0))
1641 (with "i5" (type 4))
1642 )
1643 )
1644 (alias export 1 "e2" (func (;1;)))
1645 (alias export 1 "e3" (component (;3;)))
1646 (alias export 1 "e4" (core module (;1;)))
1647 (instance (;2;) (instantiate 2
1648 (with "i2" (func 1))
1649 (with "i3" (component 3))
1650 (with "i4" (core module 1))
1651 (with "i1" (instance 0))
1652 (with "i5" (type 4))
1653 )
1654 )
1655 (instance (;3;) (instantiate 2
1656 (with "i2" (func 1))
1657 (with "i3" (component 3))
1658 (with "i4" (core module 1))
1659 (with "i1" (instance 0))
1660 (with "i5" (type 4))
1661 )
1662 )
1663 (instance (;4;) (instantiate 2
1664 (with "i2" (func 1))
1665 (with "i3" (component 3))
1666 (with "i4" (core module 1))
1667 (with "i1" (instance 0))
1668 (with "i5" (type 4))
1669 )
1670 )
1671)
1672"#,
1673 wat
1674 );
1675
1676 Ok(())
1677 }
1678}