jrandolf edited PR #14872.
github-actions[bot] added the label wasmtime:api on PR #14872.
github-actions[bot] added the label wasmtime:c-api on PR #14872.
jrandolf updated PR #14872.
jrandolf updated PR #14872.
jrandolf updated PR #14872.
jrandolf edited PR #14872:
This fills the gaps from #14862 in how
implementsandexternal-idare exposed to embedders:
Component::get_export_externandInstance::get_export_externreturn an export's attributes along with its type and index, with C and C++ versions.- Type imports at the root of a component now show up in
component_type().imports(), like root type exports do.ComponentExternno longer borrows theEngine, so thenamed_importscode frombindgen!drops its workaround for that.ComponentExternis now#[non_exhaustive], so fields can be added later.Closes #14862
<details>
<summary>LLM summary of the changes</summary>1. Looking up an export together with its attributes
The problem:
Component::get_exportandInstance::get_exportreturn aComponentItem, which is only the type. Theimplementsandexternal-idof an export were only reachable by walkingcomponent_type().exports(). Wasmtime already stored them next to each export name, inNameMap<_, (ExportIndex, ComponentExternData)>, but nothing read them.The change:
Component::get_export_extern(instance, name)returnsOption<(types::ComponentExtern, ComponentExportIndex)>: the type plusimplementsandexternal_id, and the index to pass toget_funcand the other lookups.Instance::get_export_extern(store, instance, name)does the same for an instance. It builds the type from the instance's real resource types, so the result borrows from the store.- How it works: a new
lookup_export_externreturns the export index and the stored attribute data. The existingExportLookup for strnow shares the name-map lookup with it (lookup_export_by_name). A small constructor,ComponentExtern::from_parts, joins the type and the attributes.- It takes a
&str, notimpl ExportLookup. Attributes belong to the name an item is exported under, so a lookup that starts from aComponentExportIndexhas no name to read them from.- C API:
wasmtime_component_get_export_externandwasmtime_component_instance_get_export_externreturn the export index and write awasmtime_component_extern_tto an out-parameter. A name that isn't valid UTF-8 returns NULL.- C++:
Component::export_externandInstance::get_export_externreturnstd::optional<std::pair<ComponentExtern, ExportIndex>>.- Docs:
get_exportnow points to the new methods.2. Type imports at the root now show up in
component_type().imports()The problem: a root import like
(import "t" (external-id "x") (type (eq $u)))was missing from the component's reported type, while root type exports and nested component types were listed. Ininline.rs, type importscontinuebefore reachingimport_types, which is the list theLinkerhas to satisfy, andComponentTypesBuilder::finishbuilt the reported imports from that list.The change:
inline.rsnow records every root import, types included, with a newtypes.push_root_import(..)called before thatcontinue.finishbuilds the reported imports from that full list, in declaration order.import_typesitself is unchanged. Type imports still don't need to be defined in aLinker, anddefine_unknown_imports_as_*still walksimport_types, so it isn't affected.3.
ComponentExternno longer borrows theEngineThe problem:
ComponentExtern<'a>fromtypes::Componentandtypes::ComponentInstancewas tied to the&'a Engineborrow, even though its strings only borrow from the type. Soty.get_import(&engine.clone(), "p")didn't compile.The change:
get_importandget_exporttake a plain&Engine.imports()andexports()clone the engine (a cheapArcclone) into the iterator and declare+ use<'a>, so the iterator only borrows the type.- The C API getters drop the
'aon their engine parameter, and the C++ docs now say the result borrows from the type only.Knock-on change in
wit-bindgen/src/lib.rs: theadd_to_linkerthatbindgen!generates fornamed_importsworked around the old borrow. It cloned the engine and collected matching imports first, then defined them in a second loop. It now does both in one loop: iteratecomponent_ty.imports(linker.engine()), skip imports whereis_implementsis false, then calllookup(name)?andlinker.instance(name)?.Side effect: if
lookupfails partway through, the imports that matched before it are already defined in the linker. Before, nothing was defined unless every lookup succeeded.4.
ComponentExternis#[non_exhaustive]This leaves room to add fields later, such as a version suffix for canonical names. It breaks code that builds the struct with a literal or destructures it exhaustively. Nothing in the repo does, and only Wasmtime produces it.
Tests
tests/all/component_model/aot.rs:external_id_shows_upadds a root type import and checks that it shows up as a record type with itsexternal-id. A newexport_extern_lookupchecksget_export_externon both the component and the instance, at the top level and nested.crates/c-api/tests/component/lookup_func.cc: a newlookup_export_externtest covers the C++ wrappers in the same way.</details>
Last updated: Oct 31 2026 at 23:03 UTC