Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions crates/wac-graph/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use wasm_encoder::{

/// A type used to abstract the API differences between a component builder,
/// component type, and instance type from `wasm-encoder`.
#[derive(Debug)]
enum Encodable {
Builder(ComponentBuilder),
Instance(InstanceType),
Expand Down Expand Up @@ -94,7 +95,7 @@ impl Default for Encodable {
}
}

#[derive(Default)]
#[derive(Debug, Default)]
pub struct Scope {
/// The map from types to encoded type index.
pub type_indexes: IndexMap<Type, u32>,
Expand All @@ -108,7 +109,7 @@ pub struct Scope {
encodable: Encodable,
}

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

fn import(&self, state: &mut State, name: &str, kind: ItemKind) {
if let ItemKind::Type(Type::Resource(id)) = kind {
return self.import_resource(state, name, id);
self.import_resource(state, name, id);
return;
}

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

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

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

log::debug!("encoded outer alias for resource `{name}` to type index {index}");
index
} else if let Some(alias_of) = resource.alias_of {
// This is an alias to another resource at the same scope
let orig =
state.current.resources[self.0[self.0.resolve_resource(alias_of)].name.as_str()];
} else if let Some(alias) = resource.alias {
let source = self.0.resolve_resource(alias.source);
let source_index = if let Some(index) =
state.current.resources.get(self.0[source].name.as_str())
{
// The source resource was previously imported
*index
} else if let Some(index) = state.current.type_indexes.get(&Type::Resource(source)) {
// The source resource isn't directly imported, but was previously aliased
*index
} else {
// Otherwise, we need to alias the source resource
// This should only occur for resources owned by interfaces
let source_index = state.current.encodable.type_count();
let iid = self.0[alias.owner.expect("should have owner")]
.id
.as_deref()
.expect("expected an interface with an id");
state.current.encodable.alias(Alias::InstanceExport {
instance: state.current.instances[iid],
kind: ComponentExportKind::Type,
name: self.0[source].name.as_str(),
});
state
.current
.type_indexes
.insert(Type::Resource(source), source_index);
source_index
};

let index = state.current.encodable.type_count();
state
.current
.encodable
.import_type(name, ComponentTypeRef::Type(TypeBounds::Eq(orig)));
.import_type(name, ComponentTypeRef::Type(TypeBounds::Eq(source_index)));

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

state.current.resources.insert(resource.name.clone(), index);
index
}

fn export(&self, state: &mut State, name: &str, kind: ItemKind) -> u32 {
Expand Down Expand Up @@ -759,10 +788,10 @@ impl<'a> TypeEncoder<'a> {
Self::export_type(state, name, ComponentTypeRef::Type(TypeBounds::Eq(outer)));
log::debug!("encoded outer alias for resource `{name}` as type index {index}");
index
} else if let Some(alias_of) = resource.alias_of {
} else if let Some(alias) = resource.alias {
// This is an alias to another resource at the same scope
let index =
state.current.resources[self.0[self.0.resolve_resource(alias_of)].name.as_str()];
let index = state.current.resources
[self.0[self.0.resolve_resource(alias.source)].name.as_str()];
let index =
Self::export_type(state, name, ComponentTypeRef::Type(TypeBounds::Eq(index)));
log::debug!("encoded alias for resource `{name}` as type index {index}");
Expand Down
10 changes: 8 additions & 2 deletions crates/wac-graph/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1458,13 +1458,19 @@ impl<'a> CompositionGraphEncoder<'a> {
}
}

let encoder = TypeEncoder::new(types);

// Defer to special handling if the item being imported is a resource
if let ItemKind::Type(Type::Resource(id)) = kind {
return encoder.import_resource(state, name, id);
}

log::debug!(
"encoding import of {kind} `{name}`",
kind = kind.desc(types)
);

// Encode the type and import
let encoder = TypeEncoder::new(types);
let ty = encoder.ty(state, kind.ty(), None);
let index = state.builder().import(
name,
Expand Down Expand Up @@ -1712,7 +1718,7 @@ mod test {
let mut graph = CompositionGraph::new();
let id = graph.types_mut().add_resource(Resource {
name: "a".to_string(),
alias_of: None,
alias: None,
});
assert!(matches!(
graph.define_type("foo", Type::Resource(id)).unwrap_err(),
Expand Down
36 changes: 36 additions & 0 deletions crates/wac-graph/tests/graphs/used-resource/encoded.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
(component
(type (;0;)
(instance
(export (;0;) "my-resource" (type (sub resource)))
)
)
(import "test:foo/my-interface" (instance (;0;) (type 0)))
(alias export 0 "my-resource" (type (;1;)))
(import "my-resource" (type (;2;) (eq 1)))
(import "my-resource2" (type (;3;) (eq 2)))
(type (;4;)
(component
(type (;0;)
(instance
(export (;0;) "my-resource" (type (sub resource)))
)
)
(import "test:foo/my-interface" (instance (;0;) (type 0)))
(alias export 0 "my-resource" (type (;1;)))
(import "my-resource" (type (;2;) (eq 1)))
(import "my-resource2" (type (;3;) (eq 2)))
(type (;4;) (own 3))
(type (;5;) (func (param "r" 4)))
(export (;0;) "my-func" (func (type 5)))
)
)
(import "unlocked-dep=<test:foo>" (component (;0;) (type 4)))
(instance (;1;) (instantiate 0
(with "test:foo/my-interface" (instance 0))
(with "my-resource" (type 2))
(with "my-resource2" (type 3))
)
)
(alias export 1 "my-func" (func (;0;)))
(export (;1;) "my-func" (func 0))
)
12 changes: 12 additions & 0 deletions crates/wac-graph/tests/graphs/used-resource/foo.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package test:foo;

world my-world {
use my-interface.{my-resource};
type my-resource2 = my-resource;

export my-func: func(r: my-resource2);
}

interface my-interface {
resource my-resource {}
}
25 changes: 25 additions & 0 deletions crates/wac-graph/tests/graphs/used-resource/graph.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"packages": [
{
"name": "test:foo",
"path": "foo.wit"
}
],
"nodes": [
{
"type": "instantiation",
"package": 0
},
{
"type": "alias",
"source": 0,
"export": "my-func"
}
],
"exports": [
{
"node": 1,
"name": "my-func"
}
]
}
Loading