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
1 change: 1 addition & 0 deletions crates/environ/src/component/translate/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1971,6 +1971,7 @@ impl ComponentExternData {
fn new(data: wasmparser::ComponentExternName<'_>) -> Self {
ComponentExternData {
implements: data.implements.map(|s| s.to_string()),
external_id: data.external_id.map(|s| s.to_string()),
}
}
}
19 changes: 14 additions & 5 deletions crates/environ/src/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,20 +449,29 @@ where
}
}

/// TODO
/// An import or an export of a component or a component instance.
///
/// This records the type of the item that is being imported or exported along
/// with any metadata associated with the item.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ComponentExtern {
/// TODO
/// Metadata associated with this item's name, such as
/// `(implements "...")`.
pub data: ComponentExternData,
/// TODO
/// The type of this item.
pub ty: TypeDef,
}

/// TODO
/// Metadata associated with the name of a component import or export.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ComponentExternData {
/// TODO
/// The `(implements "...")` annotation, if present: the name of the
/// interface that this item implements, used when matching this item
/// against imports by interface name rather than by import name.
pub implements: Option<String>,
/// The `(external-id "...")` annotation, if present: a free-form
/// host-defined identifier which is ignored by type checking.
pub external_id: Option<String>,
}

/// Types of imports and exports in the component model.
Expand Down
1 change: 1 addition & 0 deletions crates/environ/src/component/types_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ impl ComponentTypesBuilder {
ty: self.convert_component_entity_type(types, ty.ty)?,
data: ComponentExternData {
implements: ty.implements.clone(),
external_id: ty.external_id.clone(),
},
})
}
Expand Down
6 changes: 6 additions & 0 deletions crates/wasmtime/src/runtime/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,11 @@ pub struct ComponentExtern<'a> {
pub ty: ComponentItem,
/// The `(implements "...")` annotation, if present.
pub implements: Option<&'a str>,
/// The `(external-id "...")` annotation, if present.
///
/// This is a free-form host-defined identifier and is intended to indicate
/// how the host should satisfy an import (or interpret an export).
pub external_id: Option<&'a str>,
}

impl<'a> ComponentExtern<'a> {
Expand All @@ -1181,6 +1186,7 @@ impl<'a> ComponentExtern<'a> {
) -> Self {
Self {
implements: env.data.implements.as_deref(),
external_id: env.data.external_id.as_deref(),
ty: ComponentItem::from(engine, &env.ty, instance_ty),
}
}
Expand Down
50 changes: 50 additions & 0 deletions tests/all/component_model/aot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,56 @@ fn implements_shows_up() -> Result<()> {
Ok(())
}

#[test]
#[cfg_attr(miri, ignore)]
fn external_id_shows_up() -> Result<()> {
let mut config = Config::new();
config.wasm_component_model_implements(true);
let engine = Engine::new(&config)?;
let component = Component::new(
&engine,
r#"
(component
(import "a" (implements "a1:b1/c1") (external-id "user-db-prod:region-a") (instance $a))
(import "b" (external-id "user-db-prod:region-b") (instance $b
(export "f" (external-id "func-b-f") (func))
))
(import "c" (instance $c))
(export "d" (external-id "queue-1") (instance $a))
)
"#,
)?;

let assert_reflection = |component: &Component| {
let ty = component.component_type();
let mut imports = ty.imports(&engine);
let (_, a) = imports.next().unwrap();
assert_eq!(a.implements.as_deref(), Some("a1:b1/c1"));
assert_eq!(a.external_id.as_deref(), Some("user-db-prod:region-a"));
let (_, b) = imports.next().unwrap();
assert_eq!(b.implements.as_deref(), None);
assert_eq!(b.external_id.as_deref(), Some("user-db-prod:region-b"));
let ComponentItem::ComponentInstance(instance) = &b.ty else {
panic!("expected an instance import");
};
let (name, f) = instance.exports(&engine).next().unwrap();
assert_eq!(name, "f");
assert_eq!(f.external_id.as_deref(), Some("func-b-f"));
let (_, c) = imports.next().unwrap();
assert_eq!(c.external_id.as_deref(), None);
let mut exports = ty.exports(&engine);
let (_, d) = exports.next().unwrap();
assert_eq!(d.external_id.as_deref(), Some("queue-1"));
};

assert_reflection(&component);
let bytes = component.serialize()?;
let component = unsafe { Component::deserialize(&engine, &bytes)? };
assert_reflection(&component);

Ok(())
}

#[test]
#[cfg_attr(miri, ignore)]
fn issue_13540_resources_in_adapter_no_concurrency() -> Result<()> {
Expand Down
Loading