Skip to content

Commit 1ad1447

Browse files
committed
Fix the encoding of imported resources in the CompositionGraph.
This commit fixes the encoding of imported resources in the `CompositionGraph`. Previously a panic was encountered when the graph attempts to import, either explicitly or implicitly, a resource. The fix requires tracking the "owner" of an aliased resource so that the export on the owning interface may be aliased and used for type equality when encoding the resource import. Fixes #62.
1 parent 66c0672 commit 1ad1447

8 files changed

Lines changed: 258 additions & 71 deletions

File tree

crates/wac-graph/src/encoding.rs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use wasm_encoder::{
1414

1515
/// A type used to abstract the API differences between a component builder,
1616
/// component type, and instance type from `wasm-encoder`.
17+
#[derive(Debug)]
1718
enum Encodable {
1819
Builder(ComponentBuilder),
1920
Instance(InstanceType),
@@ -94,7 +95,7 @@ impl Default for Encodable {
9495
}
9596
}
9697

97-
#[derive(Default)]
98+
#[derive(Debug, Default)]
9899
pub struct Scope {
99100
/// The map from types to encoded type index.
100101
pub type_indexes: IndexMap<Type, u32>,
@@ -108,7 +109,7 @@ pub struct Scope {
108109
encodable: Encodable,
109110
}
110111

111-
#[derive(Default)]
112+
#[derive(Debug, Default)]
112113
pub struct State {
113114
/// The stack of encoding scopes.
114115
scopes: Vec<Scope>,
@@ -630,7 +631,8 @@ impl<'a> TypeEncoder<'a> {
630631

631632
fn import(&self, state: &mut State, name: &str, kind: ItemKind) {
632633
if let ItemKind::Type(Type::Resource(id)) = kind {
633-
return self.import_resource(state, name, id);
634+
self.import_resource(state, name, id);
635+
return;
634636
}
635637

636638
log::debug!("encoding {kind} import `{name}`", kind = kind.desc(self.0));
@@ -669,9 +671,9 @@ impl<'a> TypeEncoder<'a> {
669671
}
670672
}
671673

672-
fn import_resource(&self, state: &mut State, name: &str, id: ResourceId) {
673-
if state.current.resources.contains_key(name) {
674-
return;
674+
pub fn import_resource(&self, state: &mut State, name: &str, id: ResourceId) -> u32 {
675+
if let Some(index) = state.current.resources.get(name) {
676+
return *index;
675677
}
676678

677679
log::debug!("encoding import of resource `{name}`");
@@ -687,17 +689,43 @@ impl<'a> TypeEncoder<'a> {
687689

688690
log::debug!("encoded outer alias for resource `{name}` to type index {index}");
689691
index
690-
} else if let Some(alias_of) = resource.alias_of {
691-
// This is an alias to another resource at the same scope
692-
let orig =
693-
state.current.resources[self.0[self.0.resolve_resource(alias_of)].name.as_str()];
692+
} else if let Some(alias) = resource.alias {
693+
let source = self.0.resolve_resource(alias.source);
694+
let source_index = if let Some(index) =
695+
state.current.resources.get(self.0[source].name.as_str())
696+
{
697+
// The source resource was previously imported
698+
*index
699+
} else if let Some(index) = state.current.type_indexes.get(&Type::Resource(source)) {
700+
// The source resource isn't directly imported, but was previously aliased
701+
*index
702+
} else {
703+
// Otherwise, we need to alias the source resource
704+
// This should only occur for resources owned by interfaces
705+
let source_index = state.current.encodable.type_count();
706+
let iid = self.0[alias.owner.expect("should have owner")]
707+
.id
708+
.as_deref()
709+
.expect("expected an interface with an id");
710+
state.current.encodable.alias(Alias::InstanceExport {
711+
instance: state.current.instances[iid],
712+
kind: ComponentExportKind::Type,
713+
name: self.0[source].name.as_str(),
714+
});
715+
state
716+
.current
717+
.type_indexes
718+
.insert(Type::Resource(source), source_index);
719+
source_index
720+
};
721+
694722
let index = state.current.encodable.type_count();
695723
state
696724
.current
697725
.encodable
698-
.import_type(name, ComponentTypeRef::Type(TypeBounds::Eq(orig)));
726+
.import_type(name, ComponentTypeRef::Type(TypeBounds::Eq(source_index)));
699727

700-
log::debug!("encoded import for resource `{name}` as type index {index} (alias of type index {orig})");
728+
log::debug!("encoded import for resource `{name}` as type index {index} (alias of type index {source_index})");
701729
index
702730
} else {
703731
// Otherwise, this is a new resource type, import with a subtype bounds
@@ -712,6 +740,7 @@ impl<'a> TypeEncoder<'a> {
712740
};
713741

714742
state.current.resources.insert(resource.name.clone(), index);
743+
index
715744
}
716745

717746
fn export(&self, state: &mut State, name: &str, kind: ItemKind) -> u32 {
@@ -759,10 +788,10 @@ impl<'a> TypeEncoder<'a> {
759788
Self::export_type(state, name, ComponentTypeRef::Type(TypeBounds::Eq(outer)));
760789
log::debug!("encoded outer alias for resource `{name}` as type index {index}");
761790
index
762-
} else if let Some(alias_of) = resource.alias_of {
791+
} else if let Some(alias) = resource.alias {
763792
// This is an alias to another resource at the same scope
764-
let index =
765-
state.current.resources[self.0[self.0.resolve_resource(alias_of)].name.as_str()];
793+
let index = state.current.resources
794+
[self.0[self.0.resolve_resource(alias.source)].name.as_str()];
766795
let index =
767796
Self::export_type(state, name, ComponentTypeRef::Type(TypeBounds::Eq(index)));
768797
log::debug!("encoded alias for resource `{name}` as type index {index}");

crates/wac-graph/src/graph.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,13 +1458,19 @@ impl<'a> CompositionGraphEncoder<'a> {
14581458
}
14591459
}
14601460

1461+
let encoder = TypeEncoder::new(types);
1462+
1463+
// Defer to special handling if the item being imported is a resource
1464+
if let ItemKind::Type(Type::Resource(id)) = kind {
1465+
return encoder.import_resource(state, name, id);
1466+
}
1467+
14611468
log::debug!(
14621469
"encoding import of {kind} `{name}`",
14631470
kind = kind.desc(types)
14641471
);
14651472

14661473
// Encode the type and import
1467-
let encoder = TypeEncoder::new(types);
14681474
let ty = encoder.ty(state, kind.ty(), None);
14691475
let index = state.builder().import(
14701476
name,
@@ -1712,7 +1718,7 @@ mod test {
17121718
let mut graph = CompositionGraph::new();
17131719
let id = graph.types_mut().add_resource(Resource {
17141720
name: "a".to_string(),
1715-
alias_of: None,
1721+
alias: None,
17161722
});
17171723
assert!(matches!(
17181724
graph.define_type("foo", Type::Resource(id)).unwrap_err(),
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
(component
2+
(type (;0;)
3+
(instance
4+
(export (;0;) "my-resource" (type (sub resource)))
5+
)
6+
)
7+
(import "test:foo/my-interface" (instance (;0;) (type 0)))
8+
(alias export 0 "my-resource" (type (;1;)))
9+
(import "my-resource" (type (;2;) (eq 1)))
10+
(import "my-resource2" (type (;3;) (eq 2)))
11+
(type (;4;)
12+
(component
13+
(type (;0;)
14+
(instance
15+
(export (;0;) "my-resource" (type (sub resource)))
16+
)
17+
)
18+
(import "test:foo/my-interface" (instance (;0;) (type 0)))
19+
(alias export 0 "my-resource" (type (;1;)))
20+
(import "my-resource" (type (;2;) (eq 1)))
21+
(import "my-resource2" (type (;3;) (eq 2)))
22+
(type (;4;) (own 3))
23+
(type (;5;) (func (param "r" 4)))
24+
(export (;0;) "my-func" (func (type 5)))
25+
)
26+
)
27+
(import "unlocked-dep=<test:foo>" (component (;0;) (type 4)))
28+
(instance (;1;) (instantiate 0
29+
(with "test:foo/my-interface" (instance 0))
30+
(with "my-resource" (type 2))
31+
(with "my-resource2" (type 3))
32+
)
33+
)
34+
(alias export 1 "my-func" (func (;0;)))
35+
(export (;1;) "my-func" (func 0))
36+
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package test:foo;
2+
3+
world my-world {
4+
use my-interface.{my-resource};
5+
type my-resource2 = my-resource;
6+
7+
export my-func: func(r: my-resource2);
8+
}
9+
10+
interface my-interface {
11+
resource my-resource {}
12+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"packages": [
3+
{
4+
"name": "test:foo",
5+
"path": "foo.wit"
6+
}
7+
],
8+
"nodes": [
9+
{
10+
"type": "instantiation",
11+
"package": 0
12+
},
13+
{
14+
"type": "alias",
15+
"source": 0,
16+
"export": "my-func"
17+
}
18+
],
19+
"exports": [
20+
{
21+
"node": 1,
22+
"name": "my-func"
23+
}
24+
]
25+
}

0 commit comments

Comments
 (0)