Path: blob/main/crates/environ/src/component/translate/inline.rs
1692 views
//! Implementation of "inlining" a component into a flat list of initializers.1//!2//! After the first phase of compiling a component we're left with a single3//! root `Translation` for the original component along with a "static" list of4//! child components. Each `Translation` has a list of `LocalInitializer` items5//! inside of it which is a primitive representation of how the component6//! should be constructed with effectively one initializer per item in the7//! index space of a component. This "local initializer" list would be8//! relatively inefficient to process at runtime and more importantly doesn't9//! convey enough information to understand what trampolines need to be10//! compiled or what fused adapters need to be generated. This consequently is11//! the motivation for this file.12//!13//! The second phase of compilation, inlining here, will in a sense interpret14//! the initializers, at compile time, into a new list of `GlobalInitializer` entries15//! which are a sort of "global initializer". The generated `GlobalInitializer` is16//! much more specific than the `LocalInitializer` and additionally far fewer17//! `GlobalInitializer` structures are generated (in theory) than there are local18//! initializers.19//!20//! The "inlining" portion of the name of this module indicates how the21//! instantiation of a component is interpreted as calling a function. The22//! function's arguments are the imports provided to the instantiation of a23//! component, and further nested function calls happen on a stack when a24//! nested component is instantiated. The inlining then refers to how this25//! stack of instantiations is flattened to one list of `GlobalInitializer`26//! entries to represent the process of instantiating a component graph,27//! similar to how function inlining removes call instructions and creates one28//! giant function for a call graph. Here there are no inlining heuristics or29//! anything like that, we simply inline everything into the root component's30//! list of initializers.31//!32//! Another primary task this module performs is a form of dataflow analysis33//! to represent items in each index space with their definition rather than34//! references of relative indices. These definitions (all the `*Def` types in35//! this module) are not local to any one nested component and instead36//! represent state available at runtime tracked in the final `Component`37//! produced.38//!39//! With all this pieced together the general idea is relatively40//! straightforward. All of a component's initializers are processed in sequence41//! where instantiating a nested component pushes a "frame" onto a stack to42//! start executing and we resume at the old one when we're done. Items are43//! tracked where they come from and at the end after processing only the44//! side-effectful initializers are emitted to the `GlobalInitializer` list in the45//! final `Component`.4647use crate::component::translate::*;48use crate::{EntityType, IndexType};49use std::borrow::Cow;50use wasmparser::component_types::{ComponentAnyTypeId, ComponentCoreModuleTypeId};5152pub(super) fn run(53types: &mut ComponentTypesBuilder,54result: &Translation<'_>,55nested_modules: &PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,56nested_components: &PrimaryMap<StaticComponentIndex, Translation<'_>>,57) -> Result<dfg::ComponentDfg> {58let mut inliner = Inliner {59nested_modules,60nested_components,61result: Default::default(),62import_path_interner: Default::default(),63runtime_instances: PrimaryMap::default(),64};6566let index = RuntimeComponentInstanceIndex::from_u32(0);6768// The initial arguments to the root component are all host imports. This69// means that they're all using the `ComponentItemDef::Host` variant. Here70// an `ImportIndex` is allocated for each item and then the argument is71// recorded.72//73// Note that this is represents the abstract state of a host import of an74// item since we don't know the precise structure of the host import.75let mut args = HashMap::with_capacity(result.exports.len());76let mut path = Vec::new();77types.resources_mut().set_current_instance(index);78let types_ref = result.types_ref();79for init in result.initializers.iter() {80let (name, ty) = match *init {81LocalInitializer::Import(name, ty) => (name, ty),82_ => continue,83};8485// Before `convert_component_entity_type` below all resource types86// introduced by this import need to be registered and have indexes87// assigned to them. Any fresh new resource type referred to by imports88// is a brand new introduction of a resource which needs to have a type89// allocated to it, so new runtime imports are injected for each90// resource along with updating the `imported_resources` map.91let index = inliner.result.import_types.next_key();92types.resources_mut().register_component_entity_type(93&types_ref,94ty,95&mut path,96&mut |path| {97let index = inliner.runtime_import(&ImportPath {98index,99path: path.iter().copied().map(Into::into).collect(),100});101inliner.result.imported_resources.push(index)102},103);104105// With resources all taken care of it's now possible to convert this106// into Wasmtime's type system.107let ty = types.convert_component_entity_type(types_ref, ty)?;108109// Imports of types that aren't resources are not required to be110// specified by the host since it's just for type information within111// the component.112if let TypeDef::Interface(_) = ty {113continue;114}115let index = inliner.result.import_types.push((name.0.to_string(), ty));116let path = ImportPath::root(index);117args.insert(name.0, ComponentItemDef::from_import(path, ty)?);118}119120// This will run the inliner to completion after being seeded with the121// initial frame. When the inliner finishes it will return the exports of122// the root frame which are then used for recording the exports of the123// component.124inliner.result.num_runtime_component_instances += 1;125let frame = InlinerFrame::new(index, result, ComponentClosure::default(), args, None);126let resources_snapshot = types.resources_mut().clone();127let mut frames = vec![(frame, resources_snapshot)];128let exports = inliner.run(types, &mut frames)?;129assert!(frames.is_empty());130131let mut export_map = Default::default();132for (name, def) in exports {133inliner.record_export(name, def, types, &mut export_map)?;134}135inliner.result.exports = export_map;136inliner.result.num_future_tables = types.num_future_tables();137inliner.result.num_stream_tables = types.num_stream_tables();138inliner.result.num_error_context_tables = types.num_error_context_tables();139140Ok(inliner.result)141}142143struct Inliner<'a> {144/// The list of static modules that were found during initial translation of145/// the component.146///147/// This is used during the instantiation of these modules to ahead-of-time148/// order the arguments precisely according to what the module is defined as149/// needing which avoids the need to do string lookups or permute arguments150/// at runtime.151nested_modules: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,152153/// The list of static components that were found during initial translation of154/// the component.155///156/// This is used when instantiating nested components to push a new157/// `InlinerFrame` with the `Translation`s here.158nested_components: &'a PrimaryMap<StaticComponentIndex, Translation<'a>>,159160/// The final `Component` that is being constructed and returned from this161/// inliner.162result: dfg::ComponentDfg,163164// Maps used to "intern" various runtime items to only save them once at165// runtime instead of multiple times.166import_path_interner: HashMap<ImportPath<'a>, RuntimeImportIndex>,167168/// Origin information about where each runtime instance came from169runtime_instances: PrimaryMap<dfg::InstanceId, InstanceModule>,170}171172/// A "stack frame" as part of the inlining process, or the progress through173/// instantiating a component.174///175/// All instantiations of a component will create an `InlinerFrame` and are176/// incrementally processed via the `initializers` list here. Note that the177/// inliner frames are stored on the heap to avoid recursion based on user178/// input.179struct InlinerFrame<'a> {180instance: RuntimeComponentInstanceIndex,181182/// The remaining initializers to process when instantiating this component.183initializers: std::slice::Iter<'a, LocalInitializer<'a>>,184185/// The component being instantiated.186translation: &'a Translation<'a>,187188/// The "closure arguments" to this component, or otherwise the maps indexed189/// by `ModuleUpvarIndex` and `ComponentUpvarIndex`. This is created when190/// a component is created and stored as part of a component's state during191/// inlining.192closure: ComponentClosure<'a>,193194/// The arguments to the creation of this component.195///196/// At the root level these are all imports from the host and between197/// components this otherwise tracks how all the arguments are defined.198args: HashMap<&'a str, ComponentItemDef<'a>>,199200// core wasm index spaces201funcs: PrimaryMap<FuncIndex, (ModuleInternedTypeIndex, dfg::CoreDef)>,202memories: PrimaryMap<MemoryIndex, dfg::CoreExport<EntityIndex>>,203tables: PrimaryMap<TableIndex, dfg::CoreExport<EntityIndex>>,204globals: PrimaryMap<GlobalIndex, dfg::CoreExport<EntityIndex>>,205tags: PrimaryMap<GlobalIndex, dfg::CoreExport<EntityIndex>>,206modules: PrimaryMap<ModuleIndex, ModuleDef<'a>>,207208// component model index spaces209component_funcs: PrimaryMap<ComponentFuncIndex, ComponentFuncDef<'a>>,210module_instances: PrimaryMap<ModuleInstanceIndex, ModuleInstanceDef<'a>>,211component_instances: PrimaryMap<ComponentInstanceIndex, ComponentInstanceDef<'a>>,212components: PrimaryMap<ComponentIndex, ComponentDef<'a>>,213214/// The type of instance produced by completing the instantiation of this215/// frame.216///217/// This is a wasmparser-relative piece of type information which is used to218/// register resource types after instantiation has completed.219///220/// This is `Some` for all subcomponents and `None` for the root component.221instance_ty: Option<ComponentInstanceTypeId>,222}223224/// "Closure state" for a component which is resolved from the `ClosedOverVars`225/// state that was calculated during translation.226//227// FIXME: this is cloned quite a lot and given the internal maps if this is a228// perf issue we may want to `Rc` these fields. Note that this is only a perf229// hit at compile-time though which we in general don't pay too much230// attention to.231#[derive(Default, Clone)]232struct ComponentClosure<'a> {233modules: PrimaryMap<ModuleUpvarIndex, ModuleDef<'a>>,234components: PrimaryMap<ComponentUpvarIndex, ComponentDef<'a>>,235}236237/// Representation of a "path" into an import.238///239/// Imports from the host at this time are one of three things:240///241/// * Functions242/// * Core wasm modules243/// * "Instances" of these three items244///245/// The "base" values are functions and core wasm modules, but the abstraction246/// of an instance allows embedding functions/modules deeply within other247/// instances. This "path" represents optionally walking through a host instance248/// to get to the final desired item. At runtime instances are just maps of249/// values and so this is used to ensure that we primarily only deal with250/// individual functions and modules instead of synthetic instances.251#[derive(Clone, PartialEq, Hash, Eq)]252struct ImportPath<'a> {253index: ImportIndex,254path: Vec<Cow<'a, str>>,255}256257/// Representation of all items which can be defined within a component.258///259/// This is the "value" of an item defined within a component and is used to260/// represent both imports and exports.261#[derive(Clone)]262enum ComponentItemDef<'a> {263Component(ComponentDef<'a>),264Instance(ComponentInstanceDef<'a>),265Func(ComponentFuncDef<'a>),266Module(ModuleDef<'a>),267Type(TypeDef),268}269270#[derive(Clone)]271enum ModuleDef<'a> {272/// A core wasm module statically defined within the original component.273///274/// The `StaticModuleIndex` indexes into the `static_modules` map in the275/// `Inliner`.276Static(StaticModuleIndex, ComponentCoreModuleTypeId),277278/// A core wasm module that was imported from the host.279Import(ImportPath<'a>, TypeModuleIndex),280}281282// Note that unlike all other `*Def` types which are not allowed to have local283// indices this type does indeed have local indices. That is represented with284// the lack of a `Clone` here where once this is created it's never moved across285// components because module instances always stick within one component.286enum ModuleInstanceDef<'a> {287/// A core wasm module instance was created through the instantiation of a288/// module.289///290/// The `RuntimeInstanceIndex` was the index allocated as this was the291/// `n`th instantiation and the `ModuleIndex` points into an292/// `InlinerFrame`'s local index space.293Instantiated(dfg::InstanceId, ModuleIndex),294295/// A "synthetic" core wasm module which is just a bag of named indices.296///297/// Note that this can really only be used for passing as an argument to298/// another module's instantiation and is used to rename arguments locally.299Synthetic(&'a HashMap<&'a str, EntityIndex>),300}301302#[derive(Clone)]303enum ComponentFuncDef<'a> {304/// A host-imported component function.305Import(ImportPath<'a>),306307/// A core wasm function was lifted into a component function.308Lifted {309/// The component function type.310ty: TypeFuncIndex,311/// The core Wasm function.312func: dfg::CoreDef,313/// Canonical options.314options: AdapterOptions,315},316}317318#[derive(Clone)]319enum ComponentInstanceDef<'a> {320/// A host-imported instance.321///322/// This typically means that it's "just" a map of named values. It's not323/// actually supported to take a `wasmtime::component::Instance` and pass it324/// to another instance at this time.325Import(ImportPath<'a>, TypeComponentInstanceIndex),326327/// A concrete map of values.328///329/// This is used for both instantiated components as well as "synthetic"330/// components. This variant can be used for both because both are331/// represented by simply a bag of items within the entire component332/// instantiation process.333//334// FIXME: same as the issue on `ComponentClosure` where this is cloned a lot335// and may need `Rc`.336Items(337IndexMap<&'a str, ComponentItemDef<'a>>,338TypeComponentInstanceIndex,339),340}341342#[derive(Clone)]343struct ComponentDef<'a> {344index: StaticComponentIndex,345closure: ComponentClosure<'a>,346}347348impl<'a> Inliner<'a> {349/// Symbolically instantiates a component using the type information and350/// `frames` provided.351///352/// The `types` provided is the type information for the entire component353/// translation process. This is a distinct output artifact separate from354/// the component metadata.355///356/// The `frames` argument is storage to handle a "call stack" of components357/// instantiating one another. The youngest frame (last element) of the358/// frames list is a component that's currently having its initializers359/// processed. The second element of each frame is a snapshot of the360/// resource-related information just before the frame was translated. For361/// more information on this snapshotting see the documentation on362/// `ResourcesBuilder`.363fn run(364&mut self,365types: &mut ComponentTypesBuilder,366frames: &mut Vec<(InlinerFrame<'a>, ResourcesBuilder)>,367) -> Result<IndexMap<&'a str, ComponentItemDef<'a>>> {368// This loop represents the execution of the instantiation of a369// component. This is an iterative process which is finished once all370// initializers are processed. Currently this is modeled as an infinite371// loop which drives the top-most iterator of the `frames` stack372// provided as an argument to this function.373loop {374let (frame, _) = frames.last_mut().unwrap();375types.resources_mut().set_current_instance(frame.instance);376match frame.initializers.next() {377// Process the initializer and if it started the instantiation378// of another component then we push that frame on the stack to379// continue onwards.380Some(init) => match self.initializer(frame, types, init)? {381Some(new_frame) => {382frames.push((new_frame, types.resources_mut().clone()));383}384None => {}385},386387// If there are no more initializers for this frame then the388// component it represents has finished instantiation. The389// exports of the component are collected and then the entire390// frame is discarded. The exports are then either pushed in the391// parent frame, if any, as a new component instance or they're392// returned from this function for the root set of exports.393None => {394let exports = frame395.translation396.exports397.iter()398.map(|(name, item)| Ok((*name, frame.item(*item, types)?)))399.collect::<Result<_>>()?;400let instance_ty = frame.instance_ty;401let (_, snapshot) = frames.pop().unwrap();402*types.resources_mut() = snapshot;403match frames.last_mut() {404Some((parent, _)) => {405parent.finish_instantiate(exports, instance_ty.unwrap(), types)?;406}407None => break Ok(exports),408}409}410}411}412}413414fn initializer(415&mut self,416frame: &mut InlinerFrame<'a>,417types: &mut ComponentTypesBuilder,418initializer: &'a LocalInitializer,419) -> Result<Option<InlinerFrame<'a>>> {420use LocalInitializer::*;421422match initializer {423// When a component imports an item the actual definition of the424// item is looked up here (not at runtime) via its name. The425// arguments provided in our `InlinerFrame` describe how each426// argument was defined, so we simply move it from there into the427// correct index space.428//429// Note that for the root component this will add `*::Import` items430// but for sub-components this will do resolution to connect what431// was provided as an import at the instantiation-site to what was432// needed during the component's instantiation.433Import(name, ty) => {434let arg = match frame.args.get(name.0) {435Some(arg) => arg,436437// Not all arguments need to be provided for instantiation,438// namely the root component in Wasmtime doesn't require439// structural type imports to be satisfied. These type440// imports are relevant for bindings generators and such but441// as a runtime there's not really a definition to fit in.442//443// If no argument was provided for `name` then it's asserted444// that this is a type import and additionally it's not a445// resource type import (which indeed must be provided). If446// all that passes then this initializer is effectively447// skipped.448None => {449match ty {450ComponentEntityType::Type {451created: ComponentAnyTypeId::Resource(_),452..453} => unreachable!(),454ComponentEntityType::Type { .. } => {}455_ => unreachable!(),456}457return Ok(None);458}459};460461// Next resource types need to be handled. For example if a462// resource is imported into this component then it needs to be463// assigned a unique table to provide the isolation guarantees464// of resources (this component's table is shared with no465// others). Here `register_component_entity_type` will find466// imported resources and then `lookup_resource` will find the467// resource within `arg` as necessary to lookup the original468// true definition of this resource.469//470// This is what enables tracking true resource origins471// throughout component translation while simultaneously also472// tracking unique tables for each resource in each component.473let mut path = Vec::new();474let (resources, types) = types.resources_mut_and_types();475resources.register_component_entity_type(476&frame.translation.types_ref(),477*ty,478&mut path,479&mut |path| arg.lookup_resource(path, types),480);481482// And now with all the type information out of the way the483// `arg` definition is moved into its corresponding index space.484frame.push_item(arg.clone());485}486487// Lowering a component function to a core wasm function is488// generally what "triggers compilation". Here various metadata is489// recorded and then the final component gets an initializer490// recording the lowering.491//492// NB: at this time only lowered imported functions are supported.493Lower {494func,495options,496lower_ty,497} => {498let lower_ty =499types.convert_component_func_type(frame.translation.types_ref(), *lower_ty)?;500let options_lower = self.adapter_options(frame, types, options);501let lower_core_type = options_lower.core_type;502let func = match &frame.component_funcs[*func] {503// If this component function was originally a host import504// then this is a lowered host function which needs a505// trampoline to enter WebAssembly. That's recorded here506// with all relevant information.507ComponentFuncDef::Import(path) => {508let import = self.runtime_import(path);509let options = self.canonical_options(options_lower);510let index = self.result.trampolines.push((511lower_core_type,512dfg::Trampoline::LowerImport {513import,514options,515lower_ty,516},517));518dfg::CoreDef::Trampoline(index)519}520521// This case handles when a lifted function is later522// lowered, and both the lowering and the lifting are523// happening within the same component instance.524//525// In this situation if the `canon.lower`'d function is526// called then it immediately sets `may_enter` to `false`.527// When calling the callee, however, that's `canon.lift`528// which immediately traps if `may_enter` is `false`. That529// means that this pairing of functions creates a function530// that always traps.531//532// When closely reading the spec though the precise trap533// that comes out can be somewhat variable. Technically the534// function yielded here is one that should validate the535// arguments by lifting them, and then trap. This means that536// the trap could be different depending on whether all537// arguments are valid for now. This was discussed in538// WebAssembly/component-model#51 somewhat and the539// conclusion was that we can probably get away with "always540// trap" here.541//542// The `CoreDef::AlwaysTrap` variant here is used to543// indicate that this function is valid but if something544// actually calls it then it just generates a trap545// immediately.546ComponentFuncDef::Lifted {547options: options_lift,548..549} if options_lift.instance == options_lower.instance => {550let index = self551.result552.trampolines553.push((lower_core_type, dfg::Trampoline::AlwaysTrap));554dfg::CoreDef::Trampoline(index)555}556557// Lowering a lifted function where the destination558// component is different than the source component means559// that a "fused adapter" was just identified.560//561// Metadata about this fused adapter is recorded in the562// `Adapters` output of this compilation pass. Currently the563// implementation of fused adapters is to generate a core564// wasm module which is instantiated with relevant imports565// and the exports are used as the fused adapters. At this566// time we don't know when precisely the instance will be567// created but we do know that the result of this will be an568// export from a previously-created instance.569//570// To model this the result of this arm is a571// `CoreDef::Export`. The actual indices listed within the572// export are "fake indices" in the sense of they're not573// resolved yet. This resolution will happen at a later574// compilation phase. Any usages of the `CoreDef::Export`575// here will be detected and rewritten to an actual runtime576// instance created.577//578// The `instance` field of the `CoreExport` has a marker579// which indicates that it's a fused adapter. The `item` is580// a function where the function index corresponds to the581// `adapter_idx` which contains the metadata about this582// adapter being created. The metadata is used to learn583// about the dependencies and when the adapter module can584// be instantiated.585ComponentFuncDef::Lifted {586ty: lift_ty,587func,588options: options_lift,589} => {590let adapter_idx = self.result.adapters.push(Adapter {591lift_ty: *lift_ty,592lift_options: options_lift.clone(),593lower_ty,594lower_options: options_lower,595func: func.clone(),596});597dfg::CoreDef::Adapter(adapter_idx)598}599};600frame.funcs.push((lower_core_type, func));601}602603// Lifting a core wasm function is relatively easy for now in that604// some metadata about the lifting is simply recorded. This'll get605// plumbed through to exports or a fused adapter later on.606Lift(ty, func, options) => {607let ty = types.convert_component_func_type(frame.translation.types_ref(), *ty)?;608let options = self.adapter_options(frame, types, options);609let func = frame.funcs[*func].1.clone();610frame611.component_funcs612.push(ComponentFuncDef::Lifted { ty, func, options });613}614615// A new resource type is being introduced, so it's recorded as a616// brand new resource in the final `resources` array. Additionally617// for now resource introductions are considered side effects to618// know when to register their destructors so that's recorded as619// well.620//621// Note that this has the effect of when a component is instantiated622// twice it will produce unique types for the resources from each623// instantiation. That's the intended runtime semantics and624// implementation here, however.625Resource(ty, rep, dtor) => {626let idx = self.result.resources.push(dfg::Resource {627rep: *rep,628dtor: dtor.map(|i| frame.funcs[i].1.clone()),629instance: frame.instance,630});631self.result632.side_effects633.push(dfg::SideEffect::Resource(idx));634635// Register with type translation that all future references to636// `ty` will refer to `idx`.637//638// Note that this registration information is lost when this639// component finishes instantiation due to the snapshotting640// behavior in the frame processing loop above. This is also641// intended, though, since `ty` can't be referred to outside of642// this component.643let idx = self.result.resource_index(idx);644types.resources_mut().register_resource(ty.resource(), idx);645}646647// Resource-related intrinsics are generally all the same.648// Wasmparser type information is converted to wasmtime type649// information and then new entries for each intrinsic are recorded.650ResourceNew(id, ty) => {651let id = types.resource_id(id.resource());652let index = self653.result654.trampolines655.push((*ty, dfg::Trampoline::ResourceNew(id)));656frame.funcs.push((*ty, dfg::CoreDef::Trampoline(index)));657}658ResourceRep(id, ty) => {659let id = types.resource_id(id.resource());660let index = self661.result662.trampolines663.push((*ty, dfg::Trampoline::ResourceRep(id)));664frame.funcs.push((*ty, dfg::CoreDef::Trampoline(index)));665}666ResourceDrop(id, ty) => {667let id = types.resource_id(id.resource());668let index = self669.result670.trampolines671.push((*ty, dfg::Trampoline::ResourceDrop(id)));672frame.funcs.push((*ty, dfg::CoreDef::Trampoline(index)));673}674BackpressureSet { func } => {675let index = self.result.trampolines.push((676*func,677dfg::Trampoline::BackpressureSet {678instance: frame.instance,679},680));681frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));682}683TaskReturn { result, options } => {684let results = result685.iter()686.map(|ty| types.valtype(frame.translation.types_ref(), ty))687.collect::<Result<_>>()?;688let results = types.new_tuple_type(results);689let func = options.core_type;690let options = self.adapter_options(frame, types, options);691let options = self.canonical_options(options);692let index = self693.result694.trampolines695.push((func, dfg::Trampoline::TaskReturn { results, options }));696frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));697}698TaskCancel { func } => {699let index = self.result.trampolines.push((700*func,701dfg::Trampoline::TaskCancel {702instance: frame.instance,703},704));705frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));706}707WaitableSetNew { func } => {708let index = self.result.trampolines.push((709*func,710dfg::Trampoline::WaitableSetNew {711instance: frame.instance,712},713));714frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));715}716WaitableSetWait { options } => {717let func = options.core_type;718let options = self.adapter_options(frame, types, options);719let options = self.canonical_options(options);720let index = self721.result722.trampolines723.push((func, dfg::Trampoline::WaitableSetWait { options }));724frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));725}726WaitableSetPoll { options } => {727let func = options.core_type;728let options = self.adapter_options(frame, types, options);729let options = self.canonical_options(options);730let index = self731.result732.trampolines733.push((func, dfg::Trampoline::WaitableSetPoll { options }));734frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));735}736WaitableSetDrop { func } => {737let index = self.result.trampolines.push((738*func,739dfg::Trampoline::WaitableSetDrop {740instance: frame.instance,741},742));743frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));744}745WaitableJoin { func } => {746let index = self.result.trampolines.push((747*func,748dfg::Trampoline::WaitableJoin {749instance: frame.instance,750},751));752frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));753}754Yield { func, async_ } => {755let index = self756.result757.trampolines758.push((*func, dfg::Trampoline::Yield { async_: *async_ }));759frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));760}761SubtaskDrop { func } => {762let index = self.result.trampolines.push((763*func,764dfg::Trampoline::SubtaskDrop {765instance: frame.instance,766},767));768frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));769}770SubtaskCancel { func, async_ } => {771let index = self.result.trampolines.push((772*func,773dfg::Trampoline::SubtaskCancel {774instance: frame.instance,775async_: *async_,776},777));778frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));779}780StreamNew { ty, func } => {781let InterfaceType::Stream(ty) =782types.defined_type(frame.translation.types_ref(), *ty)?783else {784unreachable!()785};786let index = self787.result788.trampolines789.push((*func, dfg::Trampoline::StreamNew { ty }));790frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));791}792StreamRead { ty, options } => {793let InterfaceType::Stream(ty) =794types.defined_type(frame.translation.types_ref(), *ty)?795else {796unreachable!()797};798let func = options.core_type;799let options = self.adapter_options(frame, types, options);800let options = self.canonical_options(options);801let index = self802.result803.trampolines804.push((func, dfg::Trampoline::StreamRead { ty, options }));805frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));806}807StreamWrite { ty, options } => {808let InterfaceType::Stream(ty) =809types.defined_type(frame.translation.types_ref(), *ty)?810else {811unreachable!()812};813let func = options.core_type;814let options = self.adapter_options(frame, types, options);815let options = self.canonical_options(options);816let index = self817.result818.trampolines819.push((func, dfg::Trampoline::StreamWrite { ty, options }));820frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));821}822StreamCancelRead { ty, func, async_ } => {823let InterfaceType::Stream(ty) =824types.defined_type(frame.translation.types_ref(), *ty)?825else {826unreachable!()827};828let index = self.result.trampolines.push((829*func,830dfg::Trampoline::StreamCancelRead {831ty,832async_: *async_,833},834));835frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));836}837StreamCancelWrite { ty, func, async_ } => {838let InterfaceType::Stream(ty) =839types.defined_type(frame.translation.types_ref(), *ty)?840else {841unreachable!()842};843let index = self.result.trampolines.push((844*func,845dfg::Trampoline::StreamCancelWrite {846ty,847async_: *async_,848},849));850frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));851}852StreamDropReadable { ty, func } => {853let InterfaceType::Stream(ty) =854types.defined_type(frame.translation.types_ref(), *ty)?855else {856unreachable!()857};858let index = self859.result860.trampolines861.push((*func, dfg::Trampoline::StreamDropReadable { ty }));862frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));863}864StreamDropWritable { ty, func } => {865let InterfaceType::Stream(ty) =866types.defined_type(frame.translation.types_ref(), *ty)?867else {868unreachable!()869};870let index = self871.result872.trampolines873.push((*func, dfg::Trampoline::StreamDropWritable { ty }));874frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));875}876FutureNew { ty, func } => {877let InterfaceType::Future(ty) =878types.defined_type(frame.translation.types_ref(), *ty)?879else {880unreachable!()881};882let index = self883.result884.trampolines885.push((*func, dfg::Trampoline::FutureNew { ty }));886frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));887}888FutureRead { ty, options } => {889let InterfaceType::Future(ty) =890types.defined_type(frame.translation.types_ref(), *ty)?891else {892unreachable!()893};894let func = options.core_type;895let options = self.adapter_options(frame, types, options);896let options = self.canonical_options(options);897let index = self898.result899.trampolines900.push((func, dfg::Trampoline::FutureRead { ty, options }));901frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));902}903FutureWrite { ty, options } => {904let InterfaceType::Future(ty) =905types.defined_type(frame.translation.types_ref(), *ty)?906else {907unreachable!()908};909let func = options.core_type;910let options = self.adapter_options(frame, types, options);911let options = self.canonical_options(options);912let index = self913.result914.trampolines915.push((func, dfg::Trampoline::FutureWrite { ty, options }));916frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));917}918FutureCancelRead { ty, func, async_ } => {919let InterfaceType::Future(ty) =920types.defined_type(frame.translation.types_ref(), *ty)?921else {922unreachable!()923};924let index = self.result.trampolines.push((925*func,926dfg::Trampoline::FutureCancelRead {927ty,928async_: *async_,929},930));931frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));932}933FutureCancelWrite { ty, func, async_ } => {934let InterfaceType::Future(ty) =935types.defined_type(frame.translation.types_ref(), *ty)?936else {937unreachable!()938};939let index = self.result.trampolines.push((940*func,941dfg::Trampoline::FutureCancelWrite {942ty,943async_: *async_,944},945));946frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));947}948FutureDropReadable { ty, func } => {949let InterfaceType::Future(ty) =950types.defined_type(frame.translation.types_ref(), *ty)?951else {952unreachable!()953};954let index = self955.result956.trampolines957.push((*func, dfg::Trampoline::FutureDropReadable { ty }));958frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));959}960FutureDropWritable { ty, func } => {961let InterfaceType::Future(ty) =962types.defined_type(frame.translation.types_ref(), *ty)?963else {964unreachable!()965};966let index = self967.result968.trampolines969.push((*func, dfg::Trampoline::FutureDropWritable { ty }));970frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));971}972ErrorContextNew { options } => {973let ty = types.error_context_table_type()?;974let func = options.core_type;975let options = self.adapter_options(frame, types, options);976let options = self.canonical_options(options);977let index = self978.result979.trampolines980.push((func, dfg::Trampoline::ErrorContextNew { ty, options }));981frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));982}983ErrorContextDebugMessage { options } => {984let ty = types.error_context_table_type()?;985let func = options.core_type;986let options = self.adapter_options(frame, types, options);987let options = self.canonical_options(options);988let index = self.result.trampolines.push((989func,990dfg::Trampoline::ErrorContextDebugMessage { ty, options },991));992frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));993}994ErrorContextDrop { func } => {995let ty = types.error_context_table_type()?;996let index = self997.result998.trampolines999.push((*func, dfg::Trampoline::ErrorContextDrop { ty }));1000frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));1001}1002ContextGet { func, i } => {1003let index = self1004.result1005.trampolines1006.push((*func, dfg::Trampoline::ContextGet(*i)));1007frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));1008}1009ContextSet { func, i } => {1010let index = self1011.result1012.trampolines1013.push((*func, dfg::Trampoline::ContextSet(*i)));1014frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));1015}10161017ModuleStatic(idx, ty) => {1018frame.modules.push(ModuleDef::Static(*idx, *ty));1019}10201021// Instantiation of a module is one of the meatier initializers that1022// we'll generate. The main magic here is that for a statically1023// known module we can order the imports as a list to exactly what1024// the static module needs to be instantiated. For imported modules,1025// however, the runtime string resolution must happen at runtime so1026// that is deferred here by organizing the arguments as a two-layer1027// `IndexMap` of what we're providing.1028//1029// In both cases though a new `RuntimeInstanceIndex` is allocated1030// and an initializer is recorded to indicate that it's being1031// instantiated.1032ModuleInstantiate(module, args) => {1033let (instance_module, init) = match &frame.modules[*module] {1034ModuleDef::Static(idx, _ty) => {1035let mut defs = Vec::new();1036for (module, name, _ty) in self.nested_modules[*idx].module.imports() {1037let instance = args[module];1038defs.push(1039self.core_def_of_module_instance_export(frame, instance, name),1040);1041}1042(1043InstanceModule::Static(*idx),1044dfg::Instance::Static(*idx, defs.into()),1045)1046}1047ModuleDef::Import(path, ty) => {1048let mut defs = IndexMap::new();1049for ((module, name), _) in types[*ty].imports.iter() {1050let instance = args[module.as_str()];1051let def =1052self.core_def_of_module_instance_export(frame, instance, name);1053defs.entry(module.to_string())1054.or_insert(IndexMap::new())1055.insert(name.to_string(), def);1056}1057let index = self.runtime_import(path);1058(1059InstanceModule::Import(*ty),1060dfg::Instance::Import(index, defs),1061)1062}1063};10641065let instance = self.result.instances.push(init);1066let instance2 = self.runtime_instances.push(instance_module);1067assert_eq!(instance, instance2);10681069self.result1070.side_effects1071.push(dfg::SideEffect::Instance(instance));10721073frame1074.module_instances1075.push(ModuleInstanceDef::Instantiated(instance, *module));1076}10771078ModuleSynthetic(map) => {1079frame1080.module_instances1081.push(ModuleInstanceDef::Synthetic(map));1082}10831084// This is one of the stages of the "magic" of implementing outer1085// aliases to components and modules. For more information on this1086// see the documentation on `LexicalScope`. This stage of the1087// implementation of outer aliases is where the `ClosedOverVars` is1088// transformed into a `ComponentClosure` state using the current1089// `InlinerFrame`'s state. This will capture the "runtime" state of1090// outer components and upvars and such naturally as part of the1091// inlining process.1092ComponentStatic(index, vars) => {1093frame.components.push(ComponentDef {1094index: *index,1095closure: ComponentClosure {1096modules: vars1097.modules1098.iter()1099.map(|(_, m)| frame.closed_over_module(m))1100.collect(),1101components: vars1102.components1103.iter()1104.map(|(_, m)| frame.closed_over_component(m))1105.collect(),1106},1107});1108}11091110// Like module instantiation is this is a "meaty" part, and don't be1111// fooled by the relative simplicity of this case. This is1112// implemented primarily by the `Inliner` structure and the design1113// of this entire module, so the "easy" step here is to simply1114// create a new inliner frame and return it to get pushed onto the1115// stack.1116ComponentInstantiate(component, args, ty) => {1117let component: &ComponentDef<'a> = &frame.components[*component];1118let index = RuntimeComponentInstanceIndex::from_u32(1119self.result.num_runtime_component_instances,1120);1121self.result.num_runtime_component_instances += 1;1122let frame = InlinerFrame::new(1123index,1124&self.nested_components[component.index],1125component.closure.clone(),1126args.iter()1127.map(|(name, item)| Ok((*name, frame.item(*item, types)?)))1128.collect::<Result<_>>()?,1129Some(*ty),1130);1131return Ok(Some(frame));1132}11331134ComponentSynthetic(map, ty) => {1135let items = map1136.iter()1137.map(|(name, index)| Ok((*name, frame.item(*index, types)?)))1138.collect::<Result<_>>()?;1139let types_ref = frame.translation.types_ref();1140let ty = types.convert_instance(types_ref, *ty)?;1141frame1142.component_instances1143.push(ComponentInstanceDef::Items(items, ty));1144}11451146// Core wasm aliases, this and the cases below, are creating1147// `CoreExport` items primarily to insert into the index space so we1148// can create a unique identifier pointing to each core wasm export1149// with the instance and relevant index/name as necessary.1150AliasExportFunc(instance, name) => {1151let (ty, def) = match &frame.module_instances[*instance] {1152ModuleInstanceDef::Instantiated(instance, module) => {1153let (ty, item) = match &frame.modules[*module] {1154ModuleDef::Static(idx, _ty) => {1155let entity = self.nested_modules[*idx].module.exports[*name];1156let ty = match entity {1157EntityIndex::Function(f) => {1158self.nested_modules[*idx].module.functions[f]1159.signature1160.unwrap_module_type_index()1161}1162_ => unreachable!(),1163};1164(ty, ExportItem::Index(entity))1165}1166ModuleDef::Import(_path, module_ty) => {1167let module_ty = &types.component_types()[*module_ty];1168let entity_ty = &module_ty.exports[&**name];1169let ty = entity_ty.unwrap_func().unwrap_module_type_index();1170(ty, ExportItem::Name((*name).to_string()))1171}1172};1173let def = dfg::CoreExport {1174instance: *instance,1175item,1176}1177.into();1178(ty, def)1179}1180ModuleInstanceDef::Synthetic(instance) => match instance[*name] {1181EntityIndex::Function(i) => frame.funcs[i].clone(),1182_ => unreachable!(),1183},1184};1185frame.funcs.push((ty, def));1186}11871188AliasExportTable(instance, name) => {1189frame.tables.push(1190match self.core_def_of_module_instance_export(frame, *instance, *name) {1191dfg::CoreDef::Export(e) => e,1192_ => unreachable!(),1193},1194);1195}11961197AliasExportGlobal(instance, name) => {1198frame.globals.push(1199match self.core_def_of_module_instance_export(frame, *instance, *name) {1200dfg::CoreDef::Export(e) => e,1201_ => unreachable!(),1202},1203);1204}12051206AliasExportMemory(instance, name) => {1207frame.memories.push(1208match self.core_def_of_module_instance_export(frame, *instance, *name) {1209dfg::CoreDef::Export(e) => e,1210_ => unreachable!(),1211},1212);1213}12141215AliasExportTag(instance, name) => {1216frame.tags.push(1217match self.core_def_of_module_instance_export(frame, *instance, *name) {1218dfg::CoreDef::Export(e) => e,1219_ => unreachable!(),1220},1221);1222}12231224AliasComponentExport(instance, name) => {1225match &frame.component_instances[*instance] {1226// Aliasing an export from an imported instance means that1227// we're extending the `ImportPath` by one name, represented1228// with the clone + push here. Afterwards an appropriate1229// item is then pushed in the relevant index space.1230ComponentInstanceDef::Import(path, ty) => {1231let path = path.push(*name);1232let def = ComponentItemDef::from_import(path, types[*ty].exports[*name])?;1233frame.push_item(def);1234}12351236// Given a component instance which was either created1237// through instantiation of a component or through a1238// synthetic renaming of items we just schlep around the1239// definitions of various items here.1240ComponentInstanceDef::Items(map, _) => frame.push_item(map[*name].clone()),1241}1242}12431244// For more information on these see `LexicalScope` but otherwise1245// this is just taking a closed over variable and inserting the1246// actual definition into the local index space since this1247// represents an outer alias to a module/component1248AliasModule(idx) => {1249frame.modules.push(frame.closed_over_module(idx));1250}1251AliasComponent(idx) => {1252frame.components.push(frame.closed_over_component(idx));1253}12541255Export(item) => match item {1256ComponentItem::Func(i) => {1257frame1258.component_funcs1259.push(frame.component_funcs[*i].clone());1260}1261ComponentItem::Module(i) => {1262frame.modules.push(frame.modules[*i].clone());1263}1264ComponentItem::Component(i) => {1265frame.components.push(frame.components[*i].clone());1266}1267ComponentItem::ComponentInstance(i) => {1268frame1269.component_instances1270.push(frame.component_instances[*i].clone());1271}12721273// Type index spaces aren't maintained during this inlining pass1274// so ignore this.1275ComponentItem::Type(_) => {}1276},1277}12781279Ok(None)1280}12811282/// "Commits" a path of an import to an actual index which is something that1283/// will be calculated at runtime.1284///1285/// Note that the cost of calculating an item for a `RuntimeImportIndex` at1286/// runtime is amortized with an `InstancePre` which represents "all the1287/// runtime imports are lined up" and after that no more name resolution is1288/// necessary.1289fn runtime_import(&mut self, path: &ImportPath<'a>) -> RuntimeImportIndex {1290*self1291.import_path_interner1292.entry(path.clone())1293.or_insert_with(|| {1294self.result.imports.push((1295path.index,1296path.path.iter().map(|s| s.to_string()).collect(),1297))1298})1299}13001301/// Returns the `CoreDef`, the canonical definition for a core wasm item,1302/// for the export `name` of `instance` within `frame`.1303fn core_def_of_module_instance_export(1304&self,1305frame: &InlinerFrame<'a>,1306instance: ModuleInstanceIndex,1307name: &'a str,1308) -> dfg::CoreDef {1309match &frame.module_instances[instance] {1310// Instantiations of a statically known module means that we can1311// refer to the exported item by a precise index, skipping name1312// lookups at runtime.1313//1314// Instantiations of an imported module, however, must do name1315// lookups at runtime since we don't know the structure ahead of1316// time here.1317ModuleInstanceDef::Instantiated(instance, module) => {1318let item = match frame.modules[*module] {1319ModuleDef::Static(idx, _ty) => {1320let entity = self.nested_modules[idx].module.exports[name];1321ExportItem::Index(entity)1322}1323ModuleDef::Import(..) => ExportItem::Name(name.to_string()),1324};1325dfg::CoreExport {1326instance: *instance,1327item,1328}1329.into()1330}13311332// This is a synthetic instance so the canonical definition of the1333// original item is returned.1334ModuleInstanceDef::Synthetic(instance) => match instance[name] {1335EntityIndex::Function(i) => frame.funcs[i].1.clone(),1336EntityIndex::Table(i) => frame.tables[i].clone().into(),1337EntityIndex::Global(i) => frame.globals[i].clone().into(),1338EntityIndex::Memory(i) => frame.memories[i].clone().into(),1339EntityIndex::Tag(_) => todo!(), // FIXME: #10252 support for tags in the component model1340},1341}1342}13431344fn memory(1345&mut self,1346frame: &InlinerFrame<'a>,1347types: &ComponentTypesBuilder,1348memory: MemoryIndex,1349) -> (dfg::CoreExport<MemoryIndex>, bool) {1350let memory = frame.memories[memory].clone().map_index(|i| match i {1351EntityIndex::Memory(i) => i,1352_ => unreachable!(),1353});1354let memory64 = match &self.runtime_instances[memory.instance] {1355InstanceModule::Static(idx) => match &memory.item {1356ExportItem::Index(i) => {1357let ty = &self.nested_modules[*idx].module.memories[*i];1358match ty.idx_type {1359IndexType::I32 => false,1360IndexType::I64 => true,1361}1362}1363ExportItem::Name(_) => unreachable!(),1364},1365InstanceModule::Import(ty) => match &memory.item {1366ExportItem::Name(name) => match types[*ty].exports[name] {1367EntityType::Memory(m) => match m.idx_type {1368IndexType::I32 => false,1369IndexType::I64 => true,1370},1371_ => unreachable!(),1372},1373ExportItem::Index(_) => unreachable!(),1374},1375};1376(memory, memory64)1377}13781379/// Translates a `LocalCanonicalOptions` which indexes into the `frame`1380/// specified into a runtime representation.1381fn adapter_options(1382&mut self,1383frame: &InlinerFrame<'a>,1384types: &ComponentTypesBuilder,1385options: &LocalCanonicalOptions,1386) -> AdapterOptions {1387let data_model = match options.data_model {1388LocalDataModel::Gc {} => DataModel::Gc {},1389LocalDataModel::LinearMemory { memory, realloc } => {1390let (memory, memory64) = memory1391.map(|i| {1392let (memory, memory64) = self.memory(frame, types, i);1393(Some(memory), memory64)1394})1395.unwrap_or((None, false));1396let realloc = realloc.map(|i| frame.funcs[i].1.clone());1397DataModel::LinearMemory {1398memory,1399memory64,1400realloc,1401}1402}1403};1404let callback = options.callback.map(|i| frame.funcs[i].1.clone());1405let post_return = options.post_return.map(|i| frame.funcs[i].1.clone());1406AdapterOptions {1407instance: frame.instance,1408string_encoding: options.string_encoding,1409callback,1410post_return,1411async_: options.async_,1412core_type: options.core_type,1413data_model,1414}1415}14161417/// Translatees an `AdapterOptions` into a `CanonicalOptions` where1418/// memories/functions are inserted into the global initializer list for1419/// use at runtime. This is only used for lowered host functions and lifted1420/// functions exported to the host.1421fn canonical_options(&mut self, options: AdapterOptions) -> dfg::OptionsId {1422let data_model = match options.data_model {1423DataModel::Gc {} => dfg::CanonicalOptionsDataModel::Gc {},1424DataModel::LinearMemory {1425memory,1426memory64: _,1427realloc,1428} => dfg::CanonicalOptionsDataModel::LinearMemory {1429memory: memory.map(|export| self.result.memories.push(export)),1430realloc: realloc.map(|def| self.result.reallocs.push(def)),1431},1432};1433let callback = options.callback.map(|def| self.result.callbacks.push(def));1434let post_return = options1435.post_return1436.map(|def| self.result.post_returns.push(def));1437self.result.options.push(dfg::CanonicalOptions {1438instance: options.instance,1439string_encoding: options.string_encoding,1440callback,1441post_return,1442async_: options.async_,1443core_type: options.core_type,1444data_model,1445})1446}14471448fn record_export(1449&mut self,1450name: &str,1451def: ComponentItemDef<'a>,1452types: &'a ComponentTypesBuilder,1453map: &mut IndexMap<String, dfg::Export>,1454) -> Result<()> {1455let export = match def {1456// Exported modules are currently saved in a `PrimaryMap`, at1457// runtime, so an index (`RuntimeModuleIndex`) is assigned here and1458// then an initializer is recorded about where the module comes1459// from.1460ComponentItemDef::Module(module) => match module {1461ModuleDef::Static(index, ty) => dfg::Export::ModuleStatic { ty, index },1462ModuleDef::Import(path, ty) => dfg::Export::ModuleImport {1463ty,1464import: self.runtime_import(&path),1465},1466},14671468ComponentItemDef::Func(func) => match func {1469// If this is a lifted function from something lowered in this1470// component then the configured options are plumbed through1471// here.1472ComponentFuncDef::Lifted { ty, func, options } => {1473let options = self.canonical_options(options);1474dfg::Export::LiftedFunction { ty, func, options }1475}14761477// Currently reexported functions from an import are not1478// supported. Being able to actually call these functions is1479// somewhat tricky and needs something like temporary scratch1480// space that isn't implemented.1481ComponentFuncDef::Import(_) => {1482bail!(1483"component export `{name}` is a reexport of an imported function which is not implemented"1484)1485}1486},14871488ComponentItemDef::Instance(instance) => {1489let mut exports = IndexMap::new();1490match instance {1491// If this instance is one that was originally imported by1492// the component itself then the imports are translated here1493// by converting to a `ComponentItemDef` and then1494// recursively recording the export as a reexport.1495//1496// Note that for now this would only work with1497// module-exporting instances.1498ComponentInstanceDef::Import(path, ty) => {1499for (name, ty) in types[ty].exports.iter() {1500let path = path.push(name);1501let def = ComponentItemDef::from_import(path, *ty)?;1502self.record_export(name, def, types, &mut exports)?;1503}1504dfg::Export::Instance { ty, exports }1505}15061507// An exported instance which is itself a bag of items is1508// translated recursively here to our `exports` map which is1509// the bag of items we're exporting.1510ComponentInstanceDef::Items(map, ty) => {1511for (name, def) in map {1512self.record_export(name, def, types, &mut exports)?;1513}1514dfg::Export::Instance { ty, exports }1515}1516}1517}15181519// FIXME(#4283) should make an official decision on whether this is1520// the final treatment of this or not.1521ComponentItemDef::Component(_) => {1522bail!("exporting a component from the root component is not supported")1523}15241525ComponentItemDef::Type(def) => dfg::Export::Type(def),1526};15271528map.insert(name.to_string(), export);1529Ok(())1530}1531}15321533impl<'a> InlinerFrame<'a> {1534fn new(1535instance: RuntimeComponentInstanceIndex,1536translation: &'a Translation<'a>,1537closure: ComponentClosure<'a>,1538args: HashMap<&'a str, ComponentItemDef<'a>>,1539instance_ty: Option<ComponentInstanceTypeId>,1540) -> Self {1541// FIXME: should iterate over the initializers of `translation` and1542// calculate the size of each index space to use `with_capacity` for1543// all the maps below. Given that doing such would be wordy and compile1544// time is otherwise not super crucial it's not done at this time.1545InlinerFrame {1546instance,1547translation,1548closure,1549args,1550instance_ty,1551initializers: translation.initializers.iter(),15521553funcs: Default::default(),1554memories: Default::default(),1555tables: Default::default(),1556globals: Default::default(),1557tags: Default::default(),15581559component_instances: Default::default(),1560component_funcs: Default::default(),1561module_instances: Default::default(),1562components: Default::default(),1563modules: Default::default(),1564}1565}15661567fn item(1568&self,1569index: ComponentItem,1570types: &mut ComponentTypesBuilder,1571) -> Result<ComponentItemDef<'a>> {1572Ok(match index {1573ComponentItem::Func(i) => ComponentItemDef::Func(self.component_funcs[i].clone()),1574ComponentItem::Component(i) => ComponentItemDef::Component(self.components[i].clone()),1575ComponentItem::ComponentInstance(i) => {1576ComponentItemDef::Instance(self.component_instances[i].clone())1577}1578ComponentItem::Module(i) => ComponentItemDef::Module(self.modules[i].clone()),1579ComponentItem::Type(t) => {1580let types_ref = self.translation.types_ref();1581ComponentItemDef::Type(types.convert_type(types_ref, t)?)1582}1583})1584}15851586/// Pushes the component `item` definition provided into the appropriate1587/// index space within this component.1588fn push_item(&mut self, item: ComponentItemDef<'a>) {1589match item {1590ComponentItemDef::Func(i) => {1591self.component_funcs.push(i);1592}1593ComponentItemDef::Module(i) => {1594self.modules.push(i);1595}1596ComponentItemDef::Component(i) => {1597self.components.push(i);1598}1599ComponentItemDef::Instance(i) => {1600self.component_instances.push(i);1601}16021603// In short, type definitions aren't tracked here.1604//1605// The longer form explanation for this is that structural types1606// like lists and records don't need to be tracked at all and the1607// only significant type which needs tracking is resource types1608// themselves. Resource types, however, are tracked within the1609// `ResourcesBuilder` state rather than an `InlinerFrame` so they're1610// ignored here as well. The general reason for that is that type1611// information is everywhere and this `InlinerFrame` is not1612// everywhere so it seemed like it would make sense to split the1613// two.1614//1615// Note though that this case is actually frequently hit, so it1616// can't be `unreachable!()`. Instead callers are responsible for1617// handling this appropriately with respect to resources.1618ComponentItemDef::Type(_ty) => {}1619}1620}16211622fn closed_over_module(&self, index: &ClosedOverModule) -> ModuleDef<'a> {1623match *index {1624ClosedOverModule::Local(i) => self.modules[i].clone(),1625ClosedOverModule::Upvar(i) => self.closure.modules[i].clone(),1626}1627}16281629fn closed_over_component(&self, index: &ClosedOverComponent) -> ComponentDef<'a> {1630match *index {1631ClosedOverComponent::Local(i) => self.components[i].clone(),1632ClosedOverComponent::Upvar(i) => self.closure.components[i].clone(),1633}1634}16351636/// Completes the instantiation of a subcomponent and records type1637/// information for the instance that was produced.1638///1639/// This method is invoked when an `InlinerFrame` finishes for a1640/// subcomponent. The `def` provided represents the instance that was1641/// produced from instantiation, and `ty` is the wasmparser-defined type of1642/// the instance produced.1643///1644/// The purpose of this method is to record type information about resources1645/// in the instance produced. In the component model all instantiations of a1646/// component produce fresh new types for all resources which are unequal to1647/// all prior resources. This means that if wasmparser's `ty` type1648/// information references a unique resource within `def` that has never1649/// been registered before then that means it's a defined resource within1650/// the component that was just instantiated (as opposed to an imported1651/// resource which was reexported).1652///1653/// Further type translation after this instantiation can refer to these1654/// resource types and a mapping from those types to the wasmtime-internal1655/// types is required, so this method builds up those mappings.1656///1657/// Essentially what happens here is that the `ty` type is registered and1658/// any new unique resources are registered so new tables can be introduced1659/// along with origin information about the actual underlying resource type1660/// and which component instantiated it.1661fn finish_instantiate(1662&mut self,1663exports: IndexMap<&'a str, ComponentItemDef<'a>>,1664ty: ComponentInstanceTypeId,1665types: &mut ComponentTypesBuilder,1666) -> Result<()> {1667let types_ref = self.translation.types_ref();1668{1669let (resources, types) = types.resources_mut_and_types();1670let mut path = Vec::new();1671resources.register_component_entity_type(1672&types_ref,1673ComponentEntityType::Instance(ty),1674&mut path,1675&mut |path| match path {1676[] => unreachable!(),1677[name, rest @ ..] => exports[name].lookup_resource(rest, types),1678},1679);1680}1681let ty = types.convert_instance(types_ref, ty)?;1682let def = ComponentInstanceDef::Items(exports, ty);1683let arg = ComponentItemDef::Instance(def);1684self.push_item(arg);1685Ok(())1686}1687}16881689impl<'a> ImportPath<'a> {1690fn root(index: ImportIndex) -> ImportPath<'a> {1691ImportPath {1692index,1693path: Vec::new(),1694}1695}16961697fn push(&self, s: impl Into<Cow<'a, str>>) -> ImportPath<'a> {1698let mut new = self.clone();1699new.path.push(s.into());1700new1701}1702}17031704impl<'a> ComponentItemDef<'a> {1705fn from_import(path: ImportPath<'a>, ty: TypeDef) -> Result<ComponentItemDef<'a>> {1706let item = match ty {1707TypeDef::Module(ty) => ComponentItemDef::Module(ModuleDef::Import(path, ty)),1708TypeDef::ComponentInstance(ty) => {1709ComponentItemDef::Instance(ComponentInstanceDef::Import(path, ty))1710}1711TypeDef::ComponentFunc(_ty) => ComponentItemDef::Func(ComponentFuncDef::Import(path)),1712// FIXME(#4283) should commit one way or another to how this1713// should be treated.1714TypeDef::Component(_ty) => bail!("root-level component imports are not supported"),1715TypeDef::Interface(_) | TypeDef::Resource(_) => ComponentItemDef::Type(ty),1716TypeDef::CoreFunc(_) => unreachable!(),1717};1718Ok(item)1719}17201721/// Walks the `path` within `self` to find a resource at that path.1722///1723/// This method is used when resources are found within wasmparser's type1724/// information and they need to be correlated with actual concrete1725/// definitions from this inlining pass. The `path` here is a list of1726/// instance export names (or empty) to walk to reach down into the final1727/// definition which should refer to a resource itself.1728fn lookup_resource(&self, path: &[&str], types: &ComponentTypes) -> ResourceIndex {1729let mut cur = self.clone();17301731// Each element of `path` represents unwrapping a layer of an instance1732// type, so handle those here by updating `cur` iteratively.1733for element in path.iter().copied() {1734let instance = match cur {1735ComponentItemDef::Instance(def) => def,1736_ => unreachable!(),1737};1738cur = match instance {1739// If this instance is a "bag of things" then this is as easy as1740// looking up the name in the bag of names.1741ComponentInstanceDef::Items(names, _) => names[element].clone(),17421743// If, however, this instance is an imported instance then this1744// is a further projection within the import with one more path1745// element. The `types` type information is used to lookup the1746// type of `element` within the instance type, and that's used1747// in conjunction with a one-longer `path` to produce a new item1748// definition.1749ComponentInstanceDef::Import(path, ty) => {1750ComponentItemDef::from_import(path.push(element), types[ty].exports[element])1751.unwrap()1752}1753};1754}17551756// Once `path` has been iterated over it must be the case that the final1757// item is a resource type, in which case a lookup can be performed.1758match cur {1759ComponentItemDef::Type(TypeDef::Resource(idx)) => types[idx].ty,1760_ => unreachable!(),1761}1762}1763}17641765#[derive(Clone, Copy)]1766enum InstanceModule {1767Static(StaticModuleIndex),1768Import(TypeModuleIndex),1769}177017711772