Path: blob/main/crates/environ/src/component/translate.rs
1692 views
use crate::ScopeVec;1use crate::component::dfg::AbstractInstantiations;2use crate::component::*;3use crate::prelude::*;4use crate::{5EngineOrModuleTypeIndex, EntityIndex, ModuleEnvironment, ModuleInternedTypeIndex,6ModuleTranslation, ModuleTypesBuilder, PrimaryMap, TagIndex, Tunables, TypeConvert,7WasmHeapType, WasmResult, WasmValType,8};9use anyhow::anyhow;10use anyhow::{Result, bail};11use cranelift_entity::SecondaryMap;12use cranelift_entity::packed_option::PackedOption;13use indexmap::IndexMap;14use std::collections::HashMap;15use std::mem;16use wasmparser::component_types::{17AliasableResourceId, ComponentCoreModuleTypeId, ComponentDefinedTypeId, ComponentEntityType,18ComponentFuncTypeId, ComponentInstanceTypeId, ComponentValType,19};20use wasmparser::types::Types;21use wasmparser::{Chunk, ComponentImportName, Encoding, Parser, Payload, Validator};2223mod adapt;24pub use self::adapt::*;25mod inline;2627/// Structure used to translate a component and parse it.28pub struct Translator<'a, 'data> {29/// The current component being translated.30///31/// This will get swapped out as translation traverses the body of a32/// component and a sub-component is entered or left.33result: Translation<'data>,3435/// Current state of parsing a binary component. Note that like `result`36/// this will change as the component is traversed.37parser: Parser,3839/// Stack of lexical scopes that are in-progress but not finished yet.40///41/// This is pushed to whenever a component is entered and popped from42/// whenever a component is left. Each lexical scope also contains43/// information about the variables that it is currently required to close44/// over which is threaded into the current in-progress translation of45/// the sub-component which pushed a scope here.46lexical_scopes: Vec<LexicalScope<'data>>,4748/// The validator in use to verify that the raw input binary is a valid49/// component.50validator: &'a mut Validator,5152/// Type information shared for the entire component.53///54/// This builder is also used for all core wasm modules found to intern55/// signatures across all modules.56types: PreInliningComponentTypes<'a>,5758/// The compiler configuration provided by the embedder.59tunables: &'a Tunables,6061/// Auxiliary location to push generated adapter modules onto.62scope_vec: &'data ScopeVec<u8>,6364/// Completely translated core wasm modules that have been found so far.65///66/// Note that this translation only involves learning about type67/// information and functions are not actually compiled here.68static_modules: PrimaryMap<StaticModuleIndex, ModuleTranslation<'data>>,6970/// Completely translated components that have been found so far.71///72/// As frames are popped from `lexical_scopes` their completed component73/// will be pushed onto this list.74static_components: PrimaryMap<StaticComponentIndex, Translation<'data>>,75}7677/// Representation of the syntactic scope of a component meaning where it is78/// and what its state is at in the binary format.79///80/// These scopes are pushed and popped when a sub-component starts being81/// parsed and finishes being parsed. The main purpose of this frame is to82/// have a `ClosedOverVars` field which encapsulates data that is inherited83/// from the scope specified into the component being translated just beneath84/// it.85///86/// This structure exists to implement outer aliases to components and modules.87/// When a component or module is closed over then that means it needs to be88/// inherited in a sense to the component which actually had the alias. This is89/// achieved with a deceptively simple scheme where each parent of the90/// component with the alias will inherit the component from the desired91/// location.92///93/// For example with a component structure that looks like:94///95/// ```wasm96/// (component $A97/// (core module $M)98/// (component $B99/// (component $C100/// (alias outer $A $M (core module))101/// )102/// )103/// )104/// ```105///106/// here the `C` component is closing over `M` located in the root component107/// `A`. When `C` is being translated the `lexical_scopes` field will look like108/// `[A, B]`. When the alias is encountered (for module index 0) this will109/// place a `ClosedOverModule::Local(0)` entry into the `closure_args` field of110/// `A`'s frame. This will in turn give a `ModuleUpvarIndex` which is then111/// inserted into `closure_args` in `B`'s frame. This produces yet another112/// `ModuleUpvarIndex` which is finally inserted into `C`'s module index space113/// via `LocalInitializer::AliasModuleUpvar` with the last index.114///115/// All of these upvar indices and such are interpreted in the "inline" phase116/// of compilation and not at runtime. This means that when `A` is being117/// instantiated one of its initializers will be118/// `LocalInitializer::ComponentStatic`. This starts to create `B` and the119/// variables captured for `B` are listed as local module 0, or `M`. This list120/// is then preserved in the definition of the component `B` and later reused121/// by `C` again to finally get access to the closed over component.122///123/// Effectively the scopes are managed hierarchically where a reference to an124/// outer variable automatically injects references into all parents up to125/// where the reference is. This variable scopes are the processed during126/// inlining where a component definition is a reference to the static127/// component information (`Translation`) plus closed over variables128/// (`ComponentClosure` during inlining).129struct LexicalScope<'data> {130/// Current state of translating the `translation` below.131parser: Parser,132/// Current state of the component's translation as found so far.133translation: Translation<'data>,134/// List of captures that `translation` will need to process to create the135/// sub-component which is directly beneath this lexical scope.136closure_args: ClosedOverVars,137}138139/// A "local" translation of a component.140///141/// This structure is used as a sort of in-progress translation of a component.142/// This is not `Component` which is the final form as consumed by Wasmtime143/// at runtime. Instead this is a fairly simple representation of a component144/// where almost everything is ordered as a list of initializers. The binary145/// format is translated to a list of initializers here which is later processed146/// during "inlining" to produce a final component with the final set of147/// initializers.148#[derive(Default)]149struct Translation<'data> {150/// Instructions which form this component.151///152/// There is one initializer for all members of each index space, and all153/// index spaces are incrementally built here as the initializer list is154/// processed.155initializers: Vec<LocalInitializer<'data>>,156157/// The list of exports from this component, as pairs of names and an158/// index into an index space of what's being exported.159exports: IndexMap<&'data str, ComponentItem>,160161/// Type information produced by `wasmparser` for this component.162///163/// This type information is available after the translation of the entire164/// component has finished, e.g. for the `inline` pass, but beforehand this165/// is set to `None`.166types: Option<Types>,167}168169// NB: the type information contained in `LocalInitializer` should always point170// to `wasmparser`'s type information, not Wasmtime's. Component types cannot be171// fully determined due to resources until instantiations are known which is172// tracked during the inlining phase. This means that all type information below173// is straight from `wasmparser`'s passes.174enum LocalInitializer<'data> {175// imports176Import(ComponentImportName<'data>, ComponentEntityType),177178// canonical function sections179Lower {180func: ComponentFuncIndex,181lower_ty: ComponentFuncTypeId,182options: LocalCanonicalOptions,183},184Lift(ComponentFuncTypeId, FuncIndex, LocalCanonicalOptions),185186// resources187Resource(AliasableResourceId, WasmValType, Option<FuncIndex>),188ResourceNew(AliasableResourceId, ModuleInternedTypeIndex),189ResourceRep(AliasableResourceId, ModuleInternedTypeIndex),190ResourceDrop(AliasableResourceId, ModuleInternedTypeIndex),191192BackpressureSet {193func: ModuleInternedTypeIndex,194},195TaskReturn {196result: Option<ComponentValType>,197options: LocalCanonicalOptions,198},199TaskCancel {200func: ModuleInternedTypeIndex,201},202WaitableSetNew {203func: ModuleInternedTypeIndex,204},205WaitableSetWait {206options: LocalCanonicalOptions,207},208WaitableSetPoll {209options: LocalCanonicalOptions,210},211WaitableSetDrop {212func: ModuleInternedTypeIndex,213},214WaitableJoin {215func: ModuleInternedTypeIndex,216},217Yield {218func: ModuleInternedTypeIndex,219async_: bool,220},221SubtaskDrop {222func: ModuleInternedTypeIndex,223},224SubtaskCancel {225func: ModuleInternedTypeIndex,226async_: bool,227},228StreamNew {229ty: ComponentDefinedTypeId,230func: ModuleInternedTypeIndex,231},232StreamRead {233ty: ComponentDefinedTypeId,234options: LocalCanonicalOptions,235},236StreamWrite {237ty: ComponentDefinedTypeId,238options: LocalCanonicalOptions,239},240StreamCancelRead {241ty: ComponentDefinedTypeId,242func: ModuleInternedTypeIndex,243async_: bool,244},245StreamCancelWrite {246ty: ComponentDefinedTypeId,247func: ModuleInternedTypeIndex,248async_: bool,249},250StreamDropReadable {251ty: ComponentDefinedTypeId,252func: ModuleInternedTypeIndex,253},254StreamDropWritable {255ty: ComponentDefinedTypeId,256func: ModuleInternedTypeIndex,257},258FutureNew {259ty: ComponentDefinedTypeId,260func: ModuleInternedTypeIndex,261},262FutureRead {263ty: ComponentDefinedTypeId,264options: LocalCanonicalOptions,265},266FutureWrite {267ty: ComponentDefinedTypeId,268options: LocalCanonicalOptions,269},270FutureCancelRead {271ty: ComponentDefinedTypeId,272func: ModuleInternedTypeIndex,273async_: bool,274},275FutureCancelWrite {276ty: ComponentDefinedTypeId,277func: ModuleInternedTypeIndex,278async_: bool,279},280FutureDropReadable {281ty: ComponentDefinedTypeId,282func: ModuleInternedTypeIndex,283},284FutureDropWritable {285ty: ComponentDefinedTypeId,286func: ModuleInternedTypeIndex,287},288ErrorContextNew {289options: LocalCanonicalOptions,290},291ErrorContextDebugMessage {292options: LocalCanonicalOptions,293},294ErrorContextDrop {295func: ModuleInternedTypeIndex,296},297ContextGet {298func: ModuleInternedTypeIndex,299i: u32,300},301ContextSet {302func: ModuleInternedTypeIndex,303i: u32,304},305306// core wasm modules307ModuleStatic(StaticModuleIndex, ComponentCoreModuleTypeId),308309// core wasm module instances310ModuleInstantiate(ModuleIndex, HashMap<&'data str, ModuleInstanceIndex>),311ModuleSynthetic(HashMap<&'data str, EntityIndex>),312313// components314ComponentStatic(StaticComponentIndex, ClosedOverVars),315316// component instances317ComponentInstantiate(318ComponentIndex,319HashMap<&'data str, ComponentItem>,320ComponentInstanceTypeId,321),322ComponentSynthetic(HashMap<&'data str, ComponentItem>, ComponentInstanceTypeId),323324// alias section325AliasExportFunc(ModuleInstanceIndex, &'data str),326AliasExportTable(ModuleInstanceIndex, &'data str),327AliasExportGlobal(ModuleInstanceIndex, &'data str),328AliasExportMemory(ModuleInstanceIndex, &'data str),329AliasExportTag(ModuleInstanceIndex, &'data str),330AliasComponentExport(ComponentInstanceIndex, &'data str),331AliasModule(ClosedOverModule),332AliasComponent(ClosedOverComponent),333334// export section335Export(ComponentItem),336}337338/// The "closure environment" of components themselves.339///340/// For more information see `LexicalScope`.341#[derive(Default)]342struct ClosedOverVars {343components: PrimaryMap<ComponentUpvarIndex, ClosedOverComponent>,344modules: PrimaryMap<ModuleUpvarIndex, ClosedOverModule>,345}346347/// Description how a component is closed over when the closure variables for348/// a component are being created.349///350/// For more information see `LexicalScope`.351enum ClosedOverComponent {352/// A closed over component is coming from the local component's index353/// space, meaning a previously defined component is being captured.354Local(ComponentIndex),355/// A closed over component is coming from our own component's list of356/// upvars. This list was passed to us by our enclosing component, which357/// will eventually have bottomed out in closing over a `Local` component358/// index for some parent component.359Upvar(ComponentUpvarIndex),360}361362/// Same as `ClosedOverComponent`, but for modules.363enum ClosedOverModule {364Local(ModuleIndex),365Upvar(ModuleUpvarIndex),366}367368/// The data model for objects that are not unboxed in locals.369#[derive(Debug, Clone, Hash, Eq, PartialEq)]370pub enum LocalDataModel {371/// Data is stored in GC objects.372Gc {},373374/// Data is stored in a linear memory.375LinearMemory {376/// An optional memory definition supplied.377memory: Option<MemoryIndex>,378/// An optional definition of `realloc` to used.379realloc: Option<FuncIndex>,380},381}382383/// Representation of canonical ABI options.384struct LocalCanonicalOptions {385string_encoding: StringEncoding,386post_return: Option<FuncIndex>,387async_: bool,388callback: Option<FuncIndex>,389/// The type index of the core GC types signature.390core_type: ModuleInternedTypeIndex,391data_model: LocalDataModel,392}393394enum Action {395KeepGoing,396Skip(usize),397Done,398}399400impl<'a, 'data> Translator<'a, 'data> {401/// Creates a new translation state ready to translate a component.402pub fn new(403tunables: &'a Tunables,404validator: &'a mut Validator,405types: &'a mut ComponentTypesBuilder,406scope_vec: &'data ScopeVec<u8>,407) -> Self {408let mut parser = Parser::new(0);409parser.set_features(*validator.features());410Self {411result: Translation::default(),412tunables,413validator,414types: PreInliningComponentTypes::new(types),415parser,416lexical_scopes: Vec::new(),417static_components: Default::default(),418static_modules: Default::default(),419scope_vec,420}421}422423/// Translates the binary `component`.424///425/// This is the workhorse of compilation which will parse all of426/// `component` and create type information for Wasmtime and such. The427/// `component` does not have to be valid and it will be validated during428/// compilation.429///430/// The result of this function is a tuple of the final component's431/// description plus a list of core wasm modules found within the432/// component. The component's description actually erases internal433/// components, instances, etc, as much as it can. Instead `Component`434/// retains a flat list of initializers (no nesting) which was created435/// as part of compilation from the nested structure of the original436/// component.437///438/// The list of core wasm modules found is provided to allow compiling439/// modules externally in parallel. Additionally initializers in440/// `Component` may refer to the modules in the map returned by index.441///442/// # Errors443///444/// This function will return an error if the `component` provided is445/// invalid.446pub fn translate(447mut self,448component: &'data [u8],449) -> Result<(450ComponentTranslation,451PrimaryMap<StaticModuleIndex, ModuleTranslation<'data>>,452)> {453// First up wasmparser is used to actually perform the translation and454// validation of this component. This will produce a list of core wasm455// modules in addition to components which are found during the456// translation process. When doing this only a `Translation` is created457// which is a simple representation of a component.458let mut remaining = component;459loop {460let payload = match self.parser.parse(remaining, true)? {461Chunk::Parsed { payload, consumed } => {462remaining = &remaining[consumed..];463payload464}465Chunk::NeedMoreData(_) => unreachable!(),466};467468match self.translate_payload(payload, component)? {469Action::KeepGoing => {}470Action::Skip(n) => remaining = &remaining[n..],471Action::Done => break,472}473}474assert!(remaining.is_empty());475assert!(self.lexical_scopes.is_empty());476477// ... after translation initially finishes the next pass is performed478// which we're calling "inlining". This will "instantiate" the root479// component, following nested component instantiations, creating a480// global list of initializers along the way. This phase uses the simple481// initializers in each component to track dataflow of host imports and482// internal references to items throughout a component at compile-time.483// The produce initializers in the final `Component` are intended to be484// much simpler than the original component and more efficient for485// Wasmtime to process at runtime as well (e.g. no string lookups as486// most everything is done through indices instead).487let mut component = inline::run(488self.types.types_mut_for_inlining(),489&self.result,490&self.static_modules,491&self.static_components,492)?;493494self.partition_adapter_modules(&mut component);495496let translation =497component.finish(self.types.types_mut_for_inlining(), self.result.types_ref())?;498499self.analyze_function_imports(&translation);500501Ok((translation, self.static_modules))502}503504fn analyze_function_imports(&mut self, translation: &ComponentTranslation) {505// First, abstract interpret the initializers to create a map from each506// static module to its abstract set of instantiations.507let mut instantiations = SecondaryMap::<StaticModuleIndex, AbstractInstantiations>::new();508let mut instance_to_module =509PrimaryMap::<RuntimeInstanceIndex, PackedOption<StaticModuleIndex>>::new();510for init in &translation.component.initializers {511match init {512GlobalInitializer::InstantiateModule(instantiation) => match instantiation {513InstantiateModule::Static(module, args) => {514instantiations[*module].join(AbstractInstantiations::One(&*args));515instance_to_module.push(Some(*module).into());516}517_ => {518instance_to_module.push(None.into());519}520},521_ => continue,522}523}524525// Second, make sure to mark exported modules as instantiated many526// times, since they could be linked with who-knows-what at runtime.527for item in translation.component.export_items.values() {528if let Export::ModuleStatic { index, .. } = item {529instantiations[*index].join(AbstractInstantiations::Many)530}531}532533// Finally, iterate over our instantiations and record statically-known534// function imports so that they can get translated into direct calls535// (and eventually get inlined) rather than indirect calls through the536// imports table.537for (module, instantiations) in instantiations.iter() {538let args = match instantiations {539dfg::AbstractInstantiations::Many | dfg::AbstractInstantiations::None => continue,540dfg::AbstractInstantiations::One(args) => args,541};542543let mut imported_func_counter = 0_u32;544for (i, arg) in args.iter().enumerate() {545// Only consider function imports.546let (_, _, crate::types::EntityType::Function(_)) =547self.static_modules[module].module.import(i).unwrap()548else {549continue;550};551552let imported_func = FuncIndex::from_u32(imported_func_counter);553imported_func_counter += 1;554debug_assert!(555self.static_modules[module]556.module557.defined_func_index(imported_func)558.is_none()559);560561match arg {562CoreDef::InstanceFlags(_) => unreachable!("instance flags are not a function"),563564// We could in theory inline these trampolines, so it could565// potentially make sense to record that we know this566// imported function is this particular trampoline. However,567// everything else is based around (module,568// defined-function) pairs and these trampolines don't fit569// that paradigm. Also, inlining trampolines gets really570// tricky when we consider the stack pointer, frame pointer,571// and return address note-taking that they do for the572// purposes of stack walking. We could, with enough effort,573// turn them into direct calls even though we probably574// wouldn't ever inline them, but it just doesn't seem worth575// the effort.576CoreDef::Trampoline(_) => continue,577578// This imported function is an export from another579// instance, a perfect candidate for becoming an inlinable580// direct call!581CoreDef::Export(export) => {582let Some(arg_module) = &instance_to_module[export.instance].expand() else {583// Instance of a dynamic module that is not part of584// this component, not a statically-known module585// inside this component. We have to do an indirect586// call.587continue;588};589590let ExportItem::Index(EntityIndex::Function(arg_func)) = &export.item591else {592unreachable!("function imports must be functions")593};594595let Some(arg_module_def_func) = self.static_modules[*arg_module]596.module597.defined_func_index(*arg_func)598else {599// TODO: we should ideally follow re-export chains600// to bottom out the instantiation argument in601// either a definition or an import at the root602// component boundary. In practice, this pattern is603// rare, so following these chains is left for the604// Future.605continue;606};607608assert!(609self.static_modules[module].known_imported_functions[imported_func]610.is_none()611);612self.static_modules[module].known_imported_functions[imported_func] =613Some((*arg_module, arg_module_def_func));614}615}616}617}618}619620fn translate_payload(621&mut self,622payload: Payload<'data>,623component: &'data [u8],624) -> Result<Action> {625match payload {626Payload::Version {627num,628encoding,629range,630} => {631self.validator.version(num, encoding, &range)?;632633match encoding {634Encoding::Component => {}635Encoding::Module => {636bail!("attempted to parse a wasm module with a component parser");637}638}639}640641Payload::End(offset) => {642assert!(self.result.types.is_none());643self.result.types = Some(self.validator.end(offset)?);644645// Exit the current lexical scope. If there is no parent (no646// frame currently on the stack) then translation is finished.647// Otherwise that means that a nested component has been648// completed and is recorded as such.649let LexicalScope {650parser,651translation,652closure_args,653} = match self.lexical_scopes.pop() {654Some(frame) => frame,655None => return Ok(Action::Done),656};657self.parser = parser;658let component = mem::replace(&mut self.result, translation);659let static_idx = self.static_components.push(component);660self.result661.initializers662.push(LocalInitializer::ComponentStatic(static_idx, closure_args));663}664665// When we see a type section the types are validated and then666// translated into Wasmtime's representation. Each active type667// definition is recorded in the `ComponentTypesBuilder` tables, or668// this component's active scope.669//670// Note that the push/pop of the component types scope happens above671// in `Version` and `End` since multiple type sections can appear672// within a component.673Payload::ComponentTypeSection(s) => {674let mut component_type_index =675self.validator.types(0).unwrap().component_type_count();676self.validator.component_type_section(&s)?;677678// Look for resource types and if a local resource is defined679// then an initializer is added to define that resource type and680// reference its destructor.681let types = self.validator.types(0).unwrap();682for ty in s {683match ty? {684wasmparser::ComponentType::Resource { rep, dtor } => {685let rep = self.types.convert_valtype(rep)?;686let id = types687.component_any_type_at(component_type_index)688.unwrap_resource();689let dtor = dtor.map(FuncIndex::from_u32);690self.result691.initializers692.push(LocalInitializer::Resource(id, rep, dtor));693}694695// no extra processing needed696wasmparser::ComponentType::Defined(_)697| wasmparser::ComponentType::Func(_)698| wasmparser::ComponentType::Instance(_)699| wasmparser::ComponentType::Component(_) => {}700}701702component_type_index += 1;703}704}705Payload::CoreTypeSection(s) => {706self.validator.core_type_section(&s)?;707}708709// Processing the import section at this point is relatively simple710// which is to simply record the name of the import and the type711// information associated with it.712Payload::ComponentImportSection(s) => {713self.validator.component_import_section(&s)?;714for import in s {715let import = import?;716let types = self.validator.types(0).unwrap();717let ty = types718.component_entity_type_of_import(import.name.0)719.unwrap();720self.result721.initializers722.push(LocalInitializer::Import(import.name, ty));723}724}725726// Entries in the canonical section will get initializers recorded727// with the listed options for lifting/lowering.728Payload::ComponentCanonicalSection(s) => {729let types = self.validator.types(0).unwrap();730let mut core_func_index = types.function_count();731self.validator.component_canonical_section(&s)?;732for func in s {733let init = match func? {734wasmparser::CanonicalFunction::Lift {735type_index,736core_func_index,737options,738} => {739let ty = self740.validator741.types(0)742.unwrap()743.component_any_type_at(type_index)744.unwrap_func();745746let func = FuncIndex::from_u32(core_func_index);747let options = self.canonical_options(&options, core_func_index)?;748LocalInitializer::Lift(ty, func, options)749}750wasmparser::CanonicalFunction::Lower {751func_index,752options,753} => {754let lower_ty = self755.validator756.types(0)757.unwrap()758.component_function_at(func_index);759let func = ComponentFuncIndex::from_u32(func_index);760let options = self.canonical_options(&options, core_func_index)?;761core_func_index += 1;762LocalInitializer::Lower {763func,764options,765lower_ty,766}767}768wasmparser::CanonicalFunction::ResourceNew { resource } => {769let resource = self770.validator771.types(0)772.unwrap()773.component_any_type_at(resource)774.unwrap_resource();775let ty = self.core_func_signature(core_func_index)?;776core_func_index += 1;777LocalInitializer::ResourceNew(resource, ty)778}779wasmparser::CanonicalFunction::ResourceDrop { resource } => {780let resource = self781.validator782.types(0)783.unwrap()784.component_any_type_at(resource)785.unwrap_resource();786let ty = self.core_func_signature(core_func_index)?;787core_func_index += 1;788LocalInitializer::ResourceDrop(resource, ty)789}790wasmparser::CanonicalFunction::ResourceDropAsync { resource } => {791let _ = resource;792bail!("support for `resource.drop async` not implemented yet")793}794wasmparser::CanonicalFunction::ResourceRep { resource } => {795let resource = self796.validator797.types(0)798.unwrap()799.component_any_type_at(resource)800.unwrap_resource();801let ty = self.core_func_signature(core_func_index)?;802core_func_index += 1;803LocalInitializer::ResourceRep(resource, ty)804}805wasmparser::CanonicalFunction::ThreadSpawnRef { .. }806| wasmparser::CanonicalFunction::ThreadSpawnIndirect { .. }807| wasmparser::CanonicalFunction::ThreadAvailableParallelism => {808bail!("unsupported intrinsic")809}810wasmparser::CanonicalFunction::BackpressureSet => {811let core_type = self.core_func_signature(core_func_index)?;812core_func_index += 1;813LocalInitializer::BackpressureSet { func: core_type }814}815wasmparser::CanonicalFunction::TaskReturn { result, options } => {816let result = result.map(|ty| match ty {817wasmparser::ComponentValType::Primitive(ty) => {818ComponentValType::Primitive(ty)819}820wasmparser::ComponentValType::Type(ty) => ComponentValType::Type(821self.validator822.types(0)823.unwrap()824.component_defined_type_at(ty),825),826});827let options = self.canonical_options(&options, core_func_index)?;828core_func_index += 1;829LocalInitializer::TaskReturn { result, options }830}831wasmparser::CanonicalFunction::TaskCancel => {832let func = self.core_func_signature(core_func_index)?;833core_func_index += 1;834LocalInitializer::TaskCancel { func }835}836wasmparser::CanonicalFunction::WaitableSetNew => {837let func = self.core_func_signature(core_func_index)?;838core_func_index += 1;839LocalInitializer::WaitableSetNew { func }840}841wasmparser::CanonicalFunction::WaitableSetWait { async_, memory } => {842let core_type = self.core_func_signature(core_func_index)?;843core_func_index += 1;844LocalInitializer::WaitableSetWait {845options: LocalCanonicalOptions {846core_type,847async_,848data_model: LocalDataModel::LinearMemory {849memory: Some(MemoryIndex::from_u32(memory)),850realloc: None,851},852post_return: None,853callback: None,854string_encoding: StringEncoding::Utf8,855},856}857}858wasmparser::CanonicalFunction::WaitableSetPoll { async_, memory } => {859let core_type = self.core_func_signature(core_func_index)?;860core_func_index += 1;861LocalInitializer::WaitableSetPoll {862options: LocalCanonicalOptions {863core_type,864async_,865data_model: LocalDataModel::LinearMemory {866memory: Some(MemoryIndex::from_u32(memory)),867realloc: None,868},869post_return: None,870callback: None,871string_encoding: StringEncoding::Utf8,872},873}874}875wasmparser::CanonicalFunction::WaitableSetDrop => {876let func = self.core_func_signature(core_func_index)?;877core_func_index += 1;878LocalInitializer::WaitableSetDrop { func }879}880wasmparser::CanonicalFunction::WaitableJoin => {881let func = self.core_func_signature(core_func_index)?;882core_func_index += 1;883LocalInitializer::WaitableJoin { func }884}885wasmparser::CanonicalFunction::Yield { async_ } => {886let func = self.core_func_signature(core_func_index)?;887core_func_index += 1;888LocalInitializer::Yield { func, async_ }889}890wasmparser::CanonicalFunction::SubtaskDrop => {891let func = self.core_func_signature(core_func_index)?;892core_func_index += 1;893LocalInitializer::SubtaskDrop { func }894}895wasmparser::CanonicalFunction::SubtaskCancel { async_ } => {896let func = self.core_func_signature(core_func_index)?;897core_func_index += 1;898LocalInitializer::SubtaskCancel { func, async_ }899}900wasmparser::CanonicalFunction::StreamNew { ty } => {901let ty = self902.validator903.types(0)904.unwrap()905.component_defined_type_at(ty);906let func = self.core_func_signature(core_func_index)?;907core_func_index += 1;908LocalInitializer::StreamNew { ty, func }909}910wasmparser::CanonicalFunction::StreamRead { ty, options } => {911let ty = self912.validator913.types(0)914.unwrap()915.component_defined_type_at(ty);916let options = self.canonical_options(&options, core_func_index)?;917core_func_index += 1;918LocalInitializer::StreamRead { ty, options }919}920wasmparser::CanonicalFunction::StreamWrite { ty, options } => {921let ty = self922.validator923.types(0)924.unwrap()925.component_defined_type_at(ty);926let options = self.canonical_options(&options, core_func_index)?;927core_func_index += 1;928LocalInitializer::StreamWrite { ty, options }929}930wasmparser::CanonicalFunction::StreamCancelRead { ty, async_ } => {931let ty = self932.validator933.types(0)934.unwrap()935.component_defined_type_at(ty);936let func = self.core_func_signature(core_func_index)?;937core_func_index += 1;938LocalInitializer::StreamCancelRead { ty, func, async_ }939}940wasmparser::CanonicalFunction::StreamCancelWrite { ty, async_ } => {941let ty = self942.validator943.types(0)944.unwrap()945.component_defined_type_at(ty);946let func = self.core_func_signature(core_func_index)?;947core_func_index += 1;948LocalInitializer::StreamCancelWrite { ty, func, async_ }949}950wasmparser::CanonicalFunction::StreamDropReadable { ty } => {951let ty = self952.validator953.types(0)954.unwrap()955.component_defined_type_at(ty);956let func = self.core_func_signature(core_func_index)?;957core_func_index += 1;958LocalInitializer::StreamDropReadable { ty, func }959}960wasmparser::CanonicalFunction::StreamDropWritable { ty } => {961let ty = self962.validator963.types(0)964.unwrap()965.component_defined_type_at(ty);966let func = self.core_func_signature(core_func_index)?;967core_func_index += 1;968LocalInitializer::StreamDropWritable { ty, func }969}970wasmparser::CanonicalFunction::FutureNew { ty } => {971let ty = self972.validator973.types(0)974.unwrap()975.component_defined_type_at(ty);976let func = self.core_func_signature(core_func_index)?;977core_func_index += 1;978LocalInitializer::FutureNew { ty, func }979}980wasmparser::CanonicalFunction::FutureRead { ty, options } => {981let ty = self982.validator983.types(0)984.unwrap()985.component_defined_type_at(ty);986let options = self.canonical_options(&options, core_func_index)?;987core_func_index += 1;988LocalInitializer::FutureRead { ty, options }989}990wasmparser::CanonicalFunction::FutureWrite { ty, options } => {991let ty = self992.validator993.types(0)994.unwrap()995.component_defined_type_at(ty);996let options = self.canonical_options(&options, core_func_index)?;997core_func_index += 1;998LocalInitializer::FutureWrite { ty, options }999}1000wasmparser::CanonicalFunction::FutureCancelRead { ty, async_ } => {1001let ty = self1002.validator1003.types(0)1004.unwrap()1005.component_defined_type_at(ty);1006let func = self.core_func_signature(core_func_index)?;1007core_func_index += 1;1008LocalInitializer::FutureCancelRead { ty, func, async_ }1009}1010wasmparser::CanonicalFunction::FutureCancelWrite { ty, async_ } => {1011let ty = self1012.validator1013.types(0)1014.unwrap()1015.component_defined_type_at(ty);1016let func = self.core_func_signature(core_func_index)?;1017core_func_index += 1;1018LocalInitializer::FutureCancelWrite { ty, func, async_ }1019}1020wasmparser::CanonicalFunction::FutureDropReadable { ty } => {1021let ty = self1022.validator1023.types(0)1024.unwrap()1025.component_defined_type_at(ty);1026let func = self.core_func_signature(core_func_index)?;1027core_func_index += 1;1028LocalInitializer::FutureDropReadable { ty, func }1029}1030wasmparser::CanonicalFunction::FutureDropWritable { ty } => {1031let ty = self1032.validator1033.types(0)1034.unwrap()1035.component_defined_type_at(ty);1036let func = self.core_func_signature(core_func_index)?;1037core_func_index += 1;1038LocalInitializer::FutureDropWritable { ty, func }1039}1040wasmparser::CanonicalFunction::ErrorContextNew { options } => {1041let options = self.canonical_options(&options, core_func_index)?;1042core_func_index += 1;1043LocalInitializer::ErrorContextNew { options }1044}1045wasmparser::CanonicalFunction::ErrorContextDebugMessage { options } => {1046let options = self.canonical_options(&options, core_func_index)?;1047core_func_index += 1;1048LocalInitializer::ErrorContextDebugMessage { options }1049}1050wasmparser::CanonicalFunction::ErrorContextDrop => {1051let func = self.core_func_signature(core_func_index)?;1052core_func_index += 1;1053LocalInitializer::ErrorContextDrop { func }1054}1055wasmparser::CanonicalFunction::ContextGet(i) => {1056let func = self.core_func_signature(core_func_index)?;1057core_func_index += 1;1058LocalInitializer::ContextGet { i, func }1059}1060wasmparser::CanonicalFunction::ContextSet(i) => {1061let func = self.core_func_signature(core_func_index)?;1062core_func_index += 1;1063LocalInitializer::ContextSet { i, func }1064}1065};1066self.result.initializers.push(init);1067}1068}10691070// Core wasm modules are translated inline directly here with the1071// `ModuleEnvironment` from core wasm compilation. This will return1072// to the caller the size of the module so it knows how many bytes1073// of the input are skipped.1074//1075// Note that this is just initial type translation of the core wasm1076// module and actual function compilation is deferred until this1077// entire process has completed.1078Payload::ModuleSection {1079parser,1080unchecked_range,1081} => {1082let index = self.validator.types(0).unwrap().module_count();1083self.validator.module_section(&unchecked_range)?;1084let static_module_index = self.static_modules.next_key();1085let translation = ModuleEnvironment::new(1086self.tunables,1087self.validator,1088self.types.module_types_builder(),1089static_module_index,1090)1091.translate(1092parser,1093component1094.get(unchecked_range.start..unchecked_range.end)1095.ok_or_else(|| {1096anyhow!(1097"section range {}..{} is out of bounds (bound = {})",1098unchecked_range.start,1099unchecked_range.end,1100component.len()1101)1102.context("wasm component contains an invalid module section")1103})?,1104)?;1105let static_module_index2 = self.static_modules.push(translation);1106assert_eq!(static_module_index, static_module_index2);1107let types = self.validator.types(0).unwrap();1108let ty = types.module_at(index);1109self.result1110.initializers1111.push(LocalInitializer::ModuleStatic(static_module_index, ty));1112return Ok(Action::Skip(unchecked_range.end - unchecked_range.start));1113}11141115// When a sub-component is found then the current translation state1116// is pushed onto the `lexical_scopes` stack. This will subsequently1117// get popped as part of `Payload::End` processing above.1118//1119// Note that the set of closure args for this new lexical scope1120// starts empty since it will only get populated if translation of1121// the nested component ends up aliasing some outer module or1122// component.1123Payload::ComponentSection {1124parser,1125unchecked_range,1126} => {1127self.validator.component_section(&unchecked_range)?;1128self.lexical_scopes.push(LexicalScope {1129parser: mem::replace(&mut self.parser, parser),1130translation: mem::take(&mut self.result),1131closure_args: ClosedOverVars::default(),1132});1133}11341135// Both core wasm instances and component instances record1136// initializers of what form of instantiation is performed which1137// largely just records the arguments given from wasmparser into a1138// `HashMap` for processing later during inlining.1139Payload::InstanceSection(s) => {1140self.validator.instance_section(&s)?;1141for instance in s {1142let init = match instance? {1143wasmparser::Instance::Instantiate { module_index, args } => {1144let index = ModuleIndex::from_u32(module_index);1145self.instantiate_module(index, &args)1146}1147wasmparser::Instance::FromExports(exports) => {1148self.instantiate_module_from_exports(&exports)1149}1150};1151self.result.initializers.push(init);1152}1153}1154Payload::ComponentInstanceSection(s) => {1155let mut index = self.validator.types(0).unwrap().component_instance_count();1156self.validator.component_instance_section(&s)?;1157for instance in s {1158let types = self.validator.types(0).unwrap();1159let ty = types.component_instance_at(index);1160let init = match instance? {1161wasmparser::ComponentInstance::Instantiate {1162component_index,1163args,1164} => {1165let index = ComponentIndex::from_u32(component_index);1166self.instantiate_component(index, &args, ty)?1167}1168wasmparser::ComponentInstance::FromExports(exports) => {1169self.instantiate_component_from_exports(&exports, ty)?1170}1171};1172self.result.initializers.push(init);1173index += 1;1174}1175}11761177// Exports don't actually fill out the `initializers` array but1178// instead fill out the one other field in a `Translation`, the1179// `exports` field (as one might imagine). This for now simply1180// records the index of what's exported and that's tracked further1181// later during inlining.1182Payload::ComponentExportSection(s) => {1183self.validator.component_export_section(&s)?;1184for export in s {1185let export = export?;1186let item = self.kind_to_item(export.kind, export.index)?;1187let prev = self.result.exports.insert(export.name.0, item);1188assert!(prev.is_none());1189self.result1190.initializers1191.push(LocalInitializer::Export(item));1192}1193}11941195Payload::ComponentStartSection { start, range } => {1196self.validator.component_start_section(&start, &range)?;1197unimplemented!("component start section");1198}11991200// Aliases of instance exports (either core or component) will be1201// recorded as an initializer of the appropriate type with outer1202// aliases handled specially via upvars and type processing.1203Payload::ComponentAliasSection(s) => {1204self.validator.component_alias_section(&s)?;1205for alias in s {1206let init = match alias? {1207wasmparser::ComponentAlias::InstanceExport {1208kind: _,1209instance_index,1210name,1211} => {1212let instance = ComponentInstanceIndex::from_u32(instance_index);1213LocalInitializer::AliasComponentExport(instance, name)1214}1215wasmparser::ComponentAlias::Outer { kind, count, index } => {1216self.alias_component_outer(kind, count, index);1217continue;1218}1219wasmparser::ComponentAlias::CoreInstanceExport {1220kind,1221instance_index,1222name,1223} => {1224let instance = ModuleInstanceIndex::from_u32(instance_index);1225self.alias_module_instance_export(kind, instance, name)1226}1227};1228self.result.initializers.push(init);1229}1230}12311232// All custom sections are ignored by Wasmtime at this time.1233//1234// FIXME(WebAssembly/component-model#14): probably want to specify1235// and parse a `name` section here.1236Payload::CustomSection { .. } => {}12371238// Anything else is either not reachable since we never enable the1239// feature in Wasmtime or we do enable it and it's a bug we don't1240// implement it, so let validation take care of most errors here and1241// if it gets past validation provide a helpful error message to1242// debug.1243other => {1244self.validator.payload(&other)?;1245panic!("unimplemented section {other:?}");1246}1247}12481249Ok(Action::KeepGoing)1250}12511252fn instantiate_module(1253&mut self,1254module: ModuleIndex,1255raw_args: &[wasmparser::InstantiationArg<'data>],1256) -> LocalInitializer<'data> {1257let mut args = HashMap::with_capacity(raw_args.len());1258for arg in raw_args {1259match arg.kind {1260wasmparser::InstantiationArgKind::Instance => {1261let idx = ModuleInstanceIndex::from_u32(arg.index);1262args.insert(arg.name, idx);1263}1264}1265}1266LocalInitializer::ModuleInstantiate(module, args)1267}12681269/// Creates a synthetic module from the list of items currently in the1270/// module and their given names.1271fn instantiate_module_from_exports(1272&mut self,1273exports: &[wasmparser::Export<'data>],1274) -> LocalInitializer<'data> {1275let mut map = HashMap::with_capacity(exports.len());1276for export in exports {1277let idx = match export.kind {1278wasmparser::ExternalKind::Func => {1279let index = FuncIndex::from_u32(export.index);1280EntityIndex::Function(index)1281}1282wasmparser::ExternalKind::Table => {1283let index = TableIndex::from_u32(export.index);1284EntityIndex::Table(index)1285}1286wasmparser::ExternalKind::Memory => {1287let index = MemoryIndex::from_u32(export.index);1288EntityIndex::Memory(index)1289}1290wasmparser::ExternalKind::Global => {1291let index = GlobalIndex::from_u32(export.index);1292EntityIndex::Global(index)1293}1294wasmparser::ExternalKind::Tag => {1295let index = TagIndex::from_u32(export.index);1296EntityIndex::Tag(index)1297}1298};1299map.insert(export.name, idx);1300}1301LocalInitializer::ModuleSynthetic(map)1302}13031304fn instantiate_component(1305&mut self,1306component: ComponentIndex,1307raw_args: &[wasmparser::ComponentInstantiationArg<'data>],1308ty: ComponentInstanceTypeId,1309) -> Result<LocalInitializer<'data>> {1310let mut args = HashMap::with_capacity(raw_args.len());1311for arg in raw_args {1312let idx = self.kind_to_item(arg.kind, arg.index)?;1313args.insert(arg.name, idx);1314}13151316Ok(LocalInitializer::ComponentInstantiate(component, args, ty))1317}13181319/// Creates a synthetic module from the list of items currently in the1320/// module and their given names.1321fn instantiate_component_from_exports(1322&mut self,1323exports: &[wasmparser::ComponentExport<'data>],1324ty: ComponentInstanceTypeId,1325) -> Result<LocalInitializer<'data>> {1326let mut map = HashMap::with_capacity(exports.len());1327for export in exports {1328let idx = self.kind_to_item(export.kind, export.index)?;1329map.insert(export.name.0, idx);1330}13311332Ok(LocalInitializer::ComponentSynthetic(map, ty))1333}13341335fn kind_to_item(1336&mut self,1337kind: wasmparser::ComponentExternalKind,1338index: u32,1339) -> Result<ComponentItem> {1340Ok(match kind {1341wasmparser::ComponentExternalKind::Func => {1342let index = ComponentFuncIndex::from_u32(index);1343ComponentItem::Func(index)1344}1345wasmparser::ComponentExternalKind::Module => {1346let index = ModuleIndex::from_u32(index);1347ComponentItem::Module(index)1348}1349wasmparser::ComponentExternalKind::Instance => {1350let index = ComponentInstanceIndex::from_u32(index);1351ComponentItem::ComponentInstance(index)1352}1353wasmparser::ComponentExternalKind::Component => {1354let index = ComponentIndex::from_u32(index);1355ComponentItem::Component(index)1356}1357wasmparser::ComponentExternalKind::Value => {1358unimplemented!("component values");1359}1360wasmparser::ComponentExternalKind::Type => {1361let types = self.validator.types(0).unwrap();1362let ty = types.component_any_type_at(index);1363ComponentItem::Type(ty)1364}1365})1366}13671368fn alias_module_instance_export(1369&mut self,1370kind: wasmparser::ExternalKind,1371instance: ModuleInstanceIndex,1372name: &'data str,1373) -> LocalInitializer<'data> {1374match kind {1375wasmparser::ExternalKind::Func => LocalInitializer::AliasExportFunc(instance, name),1376wasmparser::ExternalKind::Memory => LocalInitializer::AliasExportMemory(instance, name),1377wasmparser::ExternalKind::Table => LocalInitializer::AliasExportTable(instance, name),1378wasmparser::ExternalKind::Global => LocalInitializer::AliasExportGlobal(instance, name),1379wasmparser::ExternalKind::Tag => LocalInitializer::AliasExportTag(instance, name),1380}1381}13821383fn alias_component_outer(1384&mut self,1385kind: wasmparser::ComponentOuterAliasKind,1386count: u32,1387index: u32,1388) {1389match kind {1390wasmparser::ComponentOuterAliasKind::CoreType1391| wasmparser::ComponentOuterAliasKind::Type => {}13921393// For more information about the implementation of outer aliases1394// see the documentation of `LexicalScope`. Otherwise though the1395// main idea here is that the data to close over starts as `Local`1396// and then transitions to `Upvar` as its inserted into the parents1397// in order from target we're aliasing back to the current1398// component.1399wasmparser::ComponentOuterAliasKind::CoreModule => {1400let index = ModuleIndex::from_u32(index);1401let mut module = ClosedOverModule::Local(index);1402let depth = self.lexical_scopes.len() - (count as usize);1403for frame in self.lexical_scopes[depth..].iter_mut() {1404module = ClosedOverModule::Upvar(frame.closure_args.modules.push(module));1405}14061407// If the `module` is still `Local` then the `depth` was 0 and1408// it's an alias into our own space. Otherwise it's switched to1409// an upvar and will index into the upvar space. Either way1410// it's just plumbed directly into the initializer.1411self.result1412.initializers1413.push(LocalInitializer::AliasModule(module));1414}1415wasmparser::ComponentOuterAliasKind::Component => {1416let index = ComponentIndex::from_u32(index);1417let mut component = ClosedOverComponent::Local(index);1418let depth = self.lexical_scopes.len() - (count as usize);1419for frame in self.lexical_scopes[depth..].iter_mut() {1420component =1421ClosedOverComponent::Upvar(frame.closure_args.components.push(component));1422}14231424self.result1425.initializers1426.push(LocalInitializer::AliasComponent(component));1427}1428}1429}14301431fn canonical_options(1432&mut self,1433opts: &[wasmparser::CanonicalOption],1434core_func_index: u32,1435) -> WasmResult<LocalCanonicalOptions> {1436let core_type = self.core_func_signature(core_func_index)?;14371438let mut string_encoding = StringEncoding::Utf8;1439let mut post_return = None;1440let mut async_ = false;1441let mut callback = None;1442let mut memory = None;1443let mut realloc = None;1444let mut gc = false;14451446for opt in opts {1447match opt {1448wasmparser::CanonicalOption::UTF8 => {1449string_encoding = StringEncoding::Utf8;1450}1451wasmparser::CanonicalOption::UTF16 => {1452string_encoding = StringEncoding::Utf16;1453}1454wasmparser::CanonicalOption::CompactUTF16 => {1455string_encoding = StringEncoding::CompactUtf16;1456}1457wasmparser::CanonicalOption::Memory(idx) => {1458let idx = MemoryIndex::from_u32(*idx);1459memory = Some(idx);1460}1461wasmparser::CanonicalOption::Realloc(idx) => {1462let idx = FuncIndex::from_u32(*idx);1463realloc = Some(idx);1464}1465wasmparser::CanonicalOption::PostReturn(idx) => {1466let idx = FuncIndex::from_u32(*idx);1467post_return = Some(idx);1468}1469wasmparser::CanonicalOption::Async => async_ = true,1470wasmparser::CanonicalOption::Callback(idx) => {1471let idx = FuncIndex::from_u32(*idx);1472callback = Some(idx);1473}1474wasmparser::CanonicalOption::CoreType(idx) => {1475if cfg!(debug_assertions) {1476let types = self.validator.types(0).unwrap();1477let core_ty_id = types.core_type_at_in_component(*idx).unwrap_sub();1478let interned = self1479.types1480.module_types_builder()1481.intern_type(types, core_ty_id)?;1482debug_assert_eq!(interned, core_type);1483}1484}1485wasmparser::CanonicalOption::Gc => {1486gc = true;1487}1488}1489}14901491Ok(LocalCanonicalOptions {1492string_encoding,1493post_return,1494async_,1495callback,1496core_type,1497data_model: if gc {1498LocalDataModel::Gc {}1499} else {1500LocalDataModel::LinearMemory { memory, realloc }1501},1502})1503}15041505/// Get the interned type index for the `index`th core function.1506fn core_func_signature(&mut self, index: u32) -> WasmResult<ModuleInternedTypeIndex> {1507let types = self.validator.types(0).unwrap();1508let id = types.core_function_at(index);1509self.types.module_types_builder().intern_type(types, id)1510}1511}15121513impl Translation<'_> {1514fn types_ref(&self) -> wasmparser::types::TypesRef<'_> {1515self.types.as_ref().unwrap().as_ref()1516}1517}15181519/// A small helper module which wraps a `ComponentTypesBuilder` and attempts1520/// to disallow access to mutable access to the builder before the inlining1521/// pass.1522///1523/// Type information in this translation pass must be preserved at the1524/// wasmparser layer of abstraction rather than being lowered into Wasmtime's1525/// own type system. Only during inlining are types fully assigned because1526/// that's when resource types become available as it's known which instance1527/// defines which resource, or more concretely the same component instantiated1528/// twice will produce two unique resource types unlike one as seen by1529/// wasmparser within the component.1530mod pre_inlining {1531use super::*;15321533pub struct PreInliningComponentTypes<'a> {1534types: &'a mut ComponentTypesBuilder,1535}15361537impl<'a> PreInliningComponentTypes<'a> {1538pub fn new(types: &'a mut ComponentTypesBuilder) -> Self {1539Self { types }1540}15411542pub fn module_types_builder(&mut self) -> &mut ModuleTypesBuilder {1543self.types.module_types_builder_mut()1544}15451546pub fn types(&self) -> &ComponentTypesBuilder {1547self.types1548}15491550// NB: this should in theory only be used for the `inline` phase of1551// translation.1552pub fn types_mut_for_inlining(&mut self) -> &mut ComponentTypesBuilder {1553self.types1554}1555}15561557impl TypeConvert for PreInliningComponentTypes<'_> {1558fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType {1559self.types.lookup_heap_type(index)1560}15611562fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {1563self.types.lookup_type_index(index)1564}1565}1566}1567use pre_inlining::PreInliningComponentTypes;156815691570