Files
wasmtime/crates/environ/src/component/translate.rs
Alex Crichton 29c7de7340 Update wasm-tools dependencies (#4970)
* Update wasm-tools dependencies

This update brings in a number of features such as:

* The component model binary format and AST has been slightly adjusted
  in a few locations. Names are dropped from parameters/results now in
  the internal representation since they were not used anyway. At this
  time the ability to bind a multi-return function has not been exposed.

* The `wasmparser` validator pass will now share allocations with prior
  functions, providing what's probably a very minor speedup for Wasmtime
  itself.

* The text format for many component-related tests now requires named
  parameters.

* Some new relaxed-simd instructions are updated to be ignored.

I hope to have a follow-up to expose the multi-return ability to the
embedding API of components.

* Update audit information for new crates
2022-09-27 13:12:34 -05:00

1026 lines
43 KiB
Rust

use crate::component::*;
use crate::ScopeVec;
use crate::{
EntityIndex, ModuleEnvironment, ModuleTranslation, PrimaryMap, SignatureIndex, Tunables,
};
use anyhow::{bail, Result};
use indexmap::IndexMap;
use std::collections::HashMap;
use std::mem;
use wasmparser::{Chunk, Encoding, Parser, Payload, Validator};
mod adapt;
pub use self::adapt::*;
mod inline;
/// Structure used to translate a component and parse it.
pub struct Translator<'a, 'data> {
/// The current component being translated.
///
/// This will get swapped out as translation traverses the body of a
/// component and a sub-component is entered or left.
result: Translation<'data>,
/// Current state of parsing a binary component. Note that like `result`
/// this will change as the component is traversed.
parser: Parser,
/// Stack of lexical scopes that are in-progress but not finished yet.
///
/// This is pushed to whenever a component is entered and popped from
/// whenever a component is left. Each lexical scope also contains
/// information about the variables that it is currently required to close
/// over which is threaded into the current in-progress translation of
/// the sub-component which pushed a scope here.
lexical_scopes: Vec<LexicalScope<'data>>,
/// The validator in use to verify that the raw input binary is a valid
/// component.
validator: &'a mut Validator,
/// Type information shared for the entire component.
///
/// This builder is also used for all core wasm modules found to intern
/// signatures across all modules.
types: &'a mut ComponentTypesBuilder,
/// The compiler configuration provided by the embedder.
tunables: &'a Tunables,
/// Auxiliary location to push generated adapter modules onto.
scope_vec: &'data ScopeVec<u8>,
/// Completely translated core wasm modules that have been found so far.
///
/// Note that this translation only involves learning about type
/// information and functions are not actually compiled here.
static_modules: PrimaryMap<StaticModuleIndex, ModuleTranslation<'data>>,
/// Completely translated components that have been found so far.
///
/// As frames are popped from `lexical_scopes` their completed component
/// will be pushed onto this list.
static_components: PrimaryMap<StaticComponentIndex, Translation<'data>>,
/// Storage for type information used by `ComponentInstanceType::Synthetic`
/// which is thrown away after compilation is finished.
synthetic_instance_types:
PrimaryMap<SyntheticInstanceTypeIndex, HashMap<&'data str, ComponentItemType>>,
}
/// Representation of the syntactic scope of a component meaning where it is
/// and what its state is at in the binary format.
///
/// These scopes are pushed and popped when a sub-component starts being
/// parsed and finishes being parsed. The main purpose of this frame is to
/// have a `ClosedOverVars` field which encapsulates data that is inherited
/// from the scope specified into the component being translated just beneath
/// it.
///
/// This structure exists to implement outer aliases to components and modules.
/// When a component or module is closed over then that means it needs to be
/// inherited in a sense to the component which actually had the alias. This is
/// achieved with a deceptively simple scheme where each parent of the
/// component with the alias will inherit the component from the desired
/// location.
///
/// For example with a component structure that looks like:
///
/// ```wasm
/// (component $A
/// (core module $M)
/// (component $B
/// (component $C
/// (alias outer $A $M (core module))
/// )
/// )
/// )
/// ```
///
/// here the `C` component is closing over `M` located in the root component
/// `A`. When `C` is being translated the `lexical_scopes` field will look like
/// `[A, B]`. When the alias is encountered (for module index 0) this will
/// place a `ClosedOverModule::Local(0)` entry into the `closure_args` field of
/// `A`'s frame. This will in turn give a `ModuleUpvarIndex` which is then
/// inserted into `closure_args` in `B`'s frame. This produces yet another
/// `ModuleUpvarIndex` which is finally inserted into `C`'s module index space
/// via `LocalInitializer::AliasModuleUpvar` with the last index.
///
/// All of these upvar indices and such are interpreted in the "inline" phase
/// of compilation and not at runtime. This means that when `A` is being
/// instantiated one of its initializers will be
/// `LocalInitializer::ComponentStatic`. This starts to create `B` and the
/// variables captured for `B` are listed as local module 0, or `M`. This list
/// is then preserved in the definition of the component `B` and later reused
/// by `C` again to finally get access to the closed over component.
///
/// Effectively the scopes are managed hierarchically where a reference to an
/// outer variable automatically injects references into all parents up to
/// where the reference is. This variable scopes are the processed during
/// inlining where a component definition is a reference to the static
/// component information (`Translation`) plus closed over variables
/// (`ComponentClosure` during inlining).
struct LexicalScope<'data> {
/// Current state of translating the `translation` below.
parser: Parser,
/// Current state of the component's translation as found so far.
translation: Translation<'data>,
/// List of captures that `translation` will need to process to create the
/// sub-component which is directly beneath this lexical scope.
closure_args: ClosedOverVars,
}
/// A "local" translation of a component.
///
/// This structure is used as a sort of in-progress translation of a component.
/// This is not `Component` which is the final form as consumed by Wasmtime
/// at runtime. Instead this is a fairly simple representation of a component
/// where almost everything is ordered as a list of initializers. The binary
/// format is translated to a list of initializers here which is later processed
/// during "inlining" to produce a final component with the final set of
/// initializers.
#[derive(Default)]
struct Translation<'data> {
/// Instructions which form this component.
///
/// There is one initializer for all members of each index space, and all
/// index spaces are incrementally built here as the initializer list is
/// processed.
initializers: Vec<LocalInitializer<'data>>,
/// The list of exports from this component, as pairs of names and an
/// index into an index space of what's being exported.
exports: IndexMap<&'data str, ComponentItem>,
/// Type information (in Wasmtime's representation) for various
/// component-model index spaces.
funcs: PrimaryMap<FuncIndex, SignatureIndex>,
components: PrimaryMap<ComponentIndex, ComponentType>,
component_funcs: PrimaryMap<ComponentFuncIndex, TypeFuncIndex>,
component_instances: PrimaryMap<ComponentInstanceIndex, ComponentInstanceType>,
}
#[allow(missing_docs)]
enum LocalInitializer<'data> {
// imports
Import(&'data str, TypeDef),
// canonical function sections
Lower(ComponentFuncIndex, LocalCanonicalOptions),
Lift(TypeFuncIndex, FuncIndex, LocalCanonicalOptions),
// core wasm modules
ModuleStatic(StaticModuleIndex),
// core wasm module instances
ModuleInstantiate(ModuleIndex, HashMap<&'data str, ModuleInstanceIndex>),
ModuleSynthetic(HashMap<&'data str, EntityIndex>),
// components
ComponentStatic(StaticComponentIndex, ClosedOverVars),
// component instances
ComponentInstantiate(ComponentIndex, HashMap<&'data str, ComponentItem>),
ComponentSynthetic(HashMap<&'data str, ComponentItem>),
// alias section
AliasExportFunc(ModuleInstanceIndex, &'data str),
AliasExportTable(ModuleInstanceIndex, &'data str),
AliasExportGlobal(ModuleInstanceIndex, &'data str),
AliasExportMemory(ModuleInstanceIndex, &'data str),
AliasComponentExport(ComponentInstanceIndex, &'data str),
AliasModule(ClosedOverModule),
AliasComponent(ClosedOverComponent),
}
/// The "closure environment" of components themselves.
///
/// For more information see `LexicalScope`.
#[derive(Default)]
struct ClosedOverVars {
components: PrimaryMap<ComponentUpvarIndex, ClosedOverComponent>,
modules: PrimaryMap<ModuleUpvarIndex, ClosedOverModule>,
}
/// Description how a component is closed over when the closure variables for
/// a component are being created.
///
/// For more information see `LexicalScope`.
enum ClosedOverComponent {
/// A closed over component is coming from the local component's index
/// space, meaning a previously defined component is being captured.
Local(ComponentIndex),
/// A closed over component is coming from our own component's list of
/// upvars. This list was passed to us by our enclosing component, which
/// will eventually have bottomed out in closing over a `Local` component
/// index for some parent component.
Upvar(ComponentUpvarIndex),
}
/// Same as `ClosedOverComponent`, but for modules.
enum ClosedOverModule {
Local(ModuleIndex),
Upvar(ModuleUpvarIndex),
}
/// Representation of canonical ABI options.
struct LocalCanonicalOptions {
string_encoding: StringEncoding,
memory: Option<MemoryIndex>,
realloc: Option<FuncIndex>,
post_return: Option<FuncIndex>,
}
#[derive(Copy, Clone)]
enum ComponentType {
/// A component was imported from so its type is listed by an index.
Index(TypeComponentIndex),
/// A component was defined statically inline so its type information can be
/// found in the `Translation` itself.
Static(StaticComponentIndex),
}
#[derive(Copy, Clone)]
enum ComponentInstanceType {
/// An instance was imported so its type information is listed by index.
Index(TypeComponentInstanceIndex),
/// An instance was created through instantiating an imported component, so
/// the type of the index is inferred from the type of the component.
InstantiatedIndex(TypeComponentIndex),
/// An instance was created through instantiation of a statically defined
/// component, so the type information is inferred from a `Translation`.
InstantiatedStatic(StaticComponentIndex),
/// An instance was created from a list of other items, and the list can be
/// found in the `synthetic_instance_types` map.
Synthetic(SyntheticInstanceTypeIndex),
}
/// Only used as part of `synthetic_instance_types` which is used to infer type
/// information for component-model items. This intentionally doesn't cover
/// everything (e.g. modules are missing at this time since they aren't needed).
#[derive(Copy, Clone)]
enum ComponentItemType {
Func(TypeFuncIndex),
Component(ComponentType),
Instance(ComponentInstanceType),
}
#[derive(Copy, Clone, PartialEq, Eq)]
struct SyntheticInstanceTypeIndex(u32);
cranelift_entity::entity_impl!(SyntheticInstanceTypeIndex);
enum Action {
KeepGoing,
Skip(usize),
Done,
}
impl<'a, 'data> Translator<'a, 'data> {
/// Creates a new translation state ready to translate a component.
pub fn new(
tunables: &'a Tunables,
validator: &'a mut Validator,
types: &'a mut ComponentTypesBuilder,
scope_vec: &'data ScopeVec<u8>,
) -> Self {
Self {
result: Translation::default(),
tunables,
validator,
types,
parser: Parser::new(0),
lexical_scopes: Vec::new(),
static_components: Default::default(),
static_modules: Default::default(),
synthetic_instance_types: Default::default(),
scope_vec,
}
}
/// Translates the binary `component`.
///
/// This is the workhorse of compilation which will parse all of
/// `component` and create type information for Wasmtime and such. The
/// `component` does not have to be valid and it will be validated during
/// compilation.
///
/// THe result of this function is a tuple of the final component's
/// description plus a list of core wasm modules found within the
/// component. The component's description actually erases internal
/// components, instances, etc, as much as it can. Instead `Component`
/// retains a flat list of initializers (no nesting) which was created
/// as part of compilation from the nested structure of the original
/// component.
///
/// The list of core wasm modules found is provided to allow compiling
/// modules externally in parallel. Additionally initializers in
/// `Component` may refer to the modules in the map returned by index.
///
/// # Errors
///
/// This function will return an error if the `component` provided is
/// invalid.
pub fn translate(
mut self,
component: &'data [u8],
) -> Result<(
Component,
PrimaryMap<StaticModuleIndex, ModuleTranslation<'data>>,
)> {
// First up wasmparser is used to actually perform the translation and
// validation of this component. This will produce a list of core wasm
// modules in addition to components which are found during the
// translation process. When doing this only a `Translation` is created
// which is a simple representation of a component.
let mut remaining = component;
loop {
let payload = match self.parser.parse(remaining, true)? {
Chunk::Parsed { payload, consumed } => {
remaining = &remaining[consumed..];
payload
}
Chunk::NeedMoreData(_) => unreachable!(),
};
match self.translate_payload(payload, component)? {
Action::KeepGoing => {}
Action::Skip(n) => remaining = &remaining[n..],
Action::Done => break,
}
}
assert!(remaining.is_empty());
assert!(self.lexical_scopes.is_empty());
// ... after translation initially finishes the next pass is performed
// which we're calling "inlining". This will "instantiate" the root
// component, following nested component instantiations, creating a
// global list of initializers along the way. This phase uses the simple
// initializers in each component to track dataflow of host imports and
// internal references to items throughout a component at compile-time.
// The produce initializers in the final `Component` are intended to be
// much simpler than the original component and more efficient for
// Wasmtime to process at runtime as well (e.g. no string lookups as
// most everything is done through indices instead).
let mut component = inline::run(
&self.types,
&self.result,
&self.static_modules,
&self.static_components,
)?;
self.partition_adapter_modules(&mut component);
Ok((component.finish(), self.static_modules))
}
fn translate_payload(
&mut self,
payload: Payload<'data>,
component: &'data [u8],
) -> Result<Action> {
match payload {
Payload::Version {
num,
encoding,
range,
} => {
self.validator.version(num, encoding, &range)?;
match encoding {
Encoding::Component => {}
Encoding::Module => {
bail!("attempted to parse a wasm module with a component parser");
}
}
// Push a new scope for component types so outer aliases know
// that the 0th level is this new component.
self.types.push_type_scope();
}
Payload::End(offset) => {
let types = self.validator.end(offset)?;
// Record type information for this component now that we'll
// have it from wasmparser.
//
// Note that this uses type information from `wasmparser` to
// lookup signature of all core wasm functions in this
// component. This avoids us having to reimplement the
// translate-interface-types-to-the-canonical-abi logic. The
// type of the function is then intern'd to get a
// `SignatureIndex` which is later used at runtime for a
// `VMSharedSignatureIndex`.
for idx in 0.. {
let lowered_function_type = match types.function_at(idx) {
Some(ty) => ty,
None => break,
};
let ty = self
.types
.module_types_builder()
.wasm_func_type(lowered_function_type.clone().try_into()?);
self.result.funcs.push(ty);
}
// When leaving a module be sure to pop the types scope to
// ensure that when we go back to the previous module outer
// type alias indices work correctly again.
self.types.pop_type_scope();
// Exit the current lexical scope. If there is no parent (no
// frame currently on the stack) then translation is finished.
// Otherwise that means that a nested component has been
// completed and is recorded as such.
let LexicalScope {
parser,
translation,
closure_args,
} = match self.lexical_scopes.pop() {
Some(frame) => frame,
None => return Ok(Action::Done),
};
self.parser = parser;
let component = mem::replace(&mut self.result, translation);
let static_idx = self.static_components.push(component);
self.result
.initializers
.push(LocalInitializer::ComponentStatic(static_idx, closure_args));
self.result
.components
.push(ComponentType::Static(static_idx));
}
// When we see a type section the types are validated and then
// translated into Wasmtime's representation. Each active type
// definition is recorded in the `ComponentTypesBuilder` tables, or
// this component's active scope.
//
// Note that the push/pop of the component types scope happens above
// in `Version` and `End` since multiple type sections can appear
// within a component.
Payload::ComponentTypeSection(s) => {
self.validator.component_type_section(&s)?;
for ty in s {
let ty = self.types.intern_component_type(&ty?)?;
self.types.push_component_typedef(ty);
}
}
Payload::CoreTypeSection(s) => {
self.validator.core_type_section(&s)?;
for ty in s {
let ty = self.types.intern_core_type(&ty?)?;
self.types.push_core_typedef(ty);
}
}
// Processing the import section at this point is relatively simple
// which is to simply record the name of the import and the type
// information associated with it.
Payload::ComponentImportSection(s) => {
self.validator.component_import_section(&s)?;
for import in s {
let import = import?;
let ty = self.types.component_type_ref(&import.ty);
self.result.push_typedef(ty);
self.result
.initializers
.push(LocalInitializer::Import(import.name, ty));
}
}
// Entries in the canonical section will get initializers recorded
// with the listed options for lifting/lowering.
Payload::ComponentCanonicalSection(s) => {
self.validator.component_canonical_section(&s)?;
for func in s {
match func? {
wasmparser::CanonicalFunction::Lift {
type_index,
core_func_index,
options,
} => {
let ty = ComponentTypeIndex::from_u32(type_index);
let ty = match self.types.component_outer_type(0, ty) {
TypeDef::ComponentFunc(ty) => ty,
// should not be possible after validation
_ => unreachable!(),
};
let func = FuncIndex::from_u32(core_func_index);
let options = self.canonical_options(&options);
self.result
.initializers
.push(LocalInitializer::Lift(ty, func, options));
self.result.component_funcs.push(ty);
}
wasmparser::CanonicalFunction::Lower {
func_index,
options,
} => {
let func = ComponentFuncIndex::from_u32(func_index);
let options = self.canonical_options(&options);
self.result
.initializers
.push(LocalInitializer::Lower(func, options));
}
}
}
}
// Core wasm modules are translated inline directly here with the
// `ModuleEnvironment` from core wasm compilation. This will return
// to the caller the size of the module so it knows how many bytes
// of the input are skipped.
//
// Note that this is just initial type translation of the core wasm
// module and actual function compilation is deferred until this
// entire process has completed.
Payload::ModuleSection { parser, range } => {
self.validator.module_section(&range)?;
let translation = ModuleEnvironment::new(
self.tunables,
self.validator,
self.types.module_types_builder(),
)
.translate(parser, &component[range.start..range.end])?;
let static_idx = self.static_modules.push(translation);
self.result
.initializers
.push(LocalInitializer::ModuleStatic(static_idx));
return Ok(Action::Skip(range.end - range.start));
}
// When a sub-component is found then the current translation state
// is pushed onto the `lexical_scopes` stack. This will subsequently
// get popped as part of `Payload::End` processing above.
//
// Note that the set of closure args for this new lexical scope
// starts empty since it will only get populated if translation of
// the nested component ends up aliasing some outer module or
// component.
Payload::ComponentSection { parser, range } => {
self.validator.component_section(&range)?;
self.lexical_scopes.push(LexicalScope {
parser: mem::replace(&mut self.parser, parser),
translation: mem::take(&mut self.result),
closure_args: ClosedOverVars::default(),
});
}
// Both core wasm instances and component instances record
// initializers of what form of instantiation is performed which
// largely just records the arguments given from wasmparser into a
// `HashMap` for processing later during inlining.
Payload::InstanceSection(s) => {
self.validator.instance_section(&s)?;
for instance in s {
let init = match instance? {
wasmparser::Instance::Instantiate { module_index, args } => {
let index = ModuleIndex::from_u32(module_index);
self.instantiate_module(index, &args)
}
wasmparser::Instance::FromExports(exports) => {
self.instantiate_module_from_exports(&exports)
}
};
self.result.initializers.push(init);
}
}
Payload::ComponentInstanceSection(s) => {
self.validator.component_instance_section(&s)?;
for instance in s {
let init = match instance? {
wasmparser::ComponentInstance::Instantiate {
component_index,
args,
} => {
let index = ComponentIndex::from_u32(component_index);
self.instantiate_component(index, &args)
}
wasmparser::ComponentInstance::FromExports(exports) => {
self.instantiate_component_from_exports(&exports)
}
};
self.result.initializers.push(init);
}
}
// Exports don't actually fill out the `initializers` array but
// instead fill out the one other field in a `Translation`, the
// `exports` field (as one might imagine). This for now simply
// records the index of what's exported and that's tracked further
// later during inlining.
Payload::ComponentExportSection(s) => {
self.validator.component_export_section(&s)?;
for export in s {
let export = export?;
let item = self.kind_to_item(export.kind, export.index);
let prev = self.result.exports.insert(export.name, item);
assert!(prev.is_none());
}
}
Payload::ComponentStartSection(s) => {
self.validator.component_start_section(&s)?;
unimplemented!("component start section");
}
// Aliases of instance exports (either core or component) will be
// recorded as an initializer of the appropriate type with outer
// aliases handled specially via upvars and type processing.
Payload::ComponentAliasSection(s) => {
self.validator.component_alias_section(&s)?;
for alias in s {
let init = match alias? {
wasmparser::ComponentAlias::InstanceExport {
kind,
instance_index,
name,
} => {
let instance = ComponentInstanceIndex::from_u32(instance_index);
drop(kind);
self.alias_component_instance_export(instance, name);
LocalInitializer::AliasComponentExport(instance, name)
}
wasmparser::ComponentAlias::Outer { kind, count, index } => {
self.alias_component_outer(kind, count, index);
continue;
}
wasmparser::ComponentAlias::CoreInstanceExport {
kind,
instance_index,
name,
} => {
let instance = ModuleInstanceIndex::from_u32(instance_index);
self.alias_module_instance_export(kind, instance, name)
}
};
self.result.initializers.push(init);
}
}
// All custom sections are ignored by Wasmtime at this time.
//
// FIXME(WebAssembly/component-model#14): probably want to specify
// and parse a `name` section here.
Payload::CustomSection { .. } => {}
// Anything else is either not reachable since we never enable the
// feature in Wasmtime or we do enable it and it's a bug we don't
// implement it, so let validation take care of most errors here and
// if it gets past validation provide a helpful error message to
// debug.
other => {
self.validator.payload(&other)?;
panic!("unimplemented section {other:?}");
}
}
Ok(Action::KeepGoing)
}
fn instantiate_module(
&mut self,
module: ModuleIndex,
raw_args: &[wasmparser::InstantiationArg<'data>],
) -> LocalInitializer<'data> {
let mut args = HashMap::with_capacity(raw_args.len());
for arg in raw_args {
match arg.kind {
wasmparser::InstantiationArgKind::Instance => {
let idx = ModuleInstanceIndex::from_u32(arg.index);
args.insert(arg.name, idx);
}
}
}
LocalInitializer::ModuleInstantiate(module, args)
}
/// Creates a synthetic module from the list of items currently in the
/// module and their given names.
fn instantiate_module_from_exports(
&mut self,
exports: &[wasmparser::Export<'data>],
) -> LocalInitializer<'data> {
let mut map = HashMap::with_capacity(exports.len());
for export in exports {
let idx = match export.kind {
wasmparser::ExternalKind::Func => {
let index = FuncIndex::from_u32(export.index);
EntityIndex::Function(index)
}
wasmparser::ExternalKind::Table => {
let index = TableIndex::from_u32(export.index);
EntityIndex::Table(index)
}
wasmparser::ExternalKind::Memory => {
let index = MemoryIndex::from_u32(export.index);
EntityIndex::Memory(index)
}
wasmparser::ExternalKind::Global => {
let index = GlobalIndex::from_u32(export.index);
EntityIndex::Global(index)
}
// doesn't get past validation
wasmparser::ExternalKind::Tag => unimplemented!("wasm exceptions"),
};
map.insert(export.name, idx);
}
LocalInitializer::ModuleSynthetic(map)
}
fn instantiate_component(
&mut self,
component: ComponentIndex,
raw_args: &[wasmparser::ComponentInstantiationArg<'data>],
) -> LocalInitializer<'data> {
let mut args = HashMap::with_capacity(raw_args.len());
for arg in raw_args {
let idx = self.kind_to_item(arg.kind, arg.index);
args.insert(arg.name, idx);
}
self.result
.component_instances
.push(match self.result.components[component] {
ComponentType::Index(i) => ComponentInstanceType::InstantiatedIndex(i),
ComponentType::Static(i) => ComponentInstanceType::InstantiatedStatic(i),
});
LocalInitializer::ComponentInstantiate(component, args)
}
/// Creates a synthetic module from the list of items currently in the
/// module and their given names.
fn instantiate_component_from_exports(
&mut self,
exports: &[wasmparser::ComponentExport<'data>],
) -> LocalInitializer<'data> {
let mut map = HashMap::with_capacity(exports.len());
let mut types = HashMap::with_capacity(exports.len());
for export in exports {
let idx = self.kind_to_item(export.kind, export.index);
let ty = match idx {
ComponentItem::Func(i) => {
Some(ComponentItemType::Func(self.result.component_funcs[i]))
}
ComponentItem::Component(i) => {
Some(ComponentItemType::Component(self.result.components[i]))
}
ComponentItem::ComponentInstance(i) => Some(ComponentItemType::Instance(
self.result.component_instances[i],
)),
ComponentItem::Module(_) | ComponentItem::Type(_) => None,
};
map.insert(export.name, idx);
if let Some(ty) = ty {
types.insert(export.name, ty);
}
}
let index = self.synthetic_instance_types.push(types);
self.result
.component_instances
.push(ComponentInstanceType::Synthetic(index));
LocalInitializer::ComponentSynthetic(map)
}
fn kind_to_item(&self, kind: wasmparser::ComponentExternalKind, index: u32) -> ComponentItem {
match kind {
wasmparser::ComponentExternalKind::Func => {
let index = ComponentFuncIndex::from_u32(index);
ComponentItem::Func(index)
}
wasmparser::ComponentExternalKind::Module => {
let index = ModuleIndex::from_u32(index);
ComponentItem::Module(index)
}
wasmparser::ComponentExternalKind::Instance => {
let index = ComponentInstanceIndex::from_u32(index);
ComponentItem::ComponentInstance(index)
}
wasmparser::ComponentExternalKind::Component => {
let index = ComponentIndex::from_u32(index);
ComponentItem::Component(index)
}
wasmparser::ComponentExternalKind::Value => {
unimplemented!("component values");
}
wasmparser::ComponentExternalKind::Type => {
let index = ComponentTypeIndex::from_u32(index);
let ty = self.types.component_outer_type(0, index);
ComponentItem::Type(ty)
}
}
}
fn alias_module_instance_export(
&mut self,
kind: wasmparser::ExternalKind,
instance: ModuleInstanceIndex,
name: &'data str,
) -> LocalInitializer<'data> {
match kind {
wasmparser::ExternalKind::Func => LocalInitializer::AliasExportFunc(instance, name),
wasmparser::ExternalKind::Memory => LocalInitializer::AliasExportMemory(instance, name),
wasmparser::ExternalKind::Table => LocalInitializer::AliasExportTable(instance, name),
wasmparser::ExternalKind::Global => LocalInitializer::AliasExportGlobal(instance, name),
wasmparser::ExternalKind::Tag => {
unimplemented!("wasm exceptions");
}
}
}
fn alias_component_instance_export(
&mut self,
instance: ComponentInstanceIndex,
name: &'data str,
) {
match self.result.component_instances[instance] {
// An imported component instance is being aliased, so the type of
// the aliased item is directly available from the instance type.
ComponentInstanceType::Index(ty) => {
self.result.push_typedef(self.types[ty].exports[name])
}
// An imported component was instantiated so the type of the aliased
// export is available through the type of the export on the
// original component.
ComponentInstanceType::InstantiatedIndex(ty) => {
self.result.push_typedef(self.types[ty].exports[name])
}
// A static nested component was instantiated which means that the
// type of the export can be looked up directly from the translation
// that was finished prior.
ComponentInstanceType::InstantiatedStatic(idx) => {
let translation = &self.static_components[idx];
match translation.exports[name] {
ComponentItem::Func(idx) => {
self.result
.component_funcs
.push(translation.component_funcs[idx]);
}
ComponentItem::Component(idx) => {
self.result.components.push(translation.components[idx]);
}
ComponentItem::ComponentInstance(idx) => {
self.result
.component_instances
.push(translation.component_instances[idx]);
}
ComponentItem::Type(ty) => {
self.types.push_component_typedef(ty);
}
// ignored during this type pass
ComponentItem::Module(_) => {}
}
}
// A synthetic instance is being aliased which means the global list
// of synthetic instance types can be consulted for the type
// information.
ComponentInstanceType::Synthetic(index) => {
let map = &self.synthetic_instance_types[index];
match map[name] {
ComponentItemType::Func(ty) => {
self.result.component_funcs.push(ty);
}
ComponentItemType::Component(ty) => {
self.result.components.push(ty);
}
ComponentItemType::Instance(ty) => {
self.result.component_instances.push(ty);
}
}
}
}
}
fn alias_component_outer(
&mut self,
kind: wasmparser::ComponentOuterAliasKind,
count: u32,
index: u32,
) {
match kind {
// When aliasing a type the `ComponentTypesBuilder` is used to
// resolve the outer `count` plus the index, and then once it's
// resolved we push the type information into our local index
// space.
//
// Note that this is just copying indices around as all type
// information is basically a pointer back into the `TypesBuilder`
// structure (and the eventual `TypeTables` that it produces).
wasmparser::ComponentOuterAliasKind::CoreType => {
let index = TypeIndex::from_u32(index);
let ty = self.types.core_outer_type(count, index);
self.types.push_core_typedef(ty);
}
wasmparser::ComponentOuterAliasKind::Type => {
let index = ComponentTypeIndex::from_u32(index);
let ty = self.types.component_outer_type(count, index);
self.types.push_component_typedef(ty);
}
// For more information about the implementation of outer aliases
// see the documentation of `LexicalScope`. Otherwise though the
// main idea here is that the data to close over starts as `Local`
// and then transitions to `Upvar` as its inserted into the parents
// in order from target we're aliasing back to the current
// component.
wasmparser::ComponentOuterAliasKind::CoreModule => {
let index = ModuleIndex::from_u32(index);
let mut module = ClosedOverModule::Local(index);
let depth = self.lexical_scopes.len() - (count as usize);
for frame in self.lexical_scopes[depth..].iter_mut() {
module = ClosedOverModule::Upvar(frame.closure_args.modules.push(module));
}
// If the `module` is still `Local` then the `depth` was 0 and
// it's an alias into our own space. Otherwise it's switched to
// an upvar and will index into the upvar space. Either way
// it's just plumbed directly into the initializer.
self.result
.initializers
.push(LocalInitializer::AliasModule(module));
}
wasmparser::ComponentOuterAliasKind::Component => {
let index = ComponentIndex::from_u32(index);
let mut component = ClosedOverComponent::Local(index);
let depth = self.lexical_scopes.len() - (count as usize);
for frame in self.lexical_scopes[depth..].iter_mut() {
component =
ClosedOverComponent::Upvar(frame.closure_args.components.push(component));
}
let component_ty = match self.lexical_scopes.get(depth) {
Some(frame) => frame.translation.components[index],
None => self.result.components[index],
};
self.result
.initializers
.push(LocalInitializer::AliasComponent(component));
self.result.components.push(component_ty);
}
}
}
fn canonical_options(&mut self, opts: &[wasmparser::CanonicalOption]) -> LocalCanonicalOptions {
let mut ret = LocalCanonicalOptions {
string_encoding: StringEncoding::Utf8,
memory: None,
realloc: None,
post_return: None,
};
for opt in opts {
match opt {
wasmparser::CanonicalOption::UTF8 => {
ret.string_encoding = StringEncoding::Utf8;
}
wasmparser::CanonicalOption::UTF16 => {
ret.string_encoding = StringEncoding::Utf16;
}
wasmparser::CanonicalOption::CompactUTF16 => {
ret.string_encoding = StringEncoding::CompactUtf16;
}
wasmparser::CanonicalOption::Memory(idx) => {
let idx = MemoryIndex::from_u32(*idx);
ret.memory = Some(idx);
}
wasmparser::CanonicalOption::Realloc(idx) => {
let idx = FuncIndex::from_u32(*idx);
ret.realloc = Some(idx);
}
wasmparser::CanonicalOption::PostReturn(idx) => {
let idx = FuncIndex::from_u32(*idx);
ret.post_return = Some(idx);
}
}
}
return ret;
}
}
impl Translation<'_> {
fn push_typedef(&mut self, ty: TypeDef) {
match ty {
TypeDef::ComponentInstance(idx) => {
self.component_instances
.push(ComponentInstanceType::Index(idx));
}
TypeDef::ComponentFunc(idx) => {
self.component_funcs.push(idx);
}
TypeDef::Component(idx) => {
self.components.push(ComponentType::Index(idx));
}
// not processed here
TypeDef::Interface(_) | TypeDef::CoreFunc(_) | TypeDef::Module(_) => {}
}
}
}