mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-26 07:01:33 +00:00
Add types and methods to provide updated API
This commit is contained in:
@ -2,15 +2,44 @@ use std::{convert::TryInto, sync::Arc};
|
|||||||
use wasmer_runtime_core::{
|
use wasmer_runtime_core::{
|
||||||
compile_with,
|
compile_with,
|
||||||
error::RuntimeError,
|
error::RuntimeError,
|
||||||
|
global::Global,
|
||||||
imports,
|
imports,
|
||||||
memory::Memory,
|
memory::Memory,
|
||||||
typed_func::{DynamicFunc, Func},
|
typed_func::{DynamicFunc, Func},
|
||||||
types::{FuncSig, MemoryDescriptor, Type, Value},
|
types::{FuncSig, MemoryDescriptor, Type, Value},
|
||||||
units::Pages,
|
units::Pages,
|
||||||
vm, Instance,
|
vm, DynFunc, Instance,
|
||||||
};
|
};
|
||||||
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
|
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_api_works() {
|
||||||
|
let wasm = r#"
|
||||||
|
(module
|
||||||
|
(type $type (func (param i32) (result i32)))
|
||||||
|
(global (export "my_global") i32 (i32.const 45))
|
||||||
|
(func (export "add_one") (type $type)
|
||||||
|
(i32.add (get_local 0)
|
||||||
|
(i32.const 1)))
|
||||||
|
(func (export "double") (type $type)
|
||||||
|
(i32.mul (get_local 0)
|
||||||
|
(i32.const 2)))
|
||||||
|
)"#;
|
||||||
|
let wasm_binary = wat2wasm(wasm.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
|
||||||
|
let import_object = imports! {};
|
||||||
|
let instance = module.instantiate(&import_object).unwrap();
|
||||||
|
|
||||||
|
let my_global: Global = instance.exports_new().get("my_global").unwrap();
|
||||||
|
assert_eq!(my_global.get(), Value::I32(45));
|
||||||
|
let double: Func<i32, i32> = instance.exports_new().get("double").unwrap();
|
||||||
|
assert_eq!(double.call(5).unwrap(), 10);
|
||||||
|
let add_one: DynFunc = instance.exports_new().get("add_one").unwrap();
|
||||||
|
assert_eq!(add_one.call(&[Value::I32(5)]).unwrap(), &[Value::I32(6)]);
|
||||||
|
let add_one_memory: Option<DynFunc> = instance.exports_new().get("my_global");
|
||||||
|
assert!(add_one_memory.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! call_and_assert {
|
macro_rules! call_and_assert {
|
||||||
($instance:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
|
($instance:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
|
||||||
#[allow(unused_parens)]
|
#[allow(unused_parens)]
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
//! The export module contains the implementation data structures and helper functions used to
|
//! This module contains types to manipulate and access a Wasm module's exports
|
||||||
//! manipulate and access a wasm module's exports including memories, tables, globals, and
|
//! including memories, tables, globals, and functions.
|
||||||
//! functions.
|
|
||||||
use crate::{
|
use crate::{
|
||||||
global::Global, instance::InstanceInner, memory::Memory, module::ExportIndex,
|
global::Global,
|
||||||
module::ModuleInner, table::Table, types::FuncSig, vm,
|
instance::{Instance, InstanceInner},
|
||||||
|
memory::Memory,
|
||||||
|
module::ExportIndex,
|
||||||
|
module::ModuleInner,
|
||||||
|
table::Table,
|
||||||
|
types::FuncSig,
|
||||||
|
vm,
|
||||||
};
|
};
|
||||||
use indexmap::map::Iter as IndexMapIter;
|
use indexmap::map::Iter as IndexMapIter;
|
||||||
use std::{ptr::NonNull, sync::Arc};
|
use std::{ptr::NonNull, sync::Arc};
|
||||||
@ -92,3 +97,10 @@ impl<'a> Iterator for ExportIter<'a> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This trait is used to mark types as gettable from an [`Instance`].
|
||||||
|
pub trait Exportable<'a>: Sized {
|
||||||
|
/// Implementation of how to get the export corresponding to the implementing type
|
||||||
|
/// from an [`Instance`] by name.
|
||||||
|
fn get_self(instance: &'a Instance, name: &str) -> Option<Self>;
|
||||||
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
//! The instance module contains the implementation data structures and helper functions used to
|
//! This module contains types for manipulating and accessing Wasm instances.
|
||||||
//! manipulate and access wasm instances.
|
//!
|
||||||
|
//! An "instance", or "instantiated module", is a compiled WebAssembly [`Module`] with its
|
||||||
|
//! corresponding imports (via [`ImportObject`]) that is ready to execute.
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::RunnableModule,
|
backend::RunnableModule,
|
||||||
backing::{ImportBacking, LocalBacking},
|
backing::{ImportBacking, LocalBacking},
|
||||||
error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError},
|
error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError},
|
||||||
export::{Context, Export, ExportIter, FuncPointer},
|
export::{Context, Export, ExportIter, Exportable, FuncPointer},
|
||||||
global::Global,
|
global::Global,
|
||||||
import::{ImportObject, LikeNamespace},
|
import::{ImportObject, LikeNamespace},
|
||||||
loader::Loader,
|
loader::Loader,
|
||||||
@ -19,6 +21,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
mem,
|
mem,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
ptr::{self, NonNull},
|
ptr::{self, NonNull},
|
||||||
@ -58,6 +61,11 @@ pub struct Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
|
/// Access the new exports API.
|
||||||
|
/// TODO: rename etc.
|
||||||
|
pub fn exports_new(&self) -> Exports {
|
||||||
|
Exports { instance: self }
|
||||||
|
}
|
||||||
pub(crate) fn new(module: Arc<ModuleInner>, imports: &ImportObject) -> Result<Instance> {
|
pub(crate) fn new(module: Arc<ModuleInner>, imports: &ImportObject) -> Result<Instance> {
|
||||||
// We need the backing and import_backing to create a vm::Ctx, but we need
|
// We need the backing and import_backing to create a vm::Ctx, but we need
|
||||||
// a vm::Ctx to create a backing and an import_backing. The solution is to create an
|
// a vm::Ctx to create a backing and an import_backing. The solution is to create an
|
||||||
@ -814,6 +822,89 @@ impl<'a> DynFunc<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Exportable<'a> for Memory {
|
||||||
|
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
|
||||||
|
let export_index = instance.module.info.exports.get(name)?;
|
||||||
|
if let ExportIndex::Memory(idx) = export_index {
|
||||||
|
let module = instance.module.borrow();
|
||||||
|
Some(instance.inner.get_memory_from_index(module, *idx))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Exportable<'a> for Table {
|
||||||
|
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
|
||||||
|
let export_index = instance.module.info.exports.get(name)?;
|
||||||
|
if let ExportIndex::Table(idx) = export_index {
|
||||||
|
let module = instance.module.borrow();
|
||||||
|
Some(instance.inner.get_table_from_index(module, *idx))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Exportable<'a> for Global {
|
||||||
|
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
|
||||||
|
let export_index = instance.module.info.exports.get(name)?;
|
||||||
|
if let ExportIndex::Global(idx) = export_index {
|
||||||
|
let module = instance.module.borrow();
|
||||||
|
Some(instance.inner.get_global_from_index(module, *idx))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Exportable<'a> for DynFunc<'a> {
|
||||||
|
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
|
||||||
|
instance.dyn_func(name).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Args: WasmTypeList, Rets: WasmTypeList> Exportable<'a> for Func<'a, Args, Rets, Wasm> {
|
||||||
|
fn get_self(instance: &'a Instance, name: &str) -> Option<Self> {
|
||||||
|
instance.func(name).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Exports` is used to get exports like [`Func`]s, [`DynFunc`]s, [`Memory`]s,
|
||||||
|
/// [`Global`]s, and [`Table`]s from an [`Instance`].
|
||||||
|
///
|
||||||
|
/// Use [`Instance::exports_new`] to get an `Exports` from an [`Instance`].
|
||||||
|
pub struct Exports<'a> {
|
||||||
|
instance: &'a Instance,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Exports<'a> {
|
||||||
|
/// Get an export.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use wasmer_runtime_core::{DynFunc, Func, Instance};
|
||||||
|
/// # use wasmer_runtime_core::global::Global;
|
||||||
|
/// # use wasmer_runtime_core::types::Value;
|
||||||
|
/// # fn example_fn(instance: &Instance) -> Option<()> {
|
||||||
|
/// // We can get a function as a static `Func`
|
||||||
|
/// let func: Func<i32, i32> = instance.exports_new().get("my_func")?;
|
||||||
|
/// let _result = func.call(42);
|
||||||
|
///
|
||||||
|
/// // Or we can get it as a dynamic `DynFunc`
|
||||||
|
/// let dyn_func: DynFunc = instance.exports_new().get("my_func")?;
|
||||||
|
/// let _result= dyn_func.call(&[Value::I32(42)]);
|
||||||
|
///
|
||||||
|
/// // We can also get other exports like `Global`s, `Memory`s, and `Table`s
|
||||||
|
/// let _counter: Global = instance.exports_new().get("counter")?;
|
||||||
|
///
|
||||||
|
/// # Some(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn get<T: Exportable<'a>>(&self, name: &str) -> Option<T> {
|
||||||
|
T::get_self(self.instance, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! The module module contains the implementation data structures and helper functions used to
|
//! This module contains the types to manipulate and access Wasm modules.
|
||||||
//! 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::{
|
use crate::{
|
||||||
backend::RunnableModule,
|
backend::RunnableModule,
|
||||||
cache::{Artifact, Error as CacheError},
|
cache::{Artifact, Error as CacheError},
|
||||||
@ -7,10 +9,10 @@ use crate::{
|
|||||||
import::ImportObject,
|
import::ImportObject,
|
||||||
structures::{Map, TypedIndex},
|
structures::{Map, TypedIndex},
|
||||||
types::{
|
types::{
|
||||||
FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
|
ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit,
|
||||||
ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer,
|
ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex,
|
||||||
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor, MemoryIndex,
|
Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor,
|
||||||
SigIndex, TableDescriptor, TableIndex,
|
MemoryIndex, SigIndex, TableDescriptor, TableIndex, Type,
|
||||||
},
|
},
|
||||||
Instance,
|
Instance,
|
||||||
};
|
};
|
||||||
@ -163,6 +165,136 @@ impl Module {
|
|||||||
pub fn info(&self) -> &ModuleInfo {
|
pub fn info(&self) -> &ModuleInfo {
|
||||||
&self.inner.info
|
&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 {
|
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 {}
|
impl ModuleInner {}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
Reference in New Issue
Block a user