Skip to content
Open
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
35 changes: 34 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ version = "0.214.0"
rust-version = "1.76.0"

[workspace.dependencies]
anyhow = "1.0.58"
anyhow = { version = "1.0.58", features = ["backtrace"] }
arbitrary = "1.1.0"
clap = { version = "4.0.0", features = ["derive"] }
clap_complete = "4.4.7"
Expand Down
2 changes: 0 additions & 2 deletions crates/wasm-compose/src/composer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,6 @@ impl<'a> CompositionGraphBuilder<'a> {
}
}

self.graph.unify_imported_resources();

Ok((self.instances[root_instance], self.graph))
}
}
Expand Down
75 changes: 62 additions & 13 deletions crates/wasm-compose/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,17 @@ impl<'a> TypeEncoder<'a> {
return ret;
}

if let Some((instance, name)) = state.cur.instance_exports.get(&key) {
let ret = state.cur.encodable.type_count();
state.cur.encodable.alias(Alias::InstanceExport {
instance: *instance,
name,
kind: ComponentExportKind::Type,
});
log::trace!("id defined in current instance");
return ret;
}

match id.peel_alias(&self.0.types) {
Some(next) => id = next,
// If there's no more aliases then fall through to the
Expand All @@ -611,15 +622,17 @@ impl<'a> TypeEncoder<'a> {
return match id {
AnyTypeId::Core(ComponentCoreTypeId::Sub(_)) => unreachable!(),
AnyTypeId::Core(ComponentCoreTypeId::Module(id)) => self.module_type(state, id),
AnyTypeId::Component(id) => match id {
ComponentAnyTypeId::Resource(_) => {
unreachable!("should have been handled in `TypeEncoder::component_entity_type`")
AnyTypeId::Component(id) => {
match id {
ComponentAnyTypeId::Resource(r) => {
unreachable!("should have been handled in `TypeEncoder::component_entity_type`: {r:?}")
}
ComponentAnyTypeId::Defined(id) => self.defined_type(state, id),
ComponentAnyTypeId::Func(id) => self.component_func_type(state, id),
ComponentAnyTypeId::Instance(id) => self.component_instance_type(state, id),
ComponentAnyTypeId::Component(id) => self.component_type(state, id),
}
ComponentAnyTypeId::Defined(id) => self.defined_type(state, id),
ComponentAnyTypeId::Func(id) => self.component_func_type(state, id),
ComponentAnyTypeId::Instance(id) => self.component_instance_type(state, id),
ComponentAnyTypeId::Component(id) => self.component_type(state, id),
},
}
};
}

Expand Down Expand Up @@ -678,6 +691,9 @@ impl<'a> TypeEncoder<'a> {
state.cur.encodable.ty().defined_type().borrow(ty);
index
}
wasmparser::types::ComponentDefinedType::Future(ty) => self.future(state, *ty),
wasmparser::types::ComponentDefinedType::Stream(ty) => self.stream(state, *ty),
wasmparser::types::ComponentDefinedType::Error => self.error(state),
}
}

Expand Down Expand Up @@ -799,6 +815,32 @@ impl<'a> TypeEncoder<'a> {
}
export
}

fn future(
&self,
state: &mut TypeState<'a>,
ty: Option<wasmparser::types::ComponentValType>,
) -> u32 {
let ty = ty.map(|ty| self.component_val_type(state, ty));

let index = state.cur.encodable.type_count();
state.cur.encodable.ty().defined_type().future(ty);
index
}

fn stream(&self, state: &mut TypeState<'a>, ty: wasmparser::types::ComponentValType) -> u32 {
let ty = self.component_val_type(state, ty);

let index = state.cur.encodable.type_count();
state.cur.encodable.ty().defined_type().stream(ty);
index
}

fn error(&self, state: &mut TypeState<'a>) -> u32 {
let index = state.cur.encodable.type_count();
state.cur.encodable.ty().defined_type().error();
index
}
}

