Add types and methods to provide updated API

This commit is contained in:
Mark McCaskey
2020-03-17 15:27:11 -07:00
parent 4746d04c44
commit f4dc419b5e
4 changed files with 380 additions and 15 deletions

View File

@ -1,5 +1,7 @@
//! The module module contains the implementation data structures and helper functions used to
//! manipulate and access wasm modules.
//! This module contains the types to manipulate and access Wasm modules.
//!
//! A Wasm module is the artifact of compiling WebAssembly. Wasm modules are not executable
//! until they're instantiated with imports (via [`ImportObject`]).
use crate::{
backend::RunnableModule,
cache::{Artifact, Error as CacheError},
@ -7,10 +9,10 @@ use crate::{
import::ImportObject,
structures::{Map, TypedIndex},
types::{
FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer,
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor, MemoryIndex,
SigIndex, TableDescriptor, TableIndex,
ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit,
ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex,
Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor,
MemoryIndex, SigIndex, TableDescriptor, TableIndex, Type,
},
Instance,
};
@ -163,6 +165,136 @@ impl Module {
pub fn info(&self) -> &ModuleInfo {
&self.inner.info
}
/// Iterate over the exports that this module provides.
///
/// TODO: show example here
pub fn exports(&self) -> impl Iterator<Item = ExportDescriptor> + '_ {
self.inner
.info
.exports
.iter()
.map(|(name, &ei)| ExportDescriptor {
name: name.clone(),
kind: ei.into(),
})
}
/// Get the [`Import`]s this [`Module`] requires to be instantiated.
pub fn imports(&self) -> Vec<Import> {
let mut out = Vec::with_capacity(
self.inner.info.imported_functions.len()
+ self.inner.info.imported_memories.len()
+ self.inner.info.imported_tables.len()
+ self.inner.info.imported_globals.len(),
);
/// Lookup the (namespace, name) in the [`ModuleInfo`] by index.
fn get_import_name(
info: &ModuleInfo,
&ImportName {
namespace_index,
name_index,
}: &ImportName,
) -> (String, String) {
let namespace = info.namespace_table.get(namespace_index).to_string();
let name = info.name_table.get(name_index).to_string();
(namespace, name)
}
let info = &self.inner.info;
let imported_functions = info.imported_functions.values().map(|import_name| {
let (namespace, name) = get_import_name(info, import_name);
Import {
namespace,
name,
type_: ImportType::Function,
}
});
let imported_memories =
info.imported_memories
.values()
.map(|(import_name, memory_descriptor)| {
let (namespace, name) = get_import_name(info, import_name);
Import {
namespace,
name,
type_: memory_descriptor.into(),
}
});
let imported_tables =
info.imported_tables
.values()
.map(|(import_name, table_descriptor)| {
let (namespace, name) = get_import_name(info, import_name);
Import {
namespace,
name,
type_: table_descriptor.into(),
}
});
let imported_globals =
info.imported_tables
.values()
.map(|(import_name, global_descriptor)| {
let (namespace, name) = get_import_name(info, import_name);
Import {
namespace,
name,
type_: global_descriptor.into(),
}
});
out.extend(imported_functions);
out.extend(imported_memories);
out.extend(imported_tables);
out.extend(imported_globals);
out
}
/// Find the custom section(s?) matching the given name.
// TODO: JS API returns `Vec<&[u8]>` here
pub fn custom_section(&self, key: impl AsRef<str>) -> Option<&[u8]> {
let key = key.as_ref();
self.inner.info.custom_sections.get(key).map(|v| v.as_ref())
}
}
// TODO: review this vs `ExportIndex`
/// Type describing an export that the [`Module`] provides.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExportDescriptor {
/// The name identifying the export.
pub name: String,
/// The type of the export.
pub kind: ExportKind,
}
// TODO: kind vs type
/// Tag indicating the type of the export.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ExportKind {
/// The export is a function.
Function,
/// The export is a global variable.
Global,
/// The export is a linear memory.
Memory,
/// The export is a table.
Table,
}
impl From<ExportIndex> for ExportKind {
fn from(other: ExportIndex) -> Self {
match other {
ExportIndex::Func(_) => Self::Function,
ExportIndex::Global(_) => Self::Global,
ExportIndex::Memory(_) => Self::Memory,
ExportIndex::Table(_) => Self::Table,
}
}
}
impl Clone for Module {
@ -173,6 +305,107 @@ impl Clone for Module {
}
}
/// The type of import. This indicates whether the import is a function, global, memory, or table.
// TODO: discuss and research Kind vs Type;
// `Kind` has meaning to me from Haskell as an incomplete type like
// `List` which is of kind `* -> *`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImportType {
/// The import is a function.
// TODO: why does function have no data?
Function,
/// The import is a global variable.
Global {
/// Whether or not the variable can be mutated.
mutable: bool,
/// The Wasm type that the global variable holds.
// TODO: attempt to understand explanation about 128bit globals:
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#webassembly-module-instatiation
type_: Type,
},
/// A Wasm linear memory.
// TODO: discuss using `Pages` here vs u32
Memory {
/// The minimum number of pages this memory must have.
minimum_pages: u32,
/// The maximum number of pages this memory can have.
maximum_pages: Option<u32>,
// TODO: missing fields, `shared`, `memory_type`
},
/// A Wasm table.
Table {
/// The minimum number of elements this table must have.
minimum_elements: u32,
/// The maximum number of elements this table can have.
maximum_elements: Option<u32>,
/// The type that this table contains
element_type: ElementType,
},
}
impl From<MemoryDescriptor> for ImportType {
fn from(other: MemoryDescriptor) -> Self {
ImportType::Memory {
minimum_pages: other.minimum.0,
maximum_pages: other.maximum.map(|inner| inner.0),
}
}
}
impl From<&MemoryDescriptor> for ImportType {
fn from(other: &MemoryDescriptor) -> Self {
ImportType::Memory {
minimum_pages: other.minimum.0,
maximum_pages: other.maximum.map(|inner| inner.0),
}
}
}
impl From<TableDescriptor> for ImportType {
fn from(other: TableDescriptor) -> Self {
ImportType::Table {
minimum_elements: other.minimum,
maximum_elements: other.maximum,
element_type: other.element,
}
}
}
impl From<&TableDescriptor> for ImportType {
fn from(other: &TableDescriptor) -> Self {
ImportType::Table {
minimum_elements: other.minimum,
maximum_elements: other.maximum,
element_type: other.element,
}
}
}
impl From<GlobalDescriptor> for ImportType {
fn from(other: GlobalDescriptor) -> Self {
ImportType::Global {
mutable: other.mutable,
type_: other.ty,
}
}
}
impl From<&GlobalDescriptor> for ImportType {
fn from(other: &GlobalDescriptor) -> Self {
ImportType::Global {
mutable: other.mutable,
type_: other.ty,
}
}
}
/// A type describing an import that a [`Module`] needs to be instantiated.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Import {
/// The namespace that this import is in.
pub namespace: String,
/// The name of the import.
pub name: String,
/// The type of the import.
pub type_: ImportType,
}
impl ModuleInner {}
#[doc(hidden)]