Add dataflow processing to component translation for imports (#4205)
This commit enhances the processing of components to track all the dataflow for the processing of `canon.lower`'d functions. At the same time this fills out a few other missing details to component processing such as aliasing from some kinds of component instances and similar. The major changes contained within this are the updates the `info` submodule which has the AST of component type information. This has been significantly refactored to prepare for representing lowered functions and implementing those. The major change is from an `Instantiation` list to an `Initializer` list which abstractly represents a few other initialization actions. This work is split off from my main work to implement component imports of host functions. This is incomplete in the sense that it doesn't actually finish everything necessary to define host functions and import them into components. Instead this is only the changes necessary at the translation layer (so far). Consequently this commit does not have tests and also namely doesn't actually include the `VMComponentContext` initialization and usage. The full body of work is still a bit too messy to PR just yet so I'm hoping that this is a slimmed-down-enough piece to adequately be reviewed.
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
// everything except imported core wasm modules.
|
||||
|
||||
use crate::component::*;
|
||||
use crate::{EntityIndex, PrimaryMap};
|
||||
use crate::{EntityIndex, PrimaryMap, SignatureIndex};
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -35,46 +35,228 @@ use serde::{Deserialize, Serialize};
|
||||
/// this is going to undergo a lot of churn.
|
||||
#[derive(Default, Debug, Serialize, Deserialize)]
|
||||
pub struct Component {
|
||||
/// A list of typed values that this component imports, indexed by either
|
||||
/// the import's position or the name of the import.
|
||||
pub imports: IndexMap<String, TypeDef>,
|
||||
/// A list of typed values that this component imports.
|
||||
///
|
||||
/// Note that each name is given an `ImportIndex` here for the next map to
|
||||
/// refer back to.
|
||||
pub import_types: PrimaryMap<ImportIndex, (String, TypeDef)>,
|
||||
|
||||
/// A list of "flattened" imports that are used by this instance.
|
||||
///
|
||||
/// This import map represents extracting imports, as necessary, from the
|
||||
/// general imported types by this component. The flattening here refers to
|
||||
/// extracting items from instances. Currently the flat imports are either a
|
||||
/// host function or a core wasm module.
|
||||
///
|
||||
/// For example if `ImportIndex(0)` pointed to an instance then this import
|
||||
/// map represent extracting names from that map, for example extracting an
|
||||
/// exported module or an exported function.
|
||||
///
|
||||
/// Each import item is keyed by a `RuntimeImportIndex` which is referred to
|
||||
/// by types below whenever something refers to an import. The value for
|
||||
/// each `RuntimeImportIndex` in this map is the `ImportIndex` for where
|
||||
/// this items comes from (which can be associated with a name above in the
|
||||
/// `import_types` array) as well as the list of export names if
|
||||
/// `ImportIndex` refers to an instance. The export names array represents
|
||||
/// recursively fetching names within an instance.
|
||||
//
|
||||
// TODO: this is probably a lot of `String` storage and may be something
|
||||
// that needs optimization in the future. For example instead of lots of
|
||||
// different `String` allocations this could instead be a pointer/length
|
||||
// into one large string allocation for the entire component. Alternatively
|
||||
// strings could otherwise be globally intern'd via some other mechanism to
|
||||
// avoid `Linker`-specific intern-ing plus intern-ing here. Unsure what the
|
||||
// best route is or whether such an optimization is even necessary here.
|
||||
pub imports: PrimaryMap<RuntimeImportIndex, (ImportIndex, Vec<String>)>,
|
||||
|
||||
/// A list of this component's exports, indexed by either position or name.
|
||||
pub exports: IndexMap<String, Export>,
|
||||
|
||||
/// The list of instances that this component creates during instantiation.
|
||||
/// Initializers that must be processed when instantiating this component.
|
||||
///
|
||||
/// Note that this is flattened/resolved from the original component to
|
||||
/// the point where alias annotations and such are not required. Instead
|
||||
/// the list of arguments to instantiate each module is provided as exports
|
||||
/// of prior instantiations.
|
||||
pub instances: PrimaryMap<RuntimeInstanceIndex, Instantiation>,
|
||||
/// This list of initializers does not correspond directly to the component
|
||||
/// itself. The general goal with this is that the recursive nature of
|
||||
/// components is "flattened" with an array like this which is a linear
|
||||
/// sequence of instructions of how to instantiate a component. This will
|
||||
/// have instantiations, for example, in addition to entries which
|
||||
/// initialize `VMComponentContext` fields with previously instantiated
|
||||
/// instances.
|
||||
///
|
||||
/// NB: at this time recursive components are not supported, and that may
|
||||
/// change this somewhat significantly.
|
||||
pub initializers: Vec<Initializer>,
|
||||
|
||||
/// The number of runtime instances (maximum `RuntimeInstanceIndex`) created
|
||||
/// when instantiating this component.
|
||||
pub num_runtime_instances: u32,
|
||||
|
||||
/// The number of runtime memories (maximum `RuntimeMemoryIndex`) needed to
|
||||
/// instantiate this component.
|
||||
///
|
||||
/// Note that this many memories will be stored in the `VMComponentContext`
|
||||
/// and each memory is intended to be unique (e.g. the same memory isn't
|
||||
/// stored in two different locations).
|
||||
pub num_runtime_memories: u32,
|
||||
|
||||
/// The number of runtime reallocs (maximum `RuntimeReallocIndex`) needed to
|
||||
/// instantiate this component.
|
||||
///
|
||||
/// Note that this many function pointers will be stored in the
|
||||
/// `VMComponentContext`.
|
||||
pub num_runtime_reallocs: u32,
|
||||
|
||||
/// The number of lowered host functions (maximum `LoweredIndex`) needed to
|
||||
/// instantiate this component.
|
||||
pub num_lowerings: u32,
|
||||
}
|
||||
|
||||
/// Different ways to instantiate a module at runtime.
|
||||
/// Initializer instructions to get processed when instantiating a component
|
||||
///
|
||||
/// The variants of this enum are processed during the instantiation phase of
|
||||
/// a component in-order from front-to-back. These are otherwise emitted as a
|
||||
/// component is parsed and read and translated.
|
||||
///
|
||||
/// NB: at this time recursive components are not supported, and that may
|
||||
/// change this somewhat significantly.
|
||||
///
|
||||
//
|
||||
// FIXME(#2639) if processing this list is ever a bottleneck we could
|
||||
// theoretically use cranelift to compile an initialization function which
|
||||
// performs all of these duties for us and skips the overhead of interpreting
|
||||
// all of these instructions.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum Instantiation {
|
||||
/// A module "upvar" is being instantiated which is a closed-over module
|
||||
/// that is known at runtime by index.
|
||||
ModuleUpvar {
|
||||
/// The module index which is being instantiated.
|
||||
module: ModuleUpvarIndex,
|
||||
/// The flat list of arguments to the module's instantiation.
|
||||
args: Box<[CoreExport<EntityIndex>]>,
|
||||
pub enum Initializer {
|
||||
/// A core was module is being instantiated.
|
||||
///
|
||||
/// This will result in a new core wasm instance being created, which may
|
||||
/// involve running the `start` function of the instance as well if it's
|
||||
/// specified. This largely delegates to the same standard instantiation
|
||||
/// process as the rest of the core wasm machinery already uses.
|
||||
InstantiateModule {
|
||||
/// The instance of the index that's being created.
|
||||
///
|
||||
/// This is guaranteed to be the `n`th `InstantiateModule` instruction
|
||||
/// if the index is `n`.
|
||||
instance: RuntimeInstanceIndex,
|
||||
|
||||
/// The module that's being instantiated, either an "upvar" or an
|
||||
/// imported module.
|
||||
module: ModuleToInstantiate,
|
||||
|
||||
/// The arguments to instantiation and where they're loaded from.
|
||||
///
|
||||
/// Note that this is a flat list. For "upvars" this list is sorted by
|
||||
/// the actual concrete imports needed by the upvar so the items can be
|
||||
/// passed directly to instantiation. For imports this list is sorted
|
||||
/// by the order of the import names on the type of the module
|
||||
/// declaration in this component.
|
||||
///
|
||||
/// Each argument is a `CoreDef` which represents that it's either, at
|
||||
/// this time, a lowered imported function or a core wasm item from
|
||||
/// another previously instantiated instance.
|
||||
args: Box<[CoreDef]>,
|
||||
},
|
||||
|
||||
/// A module import is being instantiated.
|
||||
/// A host function is being lowered, creating a core wasm function.
|
||||
///
|
||||
/// NB: this is not implemented in the runtime yet so this is a little less
|
||||
/// fleshed out than the above case. For example it's not entirely clear how
|
||||
/// the import will be referred to here (here a `usize` is used but unsure
|
||||
/// if that will work out).
|
||||
ModuleImport {
|
||||
/// Which module import is being instantiated.
|
||||
import_index: usize,
|
||||
/// The flat list of arguments to the module's instantiation.
|
||||
args: Box<[CoreExport<EntityIndex>]>,
|
||||
/// This initializer entry is intended to be used to fill out the
|
||||
/// `VMComponentContext` and information about this lowering such as the
|
||||
/// cranelift-compiled trampoline function pointer, the host function
|
||||
/// pointer the trampline calls, and the canonical ABI options.
|
||||
LowerImport(LowerImport),
|
||||
|
||||
/// A core wasm linear memory is going to be saved into the
|
||||
/// `VMComponentContext`.
|
||||
///
|
||||
/// This instruction indicates that the `index`th core wasm linear memory
|
||||
/// needs to be extracted from the `export` specified, a pointer to a
|
||||
/// previously created module instance, and stored into the
|
||||
/// `VMComponentContext` at the `index` specified. This lowering is then
|
||||
/// used in the future by pointers from `CanonicalOptions`.
|
||||
ExtractMemory {
|
||||
/// The index of the memory we're storing.
|
||||
///
|
||||
/// This is guaranteed to be the `n`th `ExtractMemory` instruction
|
||||
/// if the index is `n`.
|
||||
index: RuntimeMemoryIndex,
|
||||
/// The source of the memory that is stored.
|
||||
export: CoreExport<MemoryIndex>,
|
||||
},
|
||||
|
||||
/// Same as `ExtractMemory`, except it's extracting a function pointer to be
|
||||
/// used as a `realloc` function.
|
||||
ExtractRealloc {
|
||||
/// The index of the realloc function we're storing.
|
||||
///
|
||||
/// This is guaranteed to be the `n`th `ExtractRealloc` instruction
|
||||
/// if the index is `n`.
|
||||
index: RuntimeReallocIndex,
|
||||
/// The source of the function pointer that is stored.
|
||||
def: CoreDef,
|
||||
},
|
||||
}
|
||||
|
||||
/// Indicator used to refer to what module is being instantiated when
|
||||
/// `Initializer::InstantiateModule` is used.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ModuleToInstantiate {
|
||||
/// An "upvar", or a module defined within a component, is being used.
|
||||
///
|
||||
/// The index here is correlated with the `Translation::upvars` map that's
|
||||
/// created during translation of a component.
|
||||
Upvar(ModuleUpvarIndex),
|
||||
|
||||
/// An imported core wasm module is being instantiated.
|
||||
///
|
||||
/// It's guaranteed that this `RuntimeImportIndex` points to a module.
|
||||
Import(RuntimeImportIndex),
|
||||
}
|
||||
|
||||
/// Description of a lowered import used in conjunction with
|
||||
/// `Initializer::LowerImport`.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct LowerImport {
|
||||
/// The index of the lowered function that's being created.
|
||||
///
|
||||
/// This is guaranteed to be the `n`th `LowerImport` instruction
|
||||
/// if the index is `n`.
|
||||
pub index: LoweredIndex,
|
||||
|
||||
/// The index of the imported host function that is being lowered.
|
||||
///
|
||||
/// It's guaranteed that this `RuntimeImportIndex` points to a function.
|
||||
pub import: RuntimeImportIndex,
|
||||
|
||||
/// The core wasm signature of the function that's being created.
|
||||
pub canonical_abi: SignatureIndex,
|
||||
|
||||
/// The canonical ABI options used when lowering this function specified in
|
||||
/// the original component.
|
||||
pub options: CanonicalOptions,
|
||||
}
|
||||
|
||||
/// Definition of a core wasm item and where it can come from within a
|
||||
/// component.
|
||||
///
|
||||
/// Note that this is sort of a result of data-flow-like analysis on a component
|
||||
/// during compile time of the component itself. References to core wasm items
|
||||
/// are "compiled" to either referring to a previous instance or to some sort of
|
||||
/// lowered host import.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
|
||||
pub enum CoreDef {
|
||||
/// This item refers to an export of a previously instantiated core wasm
|
||||
/// instance.
|
||||
Export(CoreExport<EntityIndex>),
|
||||
/// This item is a core wasm function with the index specified here. Note
|
||||
/// that this `LoweredIndex` corresponds to the nth
|
||||
/// `Initializer::LowerImport` instruction.
|
||||
Lowered(LoweredIndex),
|
||||
}
|
||||
|
||||
impl From<CoreExport<EntityIndex>> for CoreDef {
|
||||
fn from(export: CoreExport<EntityIndex>) -> CoreDef {
|
||||
CoreDef::Export(export)
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier of an exported item from a core WebAssembly module instance.
|
||||
@@ -82,7 +264,7 @@ pub enum Instantiation {
|
||||
/// Note that the `T` here is the index type for exports which can be
|
||||
/// identified by index. The `T` is monomorphized with types like
|
||||
/// [`EntityIndex`] or [`FuncIndex`].
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
|
||||
pub struct CoreExport<T> {
|
||||
/// The instance that this item is located within.
|
||||
///
|
||||
@@ -96,7 +278,7 @@ pub struct CoreExport<T> {
|
||||
}
|
||||
|
||||
/// An index at which to find an item within a runtime instance.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
|
||||
pub enum ExportItem<T> {
|
||||
/// An exact index that the target can be found at.
|
||||
///
|
||||
@@ -119,65 +301,50 @@ pub enum ExportItem<T> {
|
||||
pub enum Export {
|
||||
/// A lifted function being exported which is an adaptation of a core wasm
|
||||
/// function.
|
||||
LiftedFunction(LiftedFunction),
|
||||
LiftedFunction {
|
||||
/// The component function type of the function being created.
|
||||
ty: FuncTypeIndex,
|
||||
/// Which core WebAssembly export is being lifted.
|
||||
func: CoreExport<FuncIndex>,
|
||||
/// Any options, if present, associated with this lifting.
|
||||
options: CanonicalOptions,
|
||||
},
|
||||
}
|
||||
|
||||
/// Description of a lifted function.
|
||||
///
|
||||
/// This represents how a function was lifted, what options were used to lift
|
||||
/// it, and how it's all processed at runtime.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LiftedFunction {
|
||||
/// The component function type of the function being created.
|
||||
pub ty: FuncTypeIndex,
|
||||
/// Which core WebAssembly export is being lifted.
|
||||
pub func: CoreExport<FuncIndex>,
|
||||
/// Any options, if present, associated with this lifting.
|
||||
pub options: CanonicalOptions,
|
||||
}
|
||||
|
||||
/// Canonical ABI options associated with a lifted function.
|
||||
/// Canonical ABI options associated with a lifted or lowered function.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CanonicalOptions {
|
||||
/// The encoding used for strings.
|
||||
pub string_encoding: StringEncoding,
|
||||
/// Representation of the `into` option where intrinsics are peeled out and
|
||||
/// identified from an instance.
|
||||
pub intrinsics: Option<Intrinsics>,
|
||||
|
||||
/// The memory used by these options, if specified.
|
||||
pub memory: Option<RuntimeMemoryIndex>,
|
||||
|
||||
/// The realloc function used by these options, if specified.
|
||||
pub realloc: Option<RuntimeReallocIndex>,
|
||||
// TODO: need to represent post-return here as well
|
||||
}
|
||||
|
||||
impl Default for CanonicalOptions {
|
||||
fn default() -> CanonicalOptions {
|
||||
CanonicalOptions {
|
||||
string_encoding: StringEncoding::Utf8,
|
||||
intrinsics: None,
|
||||
memory: None,
|
||||
realloc: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible encodings of strings within the component model.
|
||||
//
|
||||
// Note that the `repr(u8)` is load-bearing here since this is used in an
|
||||
// `extern "C" fn()` function argument which is called from cranelift-compiled
|
||||
// code so we must know the representation of this.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(u8)]
|
||||
pub enum StringEncoding {
|
||||
Utf8,
|
||||
Utf16,
|
||||
CompactUtf16,
|
||||
}
|
||||
|
||||
/// Intrinsics required with the `(into $instance)` option specified in
|
||||
/// `canon.lift`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Intrinsics {
|
||||
/// The linear memory that the module exports which we're reading/writing
|
||||
/// from.
|
||||
pub memory: CoreExport<MemoryIndex>,
|
||||
|
||||
/// A memory allocation, and reallocation, function.
|
||||
pub canonical_abi_realloc: CoreExport<FuncIndex>,
|
||||
|
||||
/// A memory deallocation function.
|
||||
///
|
||||
/// NB: this will probably be replaced with a per-export-destructor rather
|
||||
/// than a general memory deallocation function.
|
||||
pub canonical_abi_free: CoreExport<FuncIndex>,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use crate::component::*;
|
||||
use crate::{EntityIndex, ModuleEnvironment, ModuleTranslation, PrimaryMap, Tunables};
|
||||
use crate::{
|
||||
EntityIndex, EntityType, ModuleEnvironment, ModuleTranslation, PrimaryMap, SignatureIndex,
|
||||
Tunables,
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
@@ -32,8 +35,7 @@ pub struct Translation<'data> {
|
||||
// runtime. These are used to understand the structure of the component and
|
||||
// where items come from but at this time these index spaces and their
|
||||
// definitions are not required at runtime as they're effectively "erased"
|
||||
// at the moment. This will probably change as more of the component model
|
||||
// is implemented.
|
||||
// at the moment.
|
||||
//
|
||||
/// Modules and how they're defined (either closed-over or imported)
|
||||
modules: PrimaryMap<ModuleIndex, ModuleDef>,
|
||||
@@ -54,10 +56,36 @@ pub struct Translation<'data> {
|
||||
|
||||
/// Core wasm tables, always sourced from a previously module instance.
|
||||
tables: PrimaryMap<TableIndex, CoreSource<'data>>,
|
||||
|
||||
/// This is a list of pairs where the first element points to an index
|
||||
/// within `component.initializers` to an `Initializer::LowerImport` entry.
|
||||
/// After a component has finished translation and we have a
|
||||
/// `wasmparser::Types` value to lookup type information within the type of
|
||||
/// `FuncIndex`, within this component, will be used to fill in the
|
||||
/// `LowerImport::canonical_abi` field.
|
||||
///
|
||||
/// This avoids wasmtime having to duplicate the
|
||||
/// interface-types-signature-to-core-wasm-signature lowering logic.
|
||||
signatures_to_fill: Vec<(usize, FuncIndex)>,
|
||||
|
||||
/// Intern'd map of imports where `RuntimeImport` represents some
|
||||
/// (optional) projection of imports from an original import and
|
||||
/// `RuntimeImportIndex` is an array built at runtime used to instantiate
|
||||
/// this component.
|
||||
import_map: HashMap<RuntimeImport, RuntimeImportIndex>,
|
||||
|
||||
/// Intern'd map of exports to the memory index they're referred to by at
|
||||
/// runtime, used when building `CanonicalOptions` to avoid storing the same
|
||||
/// memory many times within a `VMComponentContext`.
|
||||
memory_to_runtime: HashMap<CoreExport<MemoryIndex>, RuntimeMemoryIndex>,
|
||||
|
||||
/// Same as `memory_to_runtime` but an intern'd map for realloc functions
|
||||
/// instead.
|
||||
realloc_to_runtime: HashMap<CoreDef, RuntimeReallocIndex>,
|
||||
}
|
||||
|
||||
/// How a module is defined within a component.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
enum ModuleDef {
|
||||
/// This module is defined as an "upvar" or a closed over variable
|
||||
/// implicitly available for the component.
|
||||
@@ -71,29 +99,71 @@ enum ModuleDef {
|
||||
/// nothing is known about it except for its type. The `import_index`
|
||||
/// provided here indexes into the `Component`'s import list.
|
||||
Import {
|
||||
type_idx: ModuleTypeIndex,
|
||||
import_index: usize,
|
||||
ty: ModuleTypeIndex,
|
||||
import: RuntimeImport,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// How instances are defined within a component.
|
||||
#[derive(Debug, Clone)]
|
||||
enum InstanceDef<'data> {
|
||||
/// A module instance created through the instantiation of a previous
|
||||
/// module.
|
||||
Module {
|
||||
/// The runtime index associated with this instance.
|
||||
///
|
||||
/// Not to be confused with `InstanceIndex` which counts "synthetic"
|
||||
/// instances as well.
|
||||
instance: RuntimeInstanceIndex,
|
||||
|
||||
/// The module that was instantiated.
|
||||
module: ModuleIndex,
|
||||
},
|
||||
|
||||
/// A "synthetic" module created as a bag of exports from other items
|
||||
/// already defined within this component.
|
||||
ModuleSynthetic(HashMap<&'data str, EntityIndex>),
|
||||
|
||||
/// An instance which was imported from the host.
|
||||
Import {
|
||||
/// The type of the imported instance
|
||||
ty: ComponentInstanceTypeIndex,
|
||||
/// The description of where this import came from.
|
||||
import: RuntimeImport,
|
||||
},
|
||||
|
||||
/// Same as `ModuleSynthetic` except for component items.
|
||||
ComponentSynthetic(HashMap<&'data str, ComponentItem>),
|
||||
}
|
||||
|
||||
/// Source of truth for where a core wasm item comes from.
|
||||
/// Description of the function index space and how functions are defined.
|
||||
#[derive(Clone)]
|
||||
enum Func<'data> {
|
||||
// component functions
|
||||
//
|
||||
/// A component function that is imported from the host.
|
||||
Import(RuntimeImport),
|
||||
|
||||
/// A component function that is lifted from core wasm function.
|
||||
Lifted {
|
||||
/// The resulting type of the lifted function
|
||||
ty: FuncTypeIndex,
|
||||
/// Which core wasm function is lifted, currently required to be an
|
||||
/// instance export as opposed to a lowered import.
|
||||
func: CoreSource<'data>,
|
||||
/// The options specified when the function was lifted.
|
||||
options: CanonicalOptions,
|
||||
},
|
||||
|
||||
// core function
|
||||
//
|
||||
/// A core wasm function that's extracted from a core wasm instance.
|
||||
Core(CoreSource<'data>),
|
||||
/// A core wasm function created by lowering an imported host function.
|
||||
///
|
||||
/// Note that `LoweredIndex` here refers to the nth
|
||||
/// `Initializer::LowerImport`.
|
||||
Lowered(LoweredIndex),
|
||||
}
|
||||
|
||||
/// Source of truth for where a core wasm item comes from.
|
||||
@@ -120,6 +190,28 @@ enum Action {
|
||||
Done,
|
||||
}
|
||||
|
||||
/// Pre-intern'd representation of a `RuntimeImportIndex`.
|
||||
///
|
||||
/// When this is actually used within a component it will be committed into the
|
||||
/// `import_map` to give it a `RuntimeImportIndex` via the
|
||||
/// `runtime_import_index` function.
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
struct RuntimeImport {
|
||||
source: ImportIndex,
|
||||
exports: Vec<String>,
|
||||
}
|
||||
|
||||
impl RuntimeImport {
|
||||
fn append(&self, name: &str) -> RuntimeImport {
|
||||
let mut exports = self.exports.clone();
|
||||
exports.push(name.to_string());
|
||||
RuntimeImport {
|
||||
source: self.source,
|
||||
exports,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'data> Translator<'a, 'data> {
|
||||
/// Creates a new translation state ready to translate a component.
|
||||
pub fn new(
|
||||
@@ -189,7 +281,24 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
}
|
||||
|
||||
Payload::End(offset) => {
|
||||
self.validator.end(offset)?;
|
||||
let types = self.validator.end(offset)?;
|
||||
|
||||
// With type information in hand fill in the canonical abi type
|
||||
// of lowered functions.
|
||||
for (idx, func) in self.result.signatures_to_fill.drain(..) {
|
||||
let i = match &mut self.result.component.initializers[idx] {
|
||||
Initializer::LowerImport(i) => i,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert!(i.canonical_abi.as_u32() == 0);
|
||||
i.canonical_abi = self.types.module_types_builder().wasm_func_type(
|
||||
types
|
||||
.function_at(func.as_u32())
|
||||
.expect("should be in-bounds")
|
||||
.clone()
|
||||
.try_into()?,
|
||||
);
|
||||
}
|
||||
|
||||
// When leaving a module be sure to pop the types scope to
|
||||
// ensure that when we go back to the previous module outer
|
||||
@@ -224,30 +333,35 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
let import = import?;
|
||||
let ty = TypeIndex::from_u32(import.ty);
|
||||
let ty = self.types.component_outer_type(0, ty);
|
||||
let (import_index, prev) = self
|
||||
// Record the `ImportIndex` to be associated with this
|
||||
// import and create the `RuntimeImport` representing the
|
||||
// "root" where it has no extra `exports`
|
||||
let source = self
|
||||
.result
|
||||
.component
|
||||
.imports
|
||||
.insert_full(import.name.to_string(), ty);
|
||||
assert!(prev.is_none());
|
||||
.import_types
|
||||
.push((import.name.to_string(), ty));
|
||||
let import = RuntimeImport {
|
||||
source,
|
||||
exports: Vec::new(),
|
||||
};
|
||||
match ty {
|
||||
TypeDef::Module(type_idx) => {
|
||||
self.result.modules.push(ModuleDef::Import {
|
||||
type_idx,
|
||||
import_index,
|
||||
});
|
||||
TypeDef::Module(ty) => {
|
||||
self.result.modules.push(ModuleDef::Import { ty, import });
|
||||
}
|
||||
TypeDef::ComponentInstance(ty) => {
|
||||
self.result
|
||||
.instances
|
||||
.push(InstanceDef::Import { ty, import });
|
||||
}
|
||||
TypeDef::Func(_ty) => {
|
||||
self.result.funcs.push(Func::Import(import));
|
||||
}
|
||||
TypeDef::Component(_) => {
|
||||
unimplemented!("component imports");
|
||||
}
|
||||
TypeDef::ComponentInstance(_) => {
|
||||
unimplemented!("component instance imports");
|
||||
}
|
||||
TypeDef::Func(_) => {
|
||||
unimplemented!("function imports");
|
||||
unimplemented!("imports of components");
|
||||
}
|
||||
TypeDef::Interface(_) => {
|
||||
unimplemented!("interface type imports");
|
||||
unimplemented!("imports of types");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,8 +384,8 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
func_index,
|
||||
options,
|
||||
} => {
|
||||
drop((func_index, options));
|
||||
unimplemented!("lowered functions");
|
||||
let func = FuncIndex::from_u32(func_index);
|
||||
self.lower_function(func, &options)
|
||||
}
|
||||
};
|
||||
self.result.funcs.push(func);
|
||||
@@ -282,6 +396,10 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
// `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(
|
||||
@@ -317,8 +435,7 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
unimplemented!("instantiating a component");
|
||||
}
|
||||
wasmparser::Instance::ComponentFromExports(exports) => {
|
||||
drop(exports);
|
||||
unimplemented!("instantiating a component");
|
||||
self.component_instance_from_exports(&exports)
|
||||
}
|
||||
};
|
||||
self.result.instances.push(instance);
|
||||
@@ -366,7 +483,7 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
|
||||
fn module_instance(
|
||||
&mut self,
|
||||
module: ModuleIndex,
|
||||
module_idx: ModuleIndex,
|
||||
args: &[wasmparser::ModuleArg<'data>],
|
||||
) -> InstanceDef<'data> {
|
||||
// Map the flat list of `args` to instead a name-to-instance index.
|
||||
@@ -379,102 +496,124 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
}
|
||||
}
|
||||
|
||||
let instantiation = match self.result.modules[module] {
|
||||
// For modules which we are statically aware of we can look at the
|
||||
// exact order of imports required and build up a list of arguemnts
|
||||
// in that order. This will be fast at runtime because all we have
|
||||
// to do is build up the import lists for instantiation, no name
|
||||
// lookups necessary.
|
||||
let (imports, module) = match self.result.modules[module_idx].clone() {
|
||||
// A module defined within this component is being instantiated
|
||||
// which means we statically know the structure of the module. The
|
||||
// list of imports required is ordered by the actual list of imports
|
||||
// listed on the module itself (which wasmtime later requires during
|
||||
// instantiation).
|
||||
ModuleDef::Upvar(upvar_idx) => {
|
||||
let trans = &self.result.upvars[upvar_idx];
|
||||
Instantiation::ModuleUpvar {
|
||||
module: upvar_idx,
|
||||
args: self.module_instance_args(
|
||||
&instance_by_name,
|
||||
trans.module.imports().map(|(m, n, _)| (m, n)),
|
||||
),
|
||||
}
|
||||
let args = self.result.upvars[upvar_idx]
|
||||
.module
|
||||
.imports()
|
||||
.map(|(m, n, _)| (m.to_string(), n.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
(args, ModuleToInstantiate::Upvar(upvar_idx))
|
||||
}
|
||||
|
||||
// For imported modules the list of arguments is built to match the
|
||||
// order of the imports listed. Note that this will need to be
|
||||
// reshuffled at runtime since the actual module being instantiated
|
||||
// may originally have required imports in a different order.
|
||||
ModuleDef::Import {
|
||||
type_idx,
|
||||
import_index,
|
||||
} => {
|
||||
let ty = &self.types[type_idx];
|
||||
Instantiation::ModuleImport {
|
||||
import_index,
|
||||
args: self.module_instance_args(
|
||||
&instance_by_name,
|
||||
ty.imports.keys().map(|(a, b)| (a.as_str(), b.as_str())),
|
||||
),
|
||||
}
|
||||
// order of the imports listed in the declared type of the module.
|
||||
// Note that this will need to be reshuffled at runtime since the
|
||||
// actual module being instantiated may originally have required
|
||||
// imports in a different order.
|
||||
ModuleDef::Import { ty, import } => {
|
||||
let import = self.runtime_import_index(import);
|
||||
let args = self.types[ty].imports.keys().cloned().collect();
|
||||
(args, ModuleToInstantiate::Import(import))
|
||||
}
|
||||
};
|
||||
let instance = self.result.component.instances.push(instantiation);
|
||||
InstanceDef::Module { instance, module }
|
||||
|
||||
// Translate the desired order of import strings to a `CoreDef` used to
|
||||
// instantiate each module. Of the two-level namespace the `module` name
|
||||
// is indicated by the `args` argument to this function and the `name`
|
||||
// is the export of the instance found that's used.
|
||||
let args = imports
|
||||
.iter()
|
||||
.map(|(module, name)| self.lookup_core_def(instance_by_name[module.as_str()], name))
|
||||
.collect();
|
||||
|
||||
// Record initializer information related to this instantiation now that
|
||||
// we've figure out all the arguments.
|
||||
let instance = RuntimeInstanceIndex::from_u32(self.result.component.num_runtime_instances);
|
||||
self.result.component.num_runtime_instances += 1;
|
||||
self.result
|
||||
.component
|
||||
.initializers
|
||||
.push(Initializer::InstantiateModule {
|
||||
instance,
|
||||
module,
|
||||
args,
|
||||
});
|
||||
InstanceDef::Module {
|
||||
instance,
|
||||
module: module_idx,
|
||||
}
|
||||
}
|
||||
|
||||
/// Translates the named arguments required by a core wasm module specified
|
||||
/// by `iter` into a list of where the arguments come from at runtime.
|
||||
/// Calculate the `CoreDef`, a definition of a core wasm item, corresponding
|
||||
/// to the export `name` of the `instance` specified.
|
||||
///
|
||||
/// The `instance_by_name` map is used go go from the module name of an
|
||||
/// import to the instance that's satisfying the import. The `name` field
|
||||
/// of the import is then looked up within the instance's own exports.
|
||||
fn module_instance_args<'b>(
|
||||
&self,
|
||||
instance_by_name: &HashMap<&'data str, InstanceIndex>,
|
||||
iter: impl Iterator<Item = (&'b str, &'b str)>,
|
||||
) -> Box<[CoreExport<EntityIndex>]> {
|
||||
iter.map(|(module, name)| {
|
||||
self.lookup_core_source(instance_by_name[module], name)
|
||||
.to_core_export(|i| i)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Looks up the `CoreSource` corresponding to the export `name` of the
|
||||
/// `module` specified.
|
||||
///
|
||||
/// This classifies the export of the module as either one which we
|
||||
/// statically know by index within the module itself (because we know the
|
||||
/// module), or one that must be referred to by name.
|
||||
fn lookup_core_source(&self, instance: InstanceIndex, name: &'data str) -> CoreSource<'data> {
|
||||
/// This classifies the export of the instance as one which we
|
||||
/// statically know by index within an instantiated module (because
|
||||
/// we know the module), one that must be referred to by name since the
|
||||
/// module isn't known, or it's a synthesized lowering or adapter of a
|
||||
/// component function.
|
||||
fn lookup_core_def(&mut self, instance: InstanceIndex, name: &str) -> CoreDef {
|
||||
match &self.result.instances[instance] {
|
||||
// The `instance` points to an instantiated module...
|
||||
InstanceDef::Module { module, instance } => match self.result.modules[*module] {
|
||||
// ... and the module instantiated is one that we statically
|
||||
// know the structure of. This means that `name` points to an
|
||||
// exact index of an item within the module which we lookup here
|
||||
// and record.
|
||||
ModuleDef::Upvar(upvar_idx) => {
|
||||
let trans = &self.result.upvars[upvar_idx];
|
||||
CoreSource::Index(*instance, trans.module.exports[name])
|
||||
}
|
||||
InstanceDef::Module { module, instance } => {
|
||||
let (src, _ty) = self.lookup_core_source_in_module(*instance, *module, name);
|
||||
src.to_core_def()
|
||||
}
|
||||
|
||||
// ... and the module instantiated is imported so we don't
|
||||
// statically know its structure. This means taht the export
|
||||
// must be identified by name.
|
||||
ModuleDef::Import { .. } => CoreSource::Export(*instance, name),
|
||||
},
|
||||
|
||||
// The `instance `points to a "synthetic" instance created in the
|
||||
// component as a collection of named items from other instances.
|
||||
// This means that we're simply copying over the original source of
|
||||
// the item in the first place.
|
||||
InstanceDef::ModuleSynthetic(defs) => match defs[&name] {
|
||||
EntityIndex::Function(f) => match &self.result.funcs[f] {
|
||||
Func::Core(c) => c.clone(),
|
||||
EntityIndex::Function(f) => match self.result.funcs[f].clone() {
|
||||
Func::Core(c) => c.to_core_def(),
|
||||
Func::Lowered(i) => CoreDef::Lowered(i),
|
||||
|
||||
// should not be possible to hit with a valid component
|
||||
Func::Lifted { .. } => unreachable!(),
|
||||
Func::Lifted { .. } | Func::Import { .. } => unreachable!(),
|
||||
},
|
||||
EntityIndex::Global(g) => self.result.globals[g].clone(),
|
||||
EntityIndex::Table(t) => self.result.tables[t].clone(),
|
||||
EntityIndex::Memory(m) => self.result.memories[m].clone(),
|
||||
EntityIndex::Global(g) => self.result.globals[g].to_core_def(),
|
||||
EntityIndex::Table(t) => self.result.tables[t].to_core_def(),
|
||||
EntityIndex::Memory(m) => self.result.memories[m].to_core_def(),
|
||||
},
|
||||
|
||||
// should not be possible to hit with a valid component
|
||||
InstanceDef::Import { .. } | InstanceDef::ComponentSynthetic(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the `CoreSource` associated with the export `name` as an
|
||||
/// instance of the instantiated `module` specified.
|
||||
///
|
||||
/// The `instance` index here represents the runtime instance index that
|
||||
/// we're looking up within.
|
||||
fn lookup_core_source_in_module<'b>(
|
||||
&self,
|
||||
instance: RuntimeInstanceIndex,
|
||||
module: ModuleIndex,
|
||||
name: &'b str,
|
||||
) -> (CoreSource<'b>, EntityType) {
|
||||
match self.result.modules[module] {
|
||||
// The module instantiated is one that we statically know the
|
||||
// structure of. This means that `name` points to an exact index of
|
||||
// an item within the module which we lookup here and record.
|
||||
ModuleDef::Upvar(upvar_idx) => {
|
||||
let trans = &self.result.upvars[upvar_idx];
|
||||
let idx = trans.module.exports[name];
|
||||
let src = CoreSource::Index(instance, idx);
|
||||
let ty = trans.module.type_of(idx);
|
||||
(src, ty)
|
||||
}
|
||||
|
||||
// The module instantiated is imported so we don't statically know
|
||||
// its structure. This means that the export must be identified by
|
||||
// name.
|
||||
ModuleDef::Import { ty, .. } => {
|
||||
let src = CoreSource::Export(instance, name);
|
||||
let ty = self.types[ty].exports[name].clone();
|
||||
(src, ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,53 +644,110 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
}
|
||||
|
||||
// doesn't get past validation
|
||||
wasmparser::ExternalKind::Tag => unimplemented!(),
|
||||
wasmparser::ExternalKind::Tag => unimplemented!("wasm exceptions"),
|
||||
};
|
||||
map.insert(export.name, idx);
|
||||
}
|
||||
InstanceDef::ModuleSynthetic(map)
|
||||
}
|
||||
|
||||
/// Creates a synthetic module from the list of items currently in the
|
||||
/// module and their given names.
|
||||
fn component_instance_from_exports(
|
||||
&mut self,
|
||||
exports: &[wasmparser::ComponentExport<'data>],
|
||||
) -> InstanceDef<'data> {
|
||||
let mut map = HashMap::with_capacity(exports.len());
|
||||
for export in exports {
|
||||
let idx = match &export.kind {
|
||||
wasmparser::ComponentArgKind::Function(i) => {
|
||||
let index = FuncIndex::from_u32(*i);
|
||||
ComponentItem::Func(index)
|
||||
}
|
||||
wasmparser::ComponentArgKind::Module(i) => {
|
||||
let index = ModuleIndex::from_u32(*i);
|
||||
ComponentItem::Module(index)
|
||||
}
|
||||
wasmparser::ComponentArgKind::Instance(i) => {
|
||||
let index = InstanceIndex::from_u32(*i);
|
||||
ComponentItem::Instance(index)
|
||||
}
|
||||
wasmparser::ComponentArgKind::Component(i) => {
|
||||
let index = ComponentIndex::from_u32(*i);
|
||||
ComponentItem::Component(index)
|
||||
}
|
||||
wasmparser::ComponentArgKind::Value(_) => {
|
||||
unimplemented!("component values");
|
||||
}
|
||||
wasmparser::ComponentArgKind::Type(_) => {
|
||||
unimplemented!("component type export");
|
||||
}
|
||||
};
|
||||
map.insert(export.name, idx);
|
||||
}
|
||||
InstanceDef::ComponentSynthetic(map)
|
||||
}
|
||||
|
||||
fn export(&mut self, export: &wasmparser::ComponentExport<'data>) {
|
||||
let name = export.name;
|
||||
let export = match export.kind {
|
||||
wasmparser::ComponentExportKind::Module(i) => {
|
||||
let idx = ModuleIndex::from_u32(i);
|
||||
drop(idx);
|
||||
unimplemented!("unimplemented module export");
|
||||
unimplemented!("exporting a module");
|
||||
}
|
||||
wasmparser::ComponentExportKind::Component(i) => {
|
||||
let idx = ComponentIndex::from_u32(i);
|
||||
drop(idx);
|
||||
unimplemented!("unimplemented component export");
|
||||
unimplemented!("exporting a component");
|
||||
}
|
||||
wasmparser::ComponentExportKind::Instance(i) => {
|
||||
let idx = InstanceIndex::from_u32(i);
|
||||
drop(idx);
|
||||
unimplemented!("unimplemented instance export");
|
||||
unimplemented!("exporting an instance");
|
||||
}
|
||||
wasmparser::ComponentExportKind::Function(i) => {
|
||||
let idx = FuncIndex::from_u32(i);
|
||||
match &self.result.funcs[idx] {
|
||||
Func::Lifted { ty, func, options } => Export::LiftedFunction(LiftedFunction {
|
||||
ty: *ty,
|
||||
match self.result.funcs[idx].clone() {
|
||||
Func::Lifted { ty, func, options } => Export::LiftedFunction {
|
||||
ty,
|
||||
func: func.to_core_export(|i| match i {
|
||||
EntityIndex::Function(i) => i,
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
options: options.clone(),
|
||||
}),
|
||||
options,
|
||||
},
|
||||
|
||||
// TODO: Not 100% clear what to do about this. Given the
|
||||
// expected implementation of host functions there's not a
|
||||
// great way to actually invoke a host function after it's
|
||||
// been wrapped up in a `Func` (or similar). One of the
|
||||
// major issues here is that the callee expects the
|
||||
// canonical-abi format but the caller has host-rust format,
|
||||
// and bridging that gap is expected to be nontrivial.
|
||||
//
|
||||
// This may be solvable with like a temporary arena to lower
|
||||
// into which is discarded after the call finishes? Or...
|
||||
// something like that? This may not be too important to
|
||||
// support in terms of perf so if it's not the fastest thing
|
||||
// in the world that's probably alright.
|
||||
//
|
||||
// Nevertheless this shouldn't panic, eventually when the
|
||||
// component model implementation is finished this should do
|
||||
// something reasonable.
|
||||
Func::Import { .. } => unimplemented!("exporting an import"),
|
||||
|
||||
// should not be possible to hit with a valid module.
|
||||
Func::Core(_) => unreachable!(),
|
||||
Func::Core(_) | Func::Lowered(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
wasmparser::ComponentExportKind::Value(_) => {
|
||||
unimplemented!("unimplemented value export");
|
||||
unimplemented!("exporting a value");
|
||||
}
|
||||
wasmparser::ComponentExportKind::Type(i) => {
|
||||
let idx = TypeIndex::from_u32(i);
|
||||
drop(idx);
|
||||
unimplemented!("unimplemented value export");
|
||||
unimplemented!("exporting a type");
|
||||
}
|
||||
};
|
||||
self.result
|
||||
@@ -568,11 +764,7 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
name,
|
||||
} => {
|
||||
let instance = InstanceIndex::from_u32(*instance);
|
||||
match &self.result.instances[instance] {
|
||||
InstanceDef::Module { .. } | InstanceDef::ModuleSynthetic(_) => {
|
||||
self.alias_module_instance_export(*kind, instance, name);
|
||||
}
|
||||
}
|
||||
self.alias_instance_export(*kind, instance, name);
|
||||
}
|
||||
wasmparser::Alias::OuterModule { .. } => {
|
||||
unimplemented!("alias outer module");
|
||||
@@ -597,36 +789,111 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts an item in to the relevant namespace aliasing the `name`'d
|
||||
/// export of the `instance` provided.
|
||||
fn alias_module_instance_export(
|
||||
fn alias_instance_export(
|
||||
&mut self,
|
||||
kind: wasmparser::AliasKind,
|
||||
instance: InstanceIndex,
|
||||
name: &'data str,
|
||||
) {
|
||||
let src = self.lookup_core_source(instance, name);
|
||||
match kind {
|
||||
wasmparser::AliasKind::Func => {
|
||||
self.result.funcs.push(Func::Core(src));
|
||||
match &self.result.instances[instance] {
|
||||
// The `instance` points to an imported component instance, meaning
|
||||
// that the item we're pushing into our index spaces is effectively
|
||||
// another form of import. The `name` is appended to the `import`
|
||||
// found here and then the appropriate namespace of an import is
|
||||
// recorded as well.
|
||||
InstanceDef::Import { import, ty } => {
|
||||
let import = import.append(name);
|
||||
match self.types[*ty].exports[name] {
|
||||
TypeDef::Module(ty) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Module);
|
||||
self.result.modules.push(ModuleDef::Import { import, ty });
|
||||
}
|
||||
TypeDef::ComponentInstance(ty) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Instance);
|
||||
self.result
|
||||
.instances
|
||||
.push(InstanceDef::Import { import, ty });
|
||||
}
|
||||
TypeDef::Func(_ty) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::ComponentFunc);
|
||||
self.result.funcs.push(Func::Import(import));
|
||||
}
|
||||
TypeDef::Interface(_) => unimplemented!("alias type export"),
|
||||
TypeDef::Component(_) => unimplemented!("alias component export"),
|
||||
}
|
||||
}
|
||||
wasmparser::AliasKind::Global => {
|
||||
self.result.globals.push(src);
|
||||
}
|
||||
wasmparser::AliasKind::Memory => {
|
||||
self.result.memories.push(src);
|
||||
}
|
||||
wasmparser::AliasKind::Table => {
|
||||
self.result.tables.push(src);
|
||||
}
|
||||
other => {
|
||||
panic!("unknown/unimplemented alias kind {other:?}");
|
||||
|
||||
// The `instance` points to an instantiated module, meaning we can
|
||||
// lookup the `CoreSource` associated with it and use the type
|
||||
// information to insert it into the appropriate namespace.
|
||||
InstanceDef::Module { instance, module } => {
|
||||
let (src, ty) = self.lookup_core_source_in_module(*instance, *module, name);
|
||||
match ty {
|
||||
EntityType::Function(_) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Func);
|
||||
self.result.funcs.push(Func::Core(src));
|
||||
}
|
||||
EntityType::Global(_) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Global);
|
||||
self.result.globals.push(src);
|
||||
}
|
||||
EntityType::Memory(_) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Memory);
|
||||
self.result.memories.push(src);
|
||||
}
|
||||
EntityType::Table(_) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Table);
|
||||
self.result.tables.push(src);
|
||||
}
|
||||
EntityType::Tag(_) => unimplemented!("wasm exceptions"),
|
||||
}
|
||||
}
|
||||
|
||||
// For synthetic component/module instances we can just copy the
|
||||
// definition of the original item into a new slot as well to record
|
||||
// that the index describes the same item.
|
||||
InstanceDef::ComponentSynthetic(exports) => match exports[&name] {
|
||||
ComponentItem::Func(i) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::ComponentFunc);
|
||||
self.result.funcs.push(self.result.funcs[i].clone());
|
||||
}
|
||||
ComponentItem::Module(i) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Module);
|
||||
self.result.modules.push(self.result.modules[i].clone());
|
||||
}
|
||||
ComponentItem::Instance(i) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Instance);
|
||||
self.result.instances.push(self.result.instances[i].clone());
|
||||
}
|
||||
ComponentItem::Component(_) => unimplemented!("aliasing a component export"),
|
||||
},
|
||||
|
||||
// ... and like above for synthetic components aliasing exports from
|
||||
// synthetic modules is also just copying around the identifying
|
||||
// information.
|
||||
InstanceDef::ModuleSynthetic(exports) => match exports[&name] {
|
||||
EntityIndex::Function(i) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Func);
|
||||
self.result.funcs.push(self.result.funcs[i].clone());
|
||||
}
|
||||
EntityIndex::Global(i) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Global);
|
||||
self.result.globals.push(self.result.globals[i].clone());
|
||||
}
|
||||
EntityIndex::Table(i) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Table);
|
||||
self.result.tables.push(self.result.tables[i].clone());
|
||||
}
|
||||
EntityIndex::Memory(i) => {
|
||||
assert_eq!(kind, wasmparser::AliasKind::Memory);
|
||||
self.result.memories.push(self.result.memories[i].clone());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn lift_function(
|
||||
&self,
|
||||
&mut self,
|
||||
ty: TypeIndex,
|
||||
func: FuncIndex,
|
||||
options: &[wasmparser::CanonicalOption],
|
||||
@@ -638,14 +905,64 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
};
|
||||
let func = match &self.result.funcs[func] {
|
||||
Func::Core(core) => core.clone(),
|
||||
|
||||
// TODO: it's not immediately obvious how to implement this. Once
|
||||
// lowered imports are fully implemented it may be the case that
|
||||
// implementing this "just falls out" of the same implementation.
|
||||
// This technically is valid and basically just result in leaking
|
||||
// memory into core wasm (since nothing is around to call
|
||||
// deallocation/free functions).
|
||||
Func::Lowered(_) => unimplemented!("lifting a lowered function"),
|
||||
|
||||
// should not be possible after validation
|
||||
Func::Lifted { .. } => unreachable!(),
|
||||
Func::Lifted { .. } | Func::Import { .. } => unreachable!(),
|
||||
};
|
||||
let options = self.canonical_options(options);
|
||||
Func::Lifted { ty, func, options }
|
||||
}
|
||||
|
||||
fn canonical_options(&self, opts: &[wasmparser::CanonicalOption]) -> CanonicalOptions {
|
||||
fn lower_function(
|
||||
&mut self,
|
||||
func: FuncIndex,
|
||||
options: &[wasmparser::CanonicalOption],
|
||||
) -> Func<'data> {
|
||||
let options = self.canonical_options(options);
|
||||
match self.result.funcs[func].clone() {
|
||||
Func::Import(import) => {
|
||||
let import = self.runtime_import_index(import);
|
||||
let index = LoweredIndex::from_u32(self.result.component.num_lowerings);
|
||||
self.result.component.num_lowerings += 1;
|
||||
let fill_idx = self.result.component.initializers.len();
|
||||
self.result
|
||||
.component
|
||||
.initializers
|
||||
.push(Initializer::LowerImport(LowerImport {
|
||||
index,
|
||||
import,
|
||||
options,
|
||||
// This is filled after the component is finished when
|
||||
// we have wasmparser's type information available, so
|
||||
// leave a dummy for now to get filled in.
|
||||
canonical_abi: SignatureIndex::from_u32(0),
|
||||
}));
|
||||
self.result
|
||||
.signatures_to_fill
|
||||
.push((fill_idx, self.result.funcs.next_key()));
|
||||
Func::Lowered(index)
|
||||
}
|
||||
|
||||
// TODO: From reading the spec, this technically should create a
|
||||
// function that lifts the arguments and then afterwards
|
||||
// unconditionally traps. That would mean that this validates the
|
||||
// arguments within the context of `options` and then traps.
|
||||
Func::Lifted { .. } => unimplemented!("lower a lifted function"),
|
||||
|
||||
// should not be possible after validation
|
||||
Func::Core(_) | Func::Lowered(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn canonical_options(&mut self, opts: &[wasmparser::CanonicalOption]) -> CanonicalOptions {
|
||||
let mut ret = CanonicalOptions::default();
|
||||
for opt in opts {
|
||||
match opt {
|
||||
@@ -663,34 +980,64 @@ impl<'a, 'data> Translator<'a, 'data> {
|
||||
|
||||
// Note that the `unreachable!()` should not happen for
|
||||
// components which have passed validation.
|
||||
let memory = self
|
||||
.lookup_core_source(instance, "memory")
|
||||
.to_core_export(|i| match i {
|
||||
EntityIndex::Memory(i) => i,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let canonical_abi_free = self
|
||||
.lookup_core_source(instance, "canonical_abi_free")
|
||||
.to_core_export(|i| match i {
|
||||
EntityIndex::Function(i) => i,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let canonical_abi_realloc = self
|
||||
.lookup_core_source(instance, "canonical_abi_realloc")
|
||||
.to_core_export(|i| match i {
|
||||
EntityIndex::Function(i) => i,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
ret.intrinsics = Some(Intrinsics {
|
||||
memory,
|
||||
canonical_abi_free,
|
||||
canonical_abi_realloc,
|
||||
})
|
||||
let memory =
|
||||
self.lookup_core_def(instance, "memory")
|
||||
.unwrap_export(|i| match i {
|
||||
EntityIndex::Memory(i) => i,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let memory = self.runtime_memory(memory);
|
||||
ret.memory = Some(memory);
|
||||
|
||||
let realloc = self.lookup_core_def(instance, "canonical_abi_realloc");
|
||||
let realloc = self.runtime_realloc(realloc);
|
||||
ret.realloc = Some(realloc);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn runtime_import_index(&mut self, import: RuntimeImport) -> RuntimeImportIndex {
|
||||
if let Some(idx) = self.result.import_map.get(&import) {
|
||||
return *idx;
|
||||
}
|
||||
let idx = self
|
||||
.result
|
||||
.component
|
||||
.imports
|
||||
.push((import.source, import.exports.clone()));
|
||||
self.result.import_map.insert(import, idx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
fn runtime_memory(&mut self, export: CoreExport<MemoryIndex>) -> RuntimeMemoryIndex {
|
||||
if let Some(idx) = self.result.memory_to_runtime.get(&export) {
|
||||
return *idx;
|
||||
}
|
||||
let index = RuntimeMemoryIndex::from_u32(self.result.component.num_runtime_memories);
|
||||
self.result.component.num_runtime_memories += 1;
|
||||
self.result.memory_to_runtime.insert(export.clone(), index);
|
||||
self.result
|
||||
.component
|
||||
.initializers
|
||||
.push(Initializer::ExtractMemory { index, export });
|
||||
index
|
||||
}
|
||||
|
||||
fn runtime_realloc(&mut self, def: CoreDef) -> RuntimeReallocIndex {
|
||||
if let Some(idx) = self.result.realloc_to_runtime.get(&def) {
|
||||
return *idx;
|
||||
}
|
||||
let index = RuntimeReallocIndex::from_u32(self.result.component.num_runtime_reallocs);
|
||||
self.result.component.num_runtime_reallocs += 1;
|
||||
self.result.realloc_to_runtime.insert(def.clone(), index);
|
||||
self.result
|
||||
.component
|
||||
.initializers
|
||||
.push(Initializer::ExtractRealloc { index, def });
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreSource<'_> {
|
||||
@@ -706,4 +1053,28 @@ impl CoreSource<'_> {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn to_core_def(&self) -> CoreDef {
|
||||
self.to_core_export(|i| i).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreDef {
|
||||
fn unwrap_export<T>(self, get_index: impl FnOnce(EntityIndex) -> T) -> CoreExport<T> {
|
||||
let export = match self {
|
||||
CoreDef::Export(export) => export,
|
||||
CoreDef::Lowered(_) => unreachable!(),
|
||||
};
|
||||
let instance = export.instance;
|
||||
match export.item {
|
||||
ExportItem::Index(idx) => CoreExport {
|
||||
instance,
|
||||
item: ExportItem::Index(get_index(idx)),
|
||||
},
|
||||
ExportItem::Name(name) => CoreExport {
|
||||
instance,
|
||||
item: ExportItem::Name(name),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,12 +98,55 @@ indices! {
|
||||
/// modules, and this index indexes, at runtime, which of the upvars is
|
||||
/// referenced.
|
||||
pub struct ModuleUpvarIndex(u32);
|
||||
|
||||
/// Used to index imports into a `Component`
|
||||
///
|
||||
/// This does not correspond to anything in the binary format for the
|
||||
/// component model.
|
||||
pub struct ImportIndex(u32);
|
||||
|
||||
/// Index that represents a leaf item imported into a component where a
|
||||
/// "leaf" means "not an instance".
|
||||
///
|
||||
/// This does not correspond to anything in the binary format for the
|
||||
/// component model.
|
||||
pub struct RuntimeImportIndex(u32);
|
||||
|
||||
/// Index that represents a lowered host function and is used to represent
|
||||
/// host function lowerings with options and such.
|
||||
///
|
||||
/// This does not correspond to anything in the binary format for the
|
||||
/// component model.
|
||||
pub struct LoweredIndex(u32);
|
||||
|
||||
/// Index representing a linear memory extracted from a wasm instance
|
||||
/// which is stored in a `VMComponentContext`. This is used to deduplicate
|
||||
/// references to the same linear memory where it's only stored once in a
|
||||
/// `VMComponentContext`.
|
||||
///
|
||||
/// This does not correspond to anything in the binary format for the
|
||||
/// component model.
|
||||
pub struct RuntimeMemoryIndex(u32);
|
||||
|
||||
/// Same as `RuntimeMemoryIndex` except for the `realloc` function.
|
||||
pub struct RuntimeReallocIndex(u32);
|
||||
}
|
||||
|
||||
// Reexport for convenience some core-wasm indices which are also used in the
|
||||
// component model, typically for when aliasing exports of core wasm modules.
|
||||
pub use crate::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex};
|
||||
|
||||
/// Equivalent of `EntityIndex` but for the component model instead of core
|
||||
/// wasm.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ComponentItem {
|
||||
Func(FuncIndex),
|
||||
Module(ModuleIndex),
|
||||
Instance(InstanceIndex),
|
||||
Component(ComponentIndex),
|
||||
}
|
||||
|
||||
/// Runtime information about the type information contained within a component.
|
||||
///
|
||||
/// One of these is created per top-level component which describes all of the
|
||||
@@ -160,6 +203,18 @@ impl_index! {
|
||||
impl Index<ExpectedTypeIndex> for ComponentTypes { ExpectedType => expecteds }
|
||||
}
|
||||
|
||||
// Additionally forward anything that can index `ModuleTypes` to `ModuleTypes`
|
||||
// (aka `SignatureIndex`)
|
||||
impl<T> Index<T> for ComponentTypes
|
||||
where
|
||||
ModuleTypes: Index<T>,
|
||||
{
|
||||
type Output = <ModuleTypes as Index<T>>::Output;
|
||||
fn index(&self, idx: T) -> &Self::Output {
|
||||
self.module_types.index(idx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Structured used to build a [`ComponentTypes`] during translation.
|
||||
///
|
||||
/// This contains tables to intern any component types found as well as
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::component::instance::lookup;
|
||||
use crate::component::instance::InstanceData;
|
||||
use crate::store::{StoreOpaque, Stored};
|
||||
use crate::{AsContext, StoreContextMut};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::component::{
|
||||
ComponentTypes, FuncTypeIndex, LiftedFunction, RuntimeInstanceIndex, StringEncoding,
|
||||
CanonicalOptions, ComponentTypes, CoreExport, FuncTypeIndex, StringEncoding,
|
||||
};
|
||||
use wasmtime_environ::PrimaryMap;
|
||||
use wasmtime_environ::FuncIndex;
|
||||
use wasmtime_runtime::{Export, ExportFunction, ExportMemory, VMTrampoline};
|
||||
|
||||
mod typed;
|
||||
@@ -36,50 +36,35 @@ pub(crate) struct Options {
|
||||
struct Intrinsics {
|
||||
memory: ExportMemory,
|
||||
realloc: ExportFunction,
|
||||
#[allow(dead_code)] // FIXME: remove this when actually used
|
||||
free: ExportFunction,
|
||||
}
|
||||
|
||||
impl Func {
|
||||
pub(crate) fn from_lifted_func(
|
||||
store: &mut StoreOpaque,
|
||||
types: &Arc<ComponentTypes>,
|
||||
instances: &PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
|
||||
func: &LiftedFunction,
|
||||
instance: &InstanceData,
|
||||
ty: FuncTypeIndex,
|
||||
func: &CoreExport<FuncIndex>,
|
||||
options: &CanonicalOptions,
|
||||
) -> Func {
|
||||
let export = match lookup(store, instances, &func.func) {
|
||||
let export = match instance.lookup_export(store, func) {
|
||||
Export::Function(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let trampoline = store.lookup_trampoline(unsafe { export.anyfunc.as_ref() });
|
||||
let intrinsics = func.options.intrinsics.as_ref().map(|i| {
|
||||
let memory = match lookup(store, instances, &i.memory) {
|
||||
Export::Memory(m) => m,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let realloc = match lookup(store, instances, &i.canonical_abi_realloc) {
|
||||
Export::Function(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let free = match lookup(store, instances, &i.canonical_abi_free) {
|
||||
Export::Function(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Intrinsics {
|
||||
memory,
|
||||
realloc,
|
||||
free,
|
||||
}
|
||||
let intrinsics = options.memory.map(|i| {
|
||||
let memory = instance.runtime_memory(i);
|
||||
let realloc = instance.runtime_realloc(options.realloc.unwrap());
|
||||
Intrinsics { memory, realloc }
|
||||
});
|
||||
Func(store.store_data_mut().insert(FuncData {
|
||||
trampoline,
|
||||
export,
|
||||
options: Options {
|
||||
intrinsics,
|
||||
string_encoding: func.options.string_encoding,
|
||||
string_encoding: options.string_encoding,
|
||||
},
|
||||
ty: func.ty,
|
||||
types: types.clone(),
|
||||
ty,
|
||||
types: instance.component_types().clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ use crate::instance::OwnedImports;
|
||||
use crate::store::{StoreOpaque, Stored};
|
||||
use crate::{AsContextMut, Module, StoreContextMut};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::component::{
|
||||
CoreExport, Export, ExportItem, Instantiation, RuntimeInstanceIndex,
|
||||
ComponentTypes, CoreDef, CoreExport, Export, ExportItem, Initializer, ModuleToInstantiate,
|
||||
RuntimeInstanceIndex, RuntimeMemoryIndex, RuntimeReallocIndex,
|
||||
};
|
||||
use wasmtime_environ::{EntityIndex, PrimaryMap};
|
||||
|
||||
@@ -24,6 +26,10 @@ pub(crate) struct InstanceData {
|
||||
// alive and things like that, instead only the bare minimum necessary
|
||||
// should be kept alive here (mostly just `wasmtime_environ::Component`.
|
||||
component: Component,
|
||||
|
||||
// TODO: move these to `VMComponentContext`
|
||||
memories: PrimaryMap<RuntimeMemoryIndex, wasmtime_runtime::ExportMemory>,
|
||||
reallocs: PrimaryMap<RuntimeReallocIndex, wasmtime_runtime::ExportFunction>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
@@ -38,10 +44,7 @@ impl Instance {
|
||||
let mut instantiator = Instantiator::new(component);
|
||||
instantiator.run(&mut store)?;
|
||||
|
||||
let data = Box::new(InstanceData {
|
||||
instances: instantiator.instances,
|
||||
component: component.clone(),
|
||||
});
|
||||
let data = Box::new(instantiator.data);
|
||||
Ok(Instance(store.0.store_data_mut().insert(Some(data))))
|
||||
}
|
||||
|
||||
@@ -103,19 +106,56 @@ impl Instance {
|
||||
impl InstanceData {
|
||||
fn get_func(&self, store: &mut StoreOpaque, name: &str) -> Option<Func> {
|
||||
match self.component.env_component().exports.get(name)? {
|
||||
Export::LiftedFunction(func) => Some(Func::from_lifted_func(
|
||||
store,
|
||||
self.component.types(),
|
||||
&self.instances,
|
||||
func,
|
||||
)),
|
||||
Export::LiftedFunction { ty, func, options } => {
|
||||
Some(Func::from_lifted_func(store, self, *ty, func, options))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_def(&self, store: &mut StoreOpaque, item: &CoreDef) -> wasmtime_runtime::Export {
|
||||
match item {
|
||||
CoreDef::Lowered(_) => unimplemented!(),
|
||||
CoreDef::Export(e) => self.lookup_export(store, e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_export<T>(
|
||||
&self,
|
||||
store: &mut StoreOpaque,
|
||||
item: &CoreExport<T>,
|
||||
) -> wasmtime_runtime::Export
|
||||
where
|
||||
T: Copy + Into<EntityIndex>,
|
||||
{
|
||||
let instance = &self.instances[item.instance];
|
||||
let id = instance.id(store);
|
||||
let instance = store.instance_mut(id);
|
||||
let idx = match &item.item {
|
||||
ExportItem::Index(idx) => (*idx).into(),
|
||||
ExportItem::Name(name) => instance.module().exports[name],
|
||||
};
|
||||
instance.get_export_by_index(idx)
|
||||
}
|
||||
|
||||
pub fn component_types(&self) -> &Arc<ComponentTypes> {
|
||||
self.component.types()
|
||||
}
|
||||
|
||||
pub fn runtime_memory(&self, memory: RuntimeMemoryIndex) -> wasmtime_runtime::ExportMemory {
|
||||
self.memories[memory].clone()
|
||||
}
|
||||
|
||||
pub fn runtime_realloc(
|
||||
&self,
|
||||
realloc: RuntimeReallocIndex,
|
||||
) -> wasmtime_runtime::ExportFunction {
|
||||
self.reallocs[realloc].clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct Instantiator<'a> {
|
||||
component: &'a Component,
|
||||
instances: PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
|
||||
data: InstanceData,
|
||||
imports: OwnedImports,
|
||||
}
|
||||
|
||||
@@ -127,33 +167,61 @@ impl<'a> Instantiator<'a> {
|
||||
}
|
||||
Instantiator {
|
||||
component,
|
||||
instances: PrimaryMap::with_capacity(env_component.instances.len()),
|
||||
imports: OwnedImports::empty(),
|
||||
data: InstanceData {
|
||||
instances: PrimaryMap::with_capacity(env_component.num_runtime_instances as usize),
|
||||
component: component.clone(),
|
||||
memories: Default::default(),
|
||||
reallocs: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn run<T>(&mut self, store: &mut StoreContextMut<'_, T>) -> Result<()> {
|
||||
let env_component = self.component.env_component();
|
||||
for (index, instantiation) in env_component.instances.iter() {
|
||||
let (module, args) = match instantiation {
|
||||
Instantiation::ModuleUpvar { module, args } => {
|
||||
(self.component.upvar(*module), args)
|
||||
}
|
||||
Instantiation::ModuleImport { import_index, args } => {
|
||||
drop((import_index, args));
|
||||
unimplemented!("component module imports");
|
||||
}
|
||||
};
|
||||
for initializer in env_component.initializers.iter() {
|
||||
match initializer {
|
||||
Initializer::InstantiateModule {
|
||||
instance,
|
||||
module,
|
||||
args,
|
||||
} => {
|
||||
let module = match module {
|
||||
ModuleToInstantiate::Upvar(module) => self.component.upvar(*module),
|
||||
ModuleToInstantiate::Import(idx) => {
|
||||
drop(idx);
|
||||
unimplemented!("component module imports");
|
||||
}
|
||||
};
|
||||
|
||||
// Note that the unsafety here should be ok because the
|
||||
// validity of the component means that type-checks have
|
||||
// already been performed. This maens that the unsafety due
|
||||
// to imports having the wrong type should not happen here.
|
||||
let imports = self.build_imports(store.0, module, args);
|
||||
let instance =
|
||||
unsafe { crate::Instance::new_started(store, module, imports.as_ref())? };
|
||||
let idx = self.instances.push(instance);
|
||||
assert_eq!(idx, index);
|
||||
// Note that the unsafety here should be ok because the
|
||||
// validity of the component means that type-checks have
|
||||
// already been performed. This maens that the unsafety due
|
||||
// to imports having the wrong type should not happen here.
|
||||
let imports = self.build_imports(store.0, module, args);
|
||||
let i =
|
||||
unsafe { crate::Instance::new_started(store, module, imports.as_ref())? };
|
||||
let idx = self.data.instances.push(i);
|
||||
assert_eq!(idx, *instance);
|
||||
}
|
||||
Initializer::LowerImport(_) => unimplemented!(),
|
||||
|
||||
Initializer::ExtractMemory { index, export } => {
|
||||
let memory = match self.data.lookup_export(store.0, export) {
|
||||
wasmtime_runtime::Export::Memory(m) => m,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_eq!(*index, self.data.memories.push(memory));
|
||||
}
|
||||
|
||||
Initializer::ExtractRealloc { index, def } => {
|
||||
let func = match self.data.lookup_def(store.0, def) {
|
||||
wasmtime_runtime::Export::Function(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_eq!(*index, self.data.reallocs.push(func));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -162,13 +230,13 @@ impl<'a> Instantiator<'a> {
|
||||
&mut self,
|
||||
store: &mut StoreOpaque,
|
||||
module: &Module,
|
||||
args: &[CoreExport<EntityIndex>],
|
||||
args: &[CoreDef],
|
||||
) -> &OwnedImports {
|
||||
self.imports.clear();
|
||||
self.imports.reserve(module);
|
||||
|
||||
for arg in args {
|
||||
let export = lookup(store, &self.instances, arg);
|
||||
let export = self.data.lookup_def(store, arg);
|
||||
|
||||
// The unsafety here should be ok since the `export` is loaded
|
||||
// directly from an instance which should only give us valid export
|
||||
@@ -181,21 +249,3 @@ impl<'a> Instantiator<'a> {
|
||||
&self.imports
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lookup<T>(
|
||||
store: &mut StoreOpaque,
|
||||
instances: &PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
|
||||
item: &CoreExport<T>,
|
||||
) -> wasmtime_runtime::Export
|
||||
where
|
||||
T: Copy + Into<EntityIndex>,
|
||||
{
|
||||
let instance = &instances[item.instance];
|
||||
let id = instance.id(store);
|
||||
let instance = store.instance_mut(id);
|
||||
let idx = match &item.item {
|
||||
ExportItem::Index(idx) => (*idx).into(),
|
||||
ExportItem::Name(name) => instance.module().exports[name],
|
||||
};
|
||||
instance.get_export_by_index(idx)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user