/// Represents an instance index in a composition graph.
Expand Down Expand Up @@ -1226,10 +1268,11 @@ impl DependencyRegistrar<'_, '_> {
match &self.types[ty] {
types::ComponentDefinedType::Primitive(_)
| types::ComponentDefinedType::Enum(_)
| types::ComponentDefinedType::Flags(_) => {}
types::ComponentDefinedType::List(t) | types::ComponentDefinedType::Option(t) => {
self.val_type(*t)
}
| types::ComponentDefinedType::Flags(_)
| types::ComponentDefinedType::Error => {}
types::ComponentDefinedType::List(t)
| types::ComponentDefinedType::Option(t)
| types::ComponentDefinedType::Stream(t) => self.val_type(*t),
types::ComponentDefinedType::Own(r) | types::ComponentDefinedType::Borrow(r) => {
self.ty(ComponentAnyTypeId::Resource(*r))
}
Expand Down Expand Up @@ -1258,6 +1301,11 @@ impl DependencyRegistrar<'_, '_> {
self.val_type(*err);
}
}
types::ComponentDefinedType::Future(ty) => {
if let Some(ty) = ty {
self.val_type(*ty);
}
}
}
}
}
Expand Down Expand Up @@ -1415,7 +1463,7 @@ impl<'a> CompositionGraphEncoder<'a> {
state.push(Encodable::Instance(InstanceType::new()));
for (name, types) in exports {
let (component, ty) = types[0];
log::trace!("export {name}");
log::trace!("export {name}: {ty:?}");
let export = TypeEncoder::new(component).export(name, ty, state);
let t = match &mut state.cur.encodable {
Encodable::Instance(c) => c,
Expand All @@ -1431,6 +1479,7 @@ impl<'a> CompositionGraphEncoder<'a> {
}
}
}

let instance_type = match state.pop() {
Encodable::Instance(c) => c,
_ => unreachable!(),
Expand Down
46 changes: 26 additions & 20 deletions crates/wasm-compose/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ pub(crate) struct Instance {
}

/// The options for encoding a composition graph.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct EncodeOptions {
/// Whether or not to define instantiated components.
///
Expand All @@ -440,7 +440,7 @@ pub struct EncodeOptions {

/// The instance in the graph to export.
///
/// If `Some`, the instance's exports will be aliased and
/// If non-empty, the instance's exports will be aliased and
/// exported from the resulting component.
pub export: Option<InstanceId>,

Expand Down Expand Up @@ -500,9 +500,6 @@ impl ResourceMapping {
if value.1 == export_resource {
self.map.insert(export_resource, value);
self.map.insert(import_resource, value);
} else {
// Can't set two different exports equal to each other -- give up.
return None;
}
} else {
// Couldn't find an export with a name that matches this
Expand Down Expand Up @@ -551,14 +548,19 @@ impl<'a> CompositionGraph<'a> {
/// connected to exports, group them by name, and update the resource
/// mapping to make all resources within each group equivalent.
///
/// This should be the last step prior to encoding, after all
/// inter-component connections have been made. It ensures that each set of
/// identical imports composed component can be merged into a single import
/// in the output component.
/// This ensures that each set of identical imports in the composed
/// components can be merged into a single import in the output component.
//
// TODO: How do we balance the need to call this early (so we can match up
// imports with exports which mutually import the same resources) with the
// need to delay decisions about where resources are coming from (so that we
// can match up imported resources with exported resources)? Right now I
// think we're erring on the side if the former at the expense of the
// latter.
pub(crate) fn unify_imported_resources(&self) {
let mut resource_mapping = self.resource_mapping.borrow_mut();

let mut resource_imports = HashMap::<_, Vec<_>>::new();
let mut resource_imports = IndexMap::<_, IndexSet<_>>::new();
for (component_id, component) in &self.components {
let component = &component.component;
for import_name in component.imports.keys() {
Expand All @@ -575,20 +577,22 @@ impl<'a> CompositionGraph<'a> {
..
} = ty
{
if !resource_mapping.map.contains_key(&resource_id.resource()) {
resource_imports
.entry(vec![import_name.to_string(), export_name.to_string()])
.or_default()
.push((*component_id, resource_id.resource()))
let set = resource_imports
.entry(vec![import_name.to_string(), export_name.to_string()])
.or_default();

if let Some(pair) = resource_mapping.map.get(&resource_id.resource()) {
set.insert(*pair);
}
set.insert((*component_id, resource_id.resource()));
}
}
}
}
}

for resources in resource_imports.values() {
match &resources[..] {
match &resources.iter().copied().collect::<Vec<_>>()[..] {
[] => unreachable!(),
[_] => {}
[first, rest @ ..] => {
Expand Down Expand Up @@ -644,10 +648,8 @@ impl<'a> CompositionGraph<'a> {
.remap_component_entity(&mut import_type, remapping);
remapping.reset_type_cache();

if context
.component_entity_type(&export_type, &import_type, 0)
.is_ok()
{
let v = context.component_entity_type(&export_type, &import_type, 0);
if v.is_ok() {
*self.resource_mapping.borrow_mut() = resource_mapping;
true
} else {
Expand Down Expand Up @@ -697,6 +699,10 @@ impl<'a> CompositionGraph<'a> {

assert!(self.components.insert(id, entry).is_none());

if self.components.len() > 1 {
self.unify_imported_resources();
}

Ok(id)
}

Expand Down
Loading