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.0.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.0.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_entity_type_of_export(name)?,
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_entity_type_of_import(name)?,
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, 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: im_rc::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
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
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_entity_type_of_import(import_name)
595 .unwrap();
596
597 if let ComponentEntityType::Instance(instance_id) = ty {
598 for (export_name, ty) in &component.types[instance_id].exports {
599 if let ComponentEntityType::Type {
601 referenced: ComponentAnyTypeId::Resource(resource_id),
602 ..
603 } = ty
604 {
605 let set = resource_imports
606 .entry(vec![import_name.to_string(), export_name.to_string()])
607 .or_default();
608
609 if let Some(pair) = resource_mapping.map.get(&resource_id.resource()) {
610 set.insert(*pair);
611 }
612 set.insert((*component_id, resource_id.resource()));
613 }
614 }
615 }
616 }
617 }
618
619 for resources in resource_imports.values() {
620 match &resources.iter().copied().collect::<Vec<_>>()[..] {
621 [] => unreachable!(),
622 [_] => {}
623 [first, rest @ ..] => {
624 resource_mapping.map.insert(first.1, *first);
625 for resource in rest {
626 resource_mapping.map.insert(resource.1, *first);
627 }
628 }
629 }
630 }
631 }
632
633 pub(crate) fn try_connection(
647 &self,
648 export_component: ComponentId,
649 mut export_type: ComponentEntityType,
650 export_types: TypesRef,
651 mut import_type: ComponentEntityType,
652 import_types: TypesRef,
653 ) -> bool {
654 let resource_mapping = self.resource_mapping.borrow().clone();
655
656 if let Some(resource_mapping) = resource_mapping.add_pairs(
657 export_component,
658 export_type,
659 export_types,
660 import_type,
661 import_types,
662 ) {
663 let remapping = &mut resource_mapping.remapping();
664 let mut context = SubtypeCx::new_with_refs(export_types, import_types);
665
666 context
667 .a
668 .remap_component_entity(&mut export_type, remapping);
669 remapping.reset_type_cache();
670
671 context
672 .b
673 .remap_component_entity(&mut import_type, remapping);
674 remapping.reset_type_cache();
675
676 let v = context.component_entity_type(&export_type, &import_type, 0);
677 if v.is_ok() {
678 *self.resource_mapping.borrow_mut() = resource_mapping;
679 true
680 } else {
681 false
682 }
683 } else {
684 false
685 }
686 }
687
688 pub(crate) fn remapping_map<'b>(
689 &'b self,
690 ) -> HashMap<ResourceId, (&'b Component<'a>, ResourceId)> {
691 let mut map = HashMap::new();
692 for (old, (component, new)) in &self.resource_mapping.borrow().map {
693 if old != new {
694 let component = &self.components.get(component).unwrap().component;
695 map.insert(*old, (component, *new));
696 }
697 }
698 map
699 }
700
701 pub fn add_component(&mut self, component: Component<'a>) -> Result<ComponentId> {
705 let id = match self.names.entry(component.name.clone()) {
706 Entry::Occupied(e) => {
707 bail!(
708 "a component with name `{name}` already exists",
709 name = e.key()
710 )
711 }
712 Entry::Vacant(e) => *e.insert(ComponentId::next()?),
713 };
714
715 log::info!(
716 "adding WebAssembly component `{name}` ({id}) to the graph",
717 name = component.name(),
718 );
719
720 let entry = ComponentEntry {
721 component,
722 instances: HashSet::new(),
723 };
724
725 assert!(self.components.insert(id, entry).is_none());
726
727 if self.components.len() > 1 {
728 self.unify_imported_resources();
729 }
730
731 Ok(id)
732 }
733
734 pub fn get_component(&self, id: impl Into<ComponentId>) -> Option<&Component<'a>> {
736 self.components.get(&id.into()).map(|e| &e.component)
737 }
738
739 pub fn get_component_by_name(&self, name: &str) -> Option<(ComponentId, &Component<'a>)> {
741 let id = self.names.get(name)?;
742 let entry = &self.components[id];
743 Some((*id, &entry.component))
744 }
745
746 pub fn remove_component(&mut self, id: impl Into<ComponentId>) {
751 let id = id.into();
752 if let Some(entry) = self.components.swap_remove(&id) {
753 log::info!(
754 "removing WebAssembly component `{name}` ({id}) from the graph",
755 name = entry.component.name(),
756 );
757
758 assert!(self.names.remove(&entry.component.name).is_some());
759
760 for instance_id in entry.instances.iter().copied() {
761 self.instances.swap_remove(&instance_id);
762
763 for (_, target_id, map) in self
765 .graph
766 .edges_directed(instance_id, EdgeDirection::Outgoing)
767 {
768 let target = self.instances.get_mut(&target_id).unwrap();
769 for index in map.keys() {
770 target.connected.swap_remove(index);
771 }
772 }
773
774 self.graph.remove_node(instance_id);
775 }
776 }
777 }
778
779 pub fn instantiate(&mut self, id: impl Into<ComponentId>) -> Result<InstanceId> {
781 let id = id.into();
782 let entry = self
783 .components
784 .get_mut(&id)
785 .ok_or_else(|| anyhow!("component does not exist in the graph"))?;
786
787 let instance_id = InstanceId::next()?;
788
789 log::info!(
790 "instantiating WebAssembly component `{name}` ({id}) with instance identifier {instance_id}",
791 name = entry.component.name(),
792 );
793
794 self.instances.insert(
795 instance_id,
796 Instance {
797 component: id,
798 connected: Default::default(),
799 },
800 );
801
802 entry.instances.insert(instance_id);
803
804 Ok(instance_id)
805 }
806
807 pub fn get_component_of_instance(
809 &self,
810 id: impl Into<InstanceId>,
811 ) -> Option<(ComponentId, &Component<'_>)> {
812 let id = id.into();
813 let instance = self.instances.get(&id)?;
814
815 Some((
816 instance.component,
817 self.get_component(instance.component).unwrap(),
818 ))
819 }
820
821 pub fn remove_instance(&mut self, id: impl Into<InstanceId>) {
825 let id = id.into();
826 if let Some(instance) = self.instances.swap_remove(&id) {
827 let entry = self.components.get_mut(&instance.component).unwrap();
828
829 log::info!(
830 "removing instance ({id}) of component `{name}` ({cid}) from the graph",
831 name = entry.component.name(),
832 cid = instance.component.0,
833 );
834
835 entry.instances.remove(&id);
836
837 for (_, target, map) in self.graph.edges_directed(id, EdgeDirection::Outgoing) {
839 let target = self.instances.get_mut(&target).unwrap();
840 for index in map.keys() {
841 target.connected.swap_remove(index);
842 }
843 }
844
845 self.graph.remove_node(id);
846 }
847 }
848
849 pub fn connect(
856 &mut self,
857 source: impl Into<InstanceId> + Copy,
858 source_export: Option<impl Into<ExportIndex> + Copy>,
859 target: impl Into<InstanceId> + Copy,
860 target_import: impl Into<ImportIndex> + Copy,
861 ) -> Result<()> {
862 self.validate_connection(source, source_export, target, target_import)?;
863
864 let source = source.into();
865 let source_export = source_export.map(Into::into);
866 let target = target.into();
867 let target_import = target_import.into();
868
869 match source_export {
870 Some(export) => log::info!(
871 "connecting export {export} of instance {source} to import `{target_import}` of instance {target}"
872 ),
873 None => log::info!(
874 "connecting instance {source} to import {target_import} of instance {target}"
875 ),
876 }
877
878 self.instances
879 .get_mut(&target)
880 .unwrap()
881 .connected
882 .insert(target_import);
883
884 if let Some(map) = self.graph.edge_weight_mut(source, target) {
885 assert!(map.insert(target_import, source_export).is_none());
886 } else {
887 let mut map = IndexMap::new();
888 map.insert(target_import, source_export);
889 self.graph.add_edge(source, target, map);
890 }
891
892 Ok(())
893 }
894
895 pub fn disconnect(
902 &mut self,
903 source: impl Into<InstanceId>,
904 target: impl Into<InstanceId>,
905 target_import: impl Into<ImportIndex>,
906 ) -> Result<()> {
907 let source = source.into();
908 let target = target.into();
909 let target_import = target_import.into();
910
911 log::info!("disconnecting import {target_import} of instance {target}");
912
913 if !self.instances.contains_key(&source) {
914 bail!("the source instance does not exist in the graph");
915 }
916
917 let target_instance = self
918 .instances
919 .get_mut(&target)
920 .ok_or_else(|| anyhow!("the target instance does not exist in the graph"))?;
921
922 target_instance.connected.swap_remove(&target_import);
923
924 let remove_edge = if let Some(set) = self.graph.edge_weight_mut(source, target) {
925 set.swap_remove(&target_import);
926 set.is_empty()
927 } else {
928 false
929 };
930
931 if remove_edge {
932 self.graph.remove_edge(source, target);
933 }
934
935 Ok(())
936 }
937
938 pub fn validate_connection(
945 &self,
946 source: impl Into<InstanceId>,
947 source_export: Option<impl Into<ExportIndex>>,
948 target: impl Into<InstanceId>,
949 target_import: impl Into<ImportIndex>,
950 ) -> Result<()> {
951 let source = source.into();
952 let source_export = source_export.map(Into::into);
953 let target = target.into();
954 let target_import = target_import.into();
955
956 if source == target {
957 bail!("an instance cannot be connected to itself");
958 }
959
960 let source_instance = self
961 .instances
962 .get(&source)
963 .ok_or_else(|| anyhow!("the source instance does not exist in the graph"))?;
964
965 let source_component = &self.components[&source_instance.component].component;
966
967 let target_instance = self
968 .instances
969 .get(&target)
970 .ok_or_else(|| anyhow!("the target instance does not exist in the graph"))?;
971
972 let target_component = &self.components[&target_instance.component].component;
973 let (import_name, import_ty) = target_component
974 .import_entity_type(target_import)
975 .ok_or_else(|| anyhow!("the target import index is invalid"))?;
976
977 if target_instance.connected.contains(&target_import) {
978 bail!(
979 "{import_ty} import `{import_name}` is already connected",
980 import_ty = type_desc(import_ty)
981 );
982 }
983
984 if let Some(export_index) = source_export {
985 let (export_name, export_ty) = source_component
986 .export_entity_type(export_index)
987 .ok_or_else(|| anyhow!("the source export index is invalid"))?;
988
989 if !self.try_connection(
990 source_instance.component,
991 export_ty,
992 source_component.types(),
993 import_ty,
994 target_component.types(),
995 ) {
996 bail!(
997 "source {export_ty} export `{export_name}` is not compatible with target \
998 {import_ty} import `{import_name}`",
999 export_ty = type_desc(export_ty),
1000 import_ty = type_desc(import_ty),
1001 );
1002 }
1003 } else {
1004 let ty = match import_ty {
1005 ComponentEntityType::Instance(id) => id,
1006 _ => bail!(
1007 "source instance is not compatible with target {import_ty} import `{import_name}`",
1008 import_ty = type_desc(import_ty)
1009 ),
1010 };
1011
1012 if !source_component.is_instance_subtype_of(ty, target_component.types()) {
1013 bail!(
1014 "source instance is not compatible with target {import_ty} import `{import_name}`",
1015 import_ty = type_desc(import_ty)
1016 );
1017 }
1018 };
1019
1020 Ok(())
1021 }
1022
1023 pub fn encode(&self, options: EncodeOptions) -> Result<Vec<u8>> {
1025 let bytes = CompositionGraphEncoder::new(options, self).encode()?;
1026
1027 if options.validate {
1028 Validator::new()
1029 .validate_all(&bytes)
1030 .context("failed to validate encoded graph bytes")?;
1031 }
1032
1033 Ok(bytes)
1034 }
1035
1036 pub(crate) fn instantiation_order(&self) -> Result<Vec<InstanceId>> {
1041 toposort(&self.graph, None).map_err(|e| {
1042 let id = e.node_id();
1043 let instance = &self.instances[&id];
1044 anyhow!(
1045 "an instantiation of component `{name}` and its dependencies form a cycle in the instantiation graph",
1046 name = self.components[&instance.component].component.name,
1047 )
1048 })
1049 }
1050}
1051
1052#[cfg(test)]
1053mod test {
1054 use super::*;
1055 use pretty_assertions::assert_eq;
1056
1057 #[test]
1058 fn it_rejects_modules() -> Result<()> {
1059 let mut validator = Validator::new();
1060 match Component::from_bytes(&mut validator, "a", b"(module)".as_ref()) {
1061 Ok(_) => panic!("expected a failure to parse"),
1062 Err(e) => assert_eq!(
1063 format!("{e:#}"),
1064 "failed to parse component: the given data is not a WebAssembly component"
1065 ),
1066 }
1067
1068 Ok(())
1069 }
1070
1071 #[test]
1072 fn it_rejects_invalid_components() -> Result<()> {
1073 let mut validator = Validator::new();
1074 match Component::from_bytes(
1075 &mut validator,
1076 "a",
1077 b"(component (export \"x\" (func 0)))".as_ref(),
1078 ) {
1079 Ok(_) => panic!("expected a failure to parse"),
1080 Err(e) => assert_eq!(
1081 format!("{e:#}"),
1082 "failed to parse component: unknown function 0: function index out of bounds (at offset 0xb)"
1083 ),
1084 }
1085
1086 Ok(())
1087 }
1088
1089 #[test]
1090 fn it_ensures_unique_component_names() -> Result<()> {
1091 let mut graph = CompositionGraph::new();
1092 let mut validator = Validator::new();
1093 graph.add_component(Component::from_bytes(
1094 &mut validator,
1095 "a",
1096 b"(component)".as_ref(),
1097 )?)?;
1098
1099 match graph.add_component(Component::from_bytes(
1100 &mut validator,
1101 "a",
1102 b"(component)".as_ref(),
1103 )?) {
1104 Ok(_) => panic!("expected a failure to add component"),
1105 Err(e) => assert_eq!(format!("{e:#}"), "a component with name `a` already exists"),
1106 }
1107
1108 Ok(())
1109 }
1110
1111 #[test]
1112 fn it_fails_to_instantiate_a_missing_component() -> Result<()> {
1113 let mut graph = CompositionGraph::new();
1114 match graph.instantiate(ComponentId(0)) {
1115 Ok(_) => panic!("expected a failure to instantiate"),
1116 Err(e) => assert_eq!(format!("{e:#}"), "component does not exist in the graph"),
1117 }
1118
1119 Ok(())
1120 }
1121
1122 #[test]
1123 fn it_instantiates_a_component() -> Result<()> {
1124 let mut graph = CompositionGraph::new();
1125 let mut validator = Validator::new();
1126 let id = graph.add_component(Component::from_bytes(
1127 &mut validator,
1128 "a",
1129 b"(component)".as_ref(),
1130 )?)?;
1131 let id = graph.instantiate(id)?;
1132 assert_eq!(graph.get_component_of_instance(id).unwrap().1.name(), "a");
1133 Ok(())
1134 }
1135
1136 #[test]
1137 fn it_cannot_get_a_component_of_missing_instance() -> Result<()> {
1138 let graph = CompositionGraph::new();
1139 assert!(graph.get_component_of_instance(InstanceId(0)).is_none());
1140 Ok(())
1141 }
1142
1143 #[test]
1144 fn it_gets_a_component() -> Result<()> {
1145 let mut graph = CompositionGraph::new();
1146 let mut validator = Validator::new();
1147 let id = graph.add_component(Component::from_bytes(
1148 &mut validator,
1149 "a",
1150 b"(component)".as_ref(),
1151 )?)?;
1152 assert_eq!(graph.get_component(id).unwrap().name(), "a");
1153 assert_eq!(graph.get_component_by_name("a").unwrap().1.name(), "a");
1154 Ok(())
1155 }
1156
1157 #[test]
1158 fn it_removes_a_component() -> Result<()> {
1159 let mut graph = CompositionGraph::new();
1160 let mut validator = Validator::new();
1161 let a = graph.add_component(Component::from_bytes(
1162 &mut validator,
1163 "a",
1164 b"(component (import \"x\" (func)))".as_ref(),
1165 )?)?;
1166 let b = graph.add_component(Component::from_bytes(
1167 &mut validator,
1168 "b",
1169 b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1170 )?)?;
1171 let ai = graph.instantiate(a)?;
1172 let bi = graph.instantiate(b)?;
1173 graph.connect(bi, Some(0), ai, 0)?;
1174
1175 assert!(graph.get_component(a).is_some());
1176 assert!(graph.get_component(b).is_some());
1177 assert_eq!(graph.components.len(), 2);
1178 assert_eq!(graph.instances.len(), 2);
1179 assert_eq!(graph.graph.node_count(), 2);
1180 assert_eq!(graph.graph.edge_count(), 1);
1181
1182 graph.remove_component(b);
1183
1184 assert!(graph.get_component(a).is_some());
1185 assert!(graph.get_component(b).is_none());
1186 assert_eq!(graph.components.len(), 1);
1187 assert_eq!(graph.instances.len(), 1);
1188 assert_eq!(graph.graph.node_count(), 1);
1189 assert_eq!(graph.graph.edge_count(), 0);
1190 Ok(())
1191 }
1192
1193 #[test]
1194 fn it_removes_a_connection() -> Result<()> {
1195 let mut graph = CompositionGraph::new();
1196 let mut validator = Validator::new();
1197 let a = graph.add_component(Component::from_bytes(
1198 &mut validator,
1199 "a",
1200 b"(component (import \"x\" (func)))".as_ref(),
1201 )?)?;
1202 let b = graph.add_component(Component::from_bytes(
1203 &mut validator,
1204 "b",
1205 b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1206 )?)?;
1207 let ai = graph.instantiate(a)?;
1208 let bi = graph.instantiate(b)?;
1209 graph.connect(bi, Some(0), ai, 0)?;
1210
1211 assert_eq!(graph.graph.node_count(), 2);
1212 assert_eq!(graph.graph.edge_count(), 1);
1213
1214 graph.disconnect(bi, ai, 0)?;
1215
1216 assert_eq!(graph.graph.node_count(), 2);
1217 assert_eq!(graph.graph.edge_count(), 0);
1218 Ok(())
1219 }
1220
1221 #[test]
1222 fn it_requires_source_to_disconnect() -> Result<()> {
1223 let mut graph = CompositionGraph::new();
1224 let mut validator = Validator::new();
1225 let a = graph.add_component(Component::from_bytes(
1226 &mut validator,
1227 "a",
1228 b"(component (import \"x\" (func)))".as_ref(),
1229 )?)?;
1230 let b = graph.add_component(Component::from_bytes(
1231 &mut validator,
1232 "b",
1233 b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1234 )?)?;
1235 let ai = graph.instantiate(a)?;
1236 let bi = graph.instantiate(b)?;
1237 graph.connect(bi, Some(0), ai, 0)?;
1238
1239 match graph.disconnect(101, ai, 0) {
1240 Ok(_) => panic!("expected a failure to disconnect"),
1241 Err(e) => assert_eq!(
1242 format!("{e:#}"),
1243 "the source instance does not exist in the graph"
1244 ),
1245 }
1246
1247 Ok(())
1248 }
1249
1250 #[test]
1251 fn it_requires_a_target_to_disconnect() -> Result<()> {
1252 let mut graph = CompositionGraph::new();
1253 let mut validator = Validator::new();
1254 let a = graph.add_component(Component::from_bytes(
1255 &mut validator,
1256 "a",
1257 b"(component (import \"x\" (func)))".as_ref(),
1258 )?)?;
1259 let b = graph.add_component(Component::from_bytes(
1260 &mut validator,
1261 "b",
1262 b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1263 )?)?;
1264 let ai = graph.instantiate(a)?;
1265 let bi = graph.instantiate(b)?;
1266 graph.connect(bi, Some(0), ai, 0)?;
1267
1268 match graph.disconnect(bi, 101, 0) {
1269 Ok(_) => panic!("expected a failure to disconnect"),
1270 Err(e) => assert_eq!(
1271 format!("{e:#}"),
1272 "the target instance does not exist in the graph"
1273 ),
1274 }
1275
1276 Ok(())
1277 }
1278
1279 #[test]
1280 fn it_validates_connections() -> Result<()> {
1281 let mut graph = CompositionGraph::new();
1282 let mut validator = Validator::new();
1283 let a = graph.add_component(Component::from_bytes(
1284 &mut validator,
1285 "a",
1286 b"(component (import \"i1\" (func)) (import \"i2\" (instance (export \"no\" (func)))))"
1287 .as_ref(),
1288 )?)?;
1289 let b = graph.add_component(Component::from_bytes(
1290 &mut validator,
1291 "b",
1292 b"(component (import \"i1\" (func)) (import \"i2\" (core module)) (export \"e1\" (func 0)) (export \"e2\" (core module 0)))".as_ref(),
1293 )?)?;
1294 let ai = graph.instantiate(a)?;
1295 let bi = graph.instantiate(b)?;
1296
1297 match graph.connect(ai, None::<ExportIndex>, ai, 0) {
1298 Ok(_) => panic!("expected a failure to connect"),
1299 Err(e) => assert_eq!(
1300 format!("{e:#}"),
1301 "an instance cannot be connected to itself"
1302 ),
1303 }
1304
1305 match graph.connect(ai, Some(0), bi, 0) {
1306 Ok(_) => panic!("expected a failure to connect"),
1307 Err(e) => assert_eq!(format!("{e:#}"), "the source export index is invalid"),
1308 }
1309
1310 match graph.connect(101, Some(0), ai, 0) {
1311 Ok(_) => panic!("expected a failure to connect"),
1312 Err(e) => assert_eq!(
1313 format!("{e:#}"),
1314 "the source instance does not exist in the graph"
1315 ),
1316 }
1317
1318 match graph.connect(bi, Some(0), 101, 0) {
1319 Ok(_) => panic!("expected a failure to connect"),
1320 Err(e) => assert_eq!(
1321 format!("{e:#}"),
1322 "the target instance does not exist in the graph"
1323 ),
1324 }
1325
1326 match graph.connect(bi, Some(101), ai, 0) {
1327 Ok(_) => panic!("expected a failure to connect"),
1328 Err(e) => assert_eq!(format!("{e:#}"), "the source export index is invalid"),
1329 }
1330
1331 match graph.connect(bi, Some(0), ai, 101) {
1332 Ok(_) => panic!("expected a failure to connect"),
1333 Err(e) => assert_eq!(format!("{e:#}"), "the target import index is invalid"),
1334 }
1335
1336 match graph.connect(bi, Some(1), ai, 0) {
1337 Ok(_) => panic!("expected a failure to connect"),
1338 Err(e) => assert_eq!(
1339 format!("{e:#}"),
1340 "source module export `e2` is not compatible with target function import `i1`"
1341 ),
1342 }
1343
1344 match graph.connect(bi, None::<ExportIndex>, ai, 0) {
1345 Ok(_) => panic!("expected a failure to connect"),
1346 Err(e) => assert_eq!(
1347 format!("{e:#}"),
1348 "source instance is not compatible with target function import `i1`"
1349 ),
1350 }
1351
1352 match graph.connect(bi, None::<ExportIndex>, ai, 1) {
1353 Ok(_) => panic!("expected a failure to connect"),
1354 Err(e) => assert_eq!(
1355 format!("{e:#}"),
1356 "source instance is not compatible with target instance import `i2`"
1357 ),
1358 }
1359
1360 Ok(())
1361 }
1362
1363 #[test]
1364 fn it_cannot_encode_a_cycle() -> Result<()> {
1365 let mut graph = CompositionGraph::new();
1366 let mut validator = Validator::new();
1367 let a = graph.add_component(Component::from_bytes(
1368 &mut validator,
1369 "a",
1370 b"(component (import \"i1\" (func)) (export \"e1\" (func 0)))".as_ref(),
1371 )?)?;
1372 let b = graph.add_component(Component::from_bytes(
1373 &mut validator,
1374 "b",
1375 b"(component (import \"i1\" (func)) (export \"e1\" (func 0)))".as_ref(),
1376 )?)?;
1377 let ai = graph.instantiate(a)?;
1378 let bi = graph.instantiate(b)?;
1379
1380 graph.connect(ai, Some(0), bi, 0)?;
1381 graph.connect(bi, Some(0), ai, 0)?;
1382
1383 match graph.encode(EncodeOptions {
1384 define_components: false,
1385 export: None,
1386 validate: true,
1387 }) {
1388 Ok(_) => panic!("graph should not encode"),
1389 Err(e) => assert_eq!(
1390 format!("{e:#}"),
1391 "an instantiation of component `b` and its dependencies form a cycle in the instantiation graph"
1392 ),
1393 }
1394
1395 Ok(())
1396 }
1397
1398 #[test]
1399 fn it_encodes_an_empty_component() -> Result<()> {
1400 let mut graph = CompositionGraph::new();
1401 let mut validator = Validator::new();
1402 graph.add_component(Component::from_bytes(
1403 &mut validator,
1404 "a",
1405 b"(component)".as_ref(),
1406 )?)?;
1407 graph.add_component(Component::from_bytes(
1408 &mut validator,
1409 "b",
1410 b"(component)".as_ref(),
1411 )?)?;
1412
1413 let encoded = graph.encode(EncodeOptions {
1414 define_components: false,
1415 export: None,
1416 validate: true,
1417 })?;
1418
1419 let wat = wasmprinter::print_bytes(encoded)?;
1420 assert_eq!(
1421 r#"(component)
1422"#,
1423 wat
1424 );
1425
1426 Ok(())
1427 }
1428
1429 #[test]
1430 fn it_encodes_component_imports() -> Result<()> {
1431 let mut graph = CompositionGraph::new();
1432 let mut validator = Validator::new();
1433 graph.add_component(Component::from_bytes(
1435 &mut validator,
1436 "a",
1437 b"(component)".as_ref(),
1438 )?)?;
1439 let b = graph.add_component(Component::from_bytes(
1440 &mut validator,
1441 "b",
1442 b"(component)".as_ref(),
1443 )?)?;
1444 graph.instantiate(b)?;
1445
1446 let encoded = graph.encode(EncodeOptions {
1447 define_components: false,
1448 export: None,
1449 validate: true,
1450 })?;
1451
1452 let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1453 assert_eq!(
1454 r#"(component
1455 (type (;0;)
1456 (component)
1457 )
1458 (import "b" (component (;0;) (type 0)))
1459 (instance (;0;) (instantiate 0))
1460)
1461"#,
1462 wat
1463 );
1464
1465 Ok(())
1466 }
1467
1468 #[test]
1469 fn it_encodes_defined_components() -> Result<()> {
1470 let mut graph = CompositionGraph::new();
1471 let mut validator = Validator::new();
1472 graph.add_component(Component::from_bytes(
1474 &mut validator,
1475 "a",
1476 b"(component)".as_ref(),
1477 )?)?;
1478 let b = graph.add_component(Component::from_bytes(
1479 &mut validator,
1480 "b",
1481 b"(component)".as_ref(),
1482 )?)?;
1483 graph.instantiate(b)?;
1484
1485 let encoded = graph.encode(EncodeOptions {
1486 define_components: true,
1487 export: None,
1488 validate: true,
1489 })?;
1490
1491 let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1492 assert_eq!(
1493 r#"(component
1494 (component (;0;))
1495 (instance (;0;) (instantiate 0))
1496)
1497"#,
1498 wat
1499 );
1500
1501 Ok(())
1502 }
1503
1504 #[test]
1505 fn it_encodes_a_simple_composition() -> Result<()> {
1506 let mut graph = CompositionGraph::new();
1507 let mut validator = Validator::new();
1508 let a = graph.add_component(Component::from_bytes(
1509 &mut validator,
1510 "a",
1511 b"(component
1512 (type (tuple u32 u32))
1513 (import \"i1\" (instance (export \"e1\" (func)) (export \"e3\" (func (param \"a\" u32)))))
1514 (import \"i2\" (func))
1515 (import \"i3\" (component))
1516 (import \"i4\" (core module))
1517 (import \"i5\" (type (eq 0)))
1518 (export \"e1\" (instance 0))
1519 (export \"e2\" (func 0))
1520 (export \"e3\" (component 0))
1521 (export \"e4\" (core module 0))
1522 (export \"e5\" (type 1))
1523)"
1524 .as_ref(),
1525 )?)?;
1526 let b = graph.add_component(Component::from_bytes(
1527 &mut validator,
1528 "b",
1529 b"(component
1530 (type (tuple u32 u32))
1531 (import \"i1\" (instance (export \"e2\" (func)) (export \"e3\" (func (param \"a\" u32)))))
1532 (import \"i2\" (func))
1533 (import \"i3\" (component))
1534 (import \"i4\" (core module))
1535 (import \"i5\" (type (eq 0)))
1536)"
1537 .as_ref(),
1538 )?)?;
1539
1540 let ai = graph.instantiate(a)?;
1541 let bi1 = graph.instantiate(b)?;
1542 let bi2 = graph.instantiate(b)?;
1543 let bi3 = graph.instantiate(b)?;
1544
1545 for i in 1..=3 {
1547 graph.connect(ai, Some(i), bi1, i)?;
1548 graph.connect(ai, Some(i), bi2, i)?;
1549 graph.connect(ai, Some(i), bi3, i)?;
1550 }
1551
1552 let encoded = graph.encode(EncodeOptions {
1553 define_components: true,
1554 export: None,
1555 validate: true,
1556 })?;
1557
1558 let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1559 assert_eq!(
1560 r#"(component
1561 (type (;0;)
1562 (instance
1563 (type (;0;) (func))
1564 (export (;0;) "e1" (func (type 0)))
1565 (type (;1;) (func (param "a" u32)))
1566 (export (;1;) "e3" (func (type 1)))
1567 (type (;2;) (func))
1568 (export (;2;) "e2" (func (type 2)))
1569 )
1570 )
1571 (import "i1" (instance (;0;) (type 0)))
1572 (type (;1;) (func))
1573 (import "i2" (func (;0;) (type 1)))
1574 (type (;2;)
1575 (component)
1576 )
1577 (import "i3" (component (;0;) (type 2)))
1578 (core type (;0;)
1579 (module)
1580 )
1581 (import "i4" (core module (;0;) (type 0)))
1582 (type (;3;) (tuple u32 u32))
1583 (import "i5" (type (;4;) (eq 3)))
1584 (component (;1;)
1585 (type (;0;) (tuple u32 u32))
1586 (type (;1;)
1587 (instance
1588 (type (;0;) (func))
1589 (export (;0;) "e1" (func (type 0)))
1590 (type (;1;) (func (param "a" u32)))
1591 (export (;1;) "e3" (func (type 1)))
1592 )
1593 )
1594 (import "i1" (instance (;0;) (type 1)))
1595 (type (;2;) (func))
1596 (import "i2" (func (;0;) (type 2)))
1597 (type (;3;)
1598 (component)
1599 )
1600 (import "i3" (component (;0;) (type 3)))
1601 (core type (;0;)
1602 (module)
1603 )
1604 (import "i4" (core module (;0;) (type 0)))
1605 (import "i5" (type (;4;) (eq 0)))
1606 (export (;1;) "e1" (instance 0))
1607 (export (;1;) "e2" (func 0))
1608 (export (;1;) "e3" (component 0))
1609 (export (;1;) "e4" (core module 0))
1610 (export (;5;) "e5" (type 1))
1611 )
1612 (component (;2;)
1613 (type (;0;) (tuple u32 u32))
1614 (type (;1;)
1615 (instance
1616 (type (;0;) (func))
1617 (export (;0;) "e2" (func (type 0)))
1618 (type (;1;) (func (param "a" u32)))
1619 (export (;1;) "e3" (func (type 1)))
1620 )
1621 )
1622 (import "i1" (instance (;0;) (type 1)))
1623 (type (;2;) (func))
1624 (import "i2" (func (;0;) (type 2)))
1625 (type (;3;)
1626 (component)
1627 )
1628 (import "i3" (component (;0;) (type 3)))
1629 (core type (;0;)
1630 (module)
1631 )
1632 (import "i4" (core module (;0;) (type 0)))
1633 (import "i5" (type (;4;) (eq 0)))
1634 )
1635 (instance (;1;) (instantiate 1
1636 (with "i1" (instance 0))
1637 (with "i2" (func 0))
1638 (with "i3" (component 0))
1639 (with "i4" (core module 0))
1640 (with "i5" (type 4))
1641 )
1642 )
1643 (alias export 1 "e2" (func (;1;)))
1644 (alias export 1 "e3" (component (;3;)))
1645 (alias export 1 "e4" (core module (;1;)))
1646 (instance (;2;) (instantiate 2
1647 (with "i2" (func 1))
1648 (with "i3" (component 3))
1649 (with "i4" (core module 1))
1650 (with "i1" (instance 0))
1651 (with "i5" (type 4))
1652 )
1653 )
1654 (instance (;3;) (instantiate 2
1655 (with "i2" (func 1))
1656 (with "i3" (component 3))
1657 (with "i4" (core module 1))
1658 (with "i1" (instance 0))
1659 (with "i5" (type 4))
1660 )
1661 )
1662 (instance (;4;) (instantiate 2
1663 (with "i2" (func 1))
1664 (with "i3" (component 3))
1665 (with "i4" (core module 1))
1666 (with "i1" (instance 0))
1667 (with "i5" (type 4))
1668 )
1669 )
1670)
1671"#,
1672 wat
1673 );
1674
1675 Ok(())
1676 }
1677}