Update support for the module linking proposal
This commit updates the various tooling used by wasmtime which has new updates to the module linking proposal. This is done primarily to sync with WebAssembly/module-linking#26. The main change implemented here is that wasmtime now supports creating instances from a set of values, nott just from instantiating a module. Additionally subtyping handling of modules with respect to imports is now properly handled by desugaring two-level imports to imports of instances. A number of small refactorings are included here as well, but most of them are in accordance with the changes to `wasmparser` and the updated binary format for module linking.
This commit is contained in:
50
Cargo.lock
generated
50
Cargo.lock
generated
@@ -388,7 +388,7 @@ dependencies = [
|
||||
"souper-ir",
|
||||
"target-lexicon",
|
||||
"thiserror",
|
||||
"wast 29.0.0",
|
||||
"wast 31.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1393,7 +1393,7 @@ dependencies = [
|
||||
"peepmatic-test-operator",
|
||||
"peepmatic-traits",
|
||||
"serde",
|
||||
"wast 29.0.0",
|
||||
"wast 31.0.0",
|
||||
"z3",
|
||||
]
|
||||
|
||||
@@ -1421,7 +1421,7 @@ dependencies = [
|
||||
"peepmatic-traits",
|
||||
"rand",
|
||||
"serde",
|
||||
"wast 29.0.0",
|
||||
"wast 31.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1446,7 +1446,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_test",
|
||||
"thiserror",
|
||||
"wast 29.0.0",
|
||||
"wast 31.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1458,7 +1458,7 @@ dependencies = [
|
||||
"peepmatic",
|
||||
"peepmatic-test-operator",
|
||||
"souper-ir",
|
||||
"wast 29.0.0",
|
||||
"wast 31.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1479,7 +1479,7 @@ version = "0.69.0"
|
||||
dependencies = [
|
||||
"peepmatic-traits",
|
||||
"serde",
|
||||
"wast 29.0.0",
|
||||
"wast 31.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2350,20 +2350,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.2.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed89eaf99e08b84f96e477a16588a07dd3b51dc5f07291c3706782f62a10a5e1"
|
||||
checksum = "c75fa62cf1464aa6655479ae454202a159cc82b7b4d66e8f174409669c0654c5"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-smith"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "509904d9c4c4659ac238a3f27c3656dd6d3931697eddd4b0f32e335769c298d0"
|
||||
checksum = "0b9b9b796bf4da5eb0523b136d6f0cc9a59c16a66ece8e0d5a14a9cdccf2864e"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"indexmap",
|
||||
"leb128",
|
||||
"wasm-encoder",
|
||||
]
|
||||
@@ -2394,15 +2395,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.71.0"
|
||||
version = "0.72.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a30c99437829ede826802bfcf28500cf58df00e66cb9114df98813bc145ff1"
|
||||
checksum = "2cdf4d872d407f9fb44956e540582eeaf0dc4fb8142f1f0f64e2c37196bada01"
|
||||
|
||||
[[package]]
|
||||
name = "wasmprinter"
|
||||
version = "0.2.18"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0515db67c610037f3c53ec36976edfd1eb01bac6b1226914b17ce609480e729f"
|
||||
checksum = "a0c139586e3b80b899f5aaaa3720c7a236ea9073e315292e01d099da12efacc5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"wasmparser",
|
||||
@@ -2754,7 +2755,7 @@ version = "0.22.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"wasmtime",
|
||||
"wast 29.0.0",
|
||||
"wast 31.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2790,29 +2791,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "29.0.0"
|
||||
version = "31.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf2268937131d63c3d833242bf5e075406f9ed868b4265f3280e15dac29ac18"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "30.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b79907b22f740634810e882d8d1d9d0f9563095a8ab94e786e370242bff5cd2"
|
||||
checksum = "9beb1f6b63f08c523a1e8e76fc70058af4d2a34ef1c504f56cdac7b6970228b9"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.0.31"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8279a02835bf12e61ed2b3c3cbc6ecf9918762fd97e036917c11a09ec20ca44"
|
||||
checksum = "2a0b3044da73d3b84a822d955afad356759b2fee454b6882722008dace80b68e"
|
||||
dependencies = [
|
||||
"wast 30.0.0",
|
||||
"wast 31.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -38,12 +38,12 @@ anyhow = "1.0.19"
|
||||
target-lexicon = { version = "0.11.0", default-features = false }
|
||||
pretty_env_logger = "0.4.0"
|
||||
file-per-thread-logger = "0.1.1"
|
||||
wat = "1.0.30"
|
||||
wat = "1.0.32"
|
||||
libc = "0.2.60"
|
||||
log = "0.4.8"
|
||||
rayon = "1.2.1"
|
||||
humantime = "2.0.0"
|
||||
wasmparser = "0.71.0"
|
||||
wasmparser = "0.72.0"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.8.1"
|
||||
|
||||
@@ -30,7 +30,7 @@ peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, versi
|
||||
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.69.0" }
|
||||
regalloc = { version = "0.0.31" }
|
||||
souper-ir = { version = "2.1.0", optional = true }
|
||||
wast = { version = "29.0.0", optional = true }
|
||||
wast = { version = "31.0.0", optional = true }
|
||||
# It is a goal of the cranelift-codegen crate to have minimal external dependencies.
|
||||
# Please don't add any unless they are essential to the task of creating binary
|
||||
# machine code. Integration tests that need external dependencies can be
|
||||
|
||||
@@ -15,7 +15,7 @@ peepmatic-macro = { version = "0.69.0", path = "crates/macro" }
|
||||
peepmatic-runtime = { version = "0.69.0", path = "crates/runtime", features = ["construct"] }
|
||||
peepmatic-traits = { version = "0.69.0", path = "crates/traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
wast = "29.0.0"
|
||||
wast = "31.0.0"
|
||||
z3 = { version = "0.7.1", features = ["static-link-z3"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -21,4 +21,4 @@ peepmatic-test-operator = { path = "../test-operator" }
|
||||
peepmatic-traits = { path = "../traits" }
|
||||
rand = { version = "0.7.3", features = ["small_rng"] }
|
||||
serde = "1.0.106"
|
||||
wast = "29.0.0"
|
||||
wast = "31.0.0"
|
||||
|
||||
@@ -16,7 +16,7 @@ peepmatic-automata = { version = "0.69.0", path = "../automata", features = ["se
|
||||
peepmatic-traits = { version = "0.69.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
thiserror = "1.0.15"
|
||||
wast = { version = "29.0.0", optional = true }
|
||||
wast = { version = "31.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
peepmatic-test-operator = { version = "0.69.0", path = "../test-operator" }
|
||||
|
||||
@@ -16,4 +16,4 @@ log = "0.4.8"
|
||||
[dev-dependencies]
|
||||
peepmatic = { path = "../..", version = "0.69.0" }
|
||||
peepmatic-test-operator = { version = "0.69.0", path = "../test-operator" }
|
||||
wast = "29.0.0"
|
||||
wast = "31.0.0"
|
||||
|
||||
@@ -11,4 +11,4 @@ edition = "2018"
|
||||
[dependencies]
|
||||
peepmatic-traits = { version = "0.69.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
wast = "29.0.0"
|
||||
wast = "31.0.0"
|
||||
|
||||
@@ -12,7 +12,7 @@ keywords = ["webassembly", "wasm"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmparser = { version = "0.71", default-features = false }
|
||||
wasmparser = { version = "0.72", default-features = false }
|
||||
cranelift-codegen = { path = "../codegen", version = "0.69.0", default-features = false }
|
||||
cranelift-entity = { path = "../entity", version = "0.69.0" }
|
||||
cranelift-frontend = { path = "../frontend", version = "0.69.0", default-features = false }
|
||||
|
||||
@@ -205,25 +205,34 @@ pub enum ReturnMode {
|
||||
|
||||
/// An entry in the alias section of a wasm module (from the module linking
|
||||
/// proposal)
|
||||
pub enum Alias {
|
||||
/// A parent's module is being aliased into our own index space.
|
||||
///
|
||||
/// Note that the index here is in the parent's index space, not our own.
|
||||
ParentModule(ModuleIndex),
|
||||
pub enum Alias<'a> {
|
||||
/// An outer module's module is being aliased into our own index space.
|
||||
OuterModule {
|
||||
/// The number of modules above us that we're referencing.
|
||||
relative_depth: u32,
|
||||
/// The module index in the outer module's index space we're referencing.
|
||||
index: ModuleIndex,
|
||||
},
|
||||
|
||||
/// A parent's type is being aliased into our own index space
|
||||
/// An outer module's type is being aliased into our own index space
|
||||
///
|
||||
/// Note that the index here is in the parent's index space, not our own.
|
||||
ParentType(TypeIndex),
|
||||
/// Note that the index here is in the outer module's index space, not our
|
||||
/// own.
|
||||
OuterType {
|
||||
/// The number of modules above us that we're referencing.
|
||||
relative_depth: u32,
|
||||
/// The type index in the outer module's index space we're referencing.
|
||||
index: TypeIndex,
|
||||
},
|
||||
|
||||
/// A previously created instance is having one of its exports aliased into
|
||||
/// our index space.
|
||||
Child {
|
||||
InstanceExport {
|
||||
/// The index we're aliasing.
|
||||
instance: InstanceIndex,
|
||||
/// The nth export that we're inserting into our own index space
|
||||
/// locally.
|
||||
export: usize,
|
||||
export: &'a str,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1014,30 +1023,15 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
drop(amount);
|
||||
}
|
||||
|
||||
/// Declares that a module will come later with the type signature provided.
|
||||
fn declare_module(&mut self, ty: TypeIndex) -> WasmResult<()> {
|
||||
drop(ty);
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Called at the beginning of translating a module.
|
||||
///
|
||||
/// The `index` argument is a monotonically increasing index which
|
||||
/// corresponds to the nth module that's being translated. This is not the
|
||||
/// 32-bit index in the current module's index space. For example the first
|
||||
/// call to `module_start` will have index 0.
|
||||
///
|
||||
/// Note that for nested modules this may be called multiple times.
|
||||
fn module_start(&mut self, index: usize) {
|
||||
drop(index);
|
||||
}
|
||||
fn module_start(&mut self) {}
|
||||
|
||||
/// Called at the end of translating a module.
|
||||
///
|
||||
/// Note that for nested modules this may be called multiple times.
|
||||
fn module_end(&mut self, index: usize) {
|
||||
drop(index);
|
||||
}
|
||||
fn module_end(&mut self) {}
|
||||
|
||||
/// Indicates that this module will have `amount` instances.
|
||||
fn reserve_instances(&mut self, amount: u32) {
|
||||
@@ -1046,7 +1040,11 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
|
||||
/// Declares a new instance which this module will instantiate before it's
|
||||
/// instantiated.
|
||||
fn declare_instance(&mut self, module: ModuleIndex, args: Vec<EntityIndex>) -> WasmResult<()> {
|
||||
fn declare_instance(
|
||||
&mut self,
|
||||
module: ModuleIndex,
|
||||
args: Vec<(&'data str, EntityIndex)>,
|
||||
) -> WasmResult<()> {
|
||||
drop((module, args));
|
||||
Err(WasmError::Unsupported("wasm instance".to_string()))
|
||||
}
|
||||
@@ -1056,7 +1054,7 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
/// The alias comes from the `instance` specified (or the parent if `None`
|
||||
/// is supplied) and the index is either in the module's own index spaces
|
||||
/// for the parent or an index into the exports for nested instances.
|
||||
fn declare_alias(&mut self, alias: Alias) -> WasmResult<()> {
|
||||
fn declare_alias(&mut self, alias: Alias<'data>) -> WasmResult<()> {
|
||||
drop(alias);
|
||||
Err(WasmError::Unsupported("wasm alias".to_string()))
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ use crate::environ::{ModuleEnvironment, WasmResult};
|
||||
use crate::sections_translator::{
|
||||
parse_alias_section, parse_data_section, parse_element_section, parse_event_section,
|
||||
parse_export_section, parse_function_section, parse_global_section, parse_import_section,
|
||||
parse_instance_section, parse_memory_section, parse_module_section, parse_name_section,
|
||||
parse_start_section, parse_table_section, parse_type_section,
|
||||
parse_instance_section, parse_memory_section, parse_name_section, parse_start_section,
|
||||
parse_table_section, parse_type_section,
|
||||
};
|
||||
use crate::state::ModuleTranslationState;
|
||||
use cranelift_codegen::timing;
|
||||
@@ -22,23 +22,16 @@ pub fn translate_module<'data>(
|
||||
let mut module_translation_state = ModuleTranslationState::new();
|
||||
let mut validator = Validator::new();
|
||||
validator.wasm_features(environ.wasm_features());
|
||||
let mut stack = Vec::new();
|
||||
let mut modules = 1;
|
||||
let mut cur_module = 0;
|
||||
|
||||
for payload in Parser::new(0).parse_all(data) {
|
||||
match payload? {
|
||||
Payload::Version { num, range } => {
|
||||
validator.version(num, &range)?;
|
||||
environ.module_start(cur_module);
|
||||
environ.module_start();
|
||||
}
|
||||
Payload::End => {
|
||||
validator.end()?;
|
||||
environ.module_end(cur_module);
|
||||
if let Some((other, other_index)) = stack.pop() {
|
||||
validator = other;
|
||||
cur_module = other_index;
|
||||
}
|
||||
environ.module_end();
|
||||
}
|
||||
|
||||
Payload::TypeSection(types) => {
|
||||
@@ -111,10 +104,6 @@ pub fn translate_module<'data>(
|
||||
environ.reserve_passive_data(count)?;
|
||||
}
|
||||
|
||||
Payload::ModuleSection(s) => {
|
||||
validator.module_section(&s)?;
|
||||
parse_module_section(s, environ)?;
|
||||
}
|
||||
Payload::InstanceSection(s) => {
|
||||
validator.instance_section(&s)?;
|
||||
parse_instance_section(s, environ)?;
|
||||
@@ -123,20 +112,17 @@ pub fn translate_module<'data>(
|
||||
validator.alias_section(&s)?;
|
||||
parse_alias_section(s, environ)?;
|
||||
}
|
||||
Payload::ModuleCodeSectionStart {
|
||||
Payload::ModuleSectionStart {
|
||||
count,
|
||||
range,
|
||||
size: _,
|
||||
} => {
|
||||
validator.module_code_section_start(count, &range)?;
|
||||
validator.module_section_start(count, &range)?;
|
||||
environ.reserve_modules(count);
|
||||
}
|
||||
|
||||
Payload::ModuleCodeSectionEntry { .. } => {
|
||||
let subvalidator = validator.module_code_section_entry();
|
||||
stack.push((validator, cur_module));
|
||||
validator = subvalidator;
|
||||
cur_module = modules;
|
||||
modules += 1;
|
||||
Payload::ModuleSectionEntry { .. } => {
|
||||
validator.module_section_entry();
|
||||
}
|
||||
|
||||
Payload::CustomSection {
|
||||
|
||||
@@ -504,19 +504,6 @@ pub fn parse_name_section<'data>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Module section of the wasm module.
|
||||
pub fn parse_module_section<'data>(
|
||||
section: wasmparser::ModuleSectionReader<'data>,
|
||||
environ: &mut dyn ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_modules(section.get_count());
|
||||
|
||||
for module_ty in section {
|
||||
environ.declare_module(TypeIndex::from_u32(module_ty?))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Instance section of the wasm module.
|
||||
pub fn parse_instance_section<'data>(
|
||||
section: wasmparser::InstanceSectionReader<'data>,
|
||||
@@ -530,20 +517,23 @@ pub fn parse_instance_section<'data>(
|
||||
let args = instance
|
||||
.args()?
|
||||
.into_iter()
|
||||
.map(|result| {
|
||||
let (kind, idx) = result?;
|
||||
Ok(match kind {
|
||||
ExternalKind::Function => EntityIndex::Function(FuncIndex::from_u32(idx)),
|
||||
ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(idx)),
|
||||
ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(idx)),
|
||||
ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(idx)),
|
||||
ExternalKind::Module => EntityIndex::Module(ModuleIndex::from_u32(idx)),
|
||||
ExternalKind::Instance => EntityIndex::Instance(InstanceIndex::from_u32(idx)),
|
||||
.map(|arg| {
|
||||
let arg = arg?;
|
||||
let index = match arg.kind {
|
||||
ExternalKind::Function => EntityIndex::Function(FuncIndex::from_u32(arg.index)),
|
||||
ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(arg.index)),
|
||||
ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(arg.index)),
|
||||
ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(arg.index)),
|
||||
ExternalKind::Module => EntityIndex::Module(ModuleIndex::from_u32(arg.index)),
|
||||
ExternalKind::Instance => {
|
||||
EntityIndex::Instance(InstanceIndex::from_u32(arg.index))
|
||||
}
|
||||
ExternalKind::Event => unimplemented!(),
|
||||
|
||||
// this won't pass validation
|
||||
ExternalKind::Type => unreachable!(),
|
||||
})
|
||||
};
|
||||
Ok((arg.name, index))
|
||||
})
|
||||
.collect::<WasmResult<Vec<_>>>()?;
|
||||
environ.declare_instance(module, args)?;
|
||||
@@ -557,19 +547,28 @@ pub fn parse_alias_section<'data>(
|
||||
environ: &mut dyn ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
for alias in section {
|
||||
let alias = alias?;
|
||||
let alias = match alias.instance {
|
||||
wasmparser::AliasedInstance::Parent => {
|
||||
match alias.kind {
|
||||
ExternalKind::Module => Alias::ParentModule(ModuleIndex::from_u32(alias.index)),
|
||||
ExternalKind::Type => Alias::ParentType(TypeIndex::from_u32(alias.index)),
|
||||
// shouldn't get past validation
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
wasmparser::AliasedInstance::Child(i) => Alias::Child {
|
||||
instance: InstanceIndex::from_u32(i),
|
||||
export: alias.index as usize,
|
||||
let alias = match alias? {
|
||||
wasmparser::Alias::OuterType {
|
||||
relative_depth,
|
||||
index,
|
||||
} => Alias::OuterType {
|
||||
relative_depth,
|
||||
index: TypeIndex::from_u32(index),
|
||||
},
|
||||
wasmparser::Alias::OuterModule {
|
||||
relative_depth,
|
||||
index,
|
||||
} => Alias::OuterModule {
|
||||
relative_depth,
|
||||
index: ModuleIndex::from_u32(index),
|
||||
},
|
||||
wasmparser::Alias::InstanceExport {
|
||||
instance,
|
||||
export,
|
||||
kind: _,
|
||||
} => Alias::InstanceExport {
|
||||
instance: InstanceIndex::from_u32(instance),
|
||||
export,
|
||||
},
|
||||
};
|
||||
environ.declare_alias(alias)?;
|
||||
|
||||
@@ -13,7 +13,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
gimli = "0.23.0"
|
||||
wasmparser = "0.71"
|
||||
wasmparser = "0.72"
|
||||
object = { version = "0.22.0", default-features = false, features = ["read_core", "elf", "write"] }
|
||||
wasmtime-environ = { path = "../environ", version = "0.22.0" }
|
||||
target-lexicon = { version = "0.11.0", default-features = false }
|
||||
|
||||
@@ -16,7 +16,7 @@ anyhow = "1.0"
|
||||
cranelift-codegen = { path = "../../cranelift/codegen", version = "0.69.0", features = ["enable-serde"] }
|
||||
cranelift-entity = { path = "../../cranelift/entity", version = "0.69.0", features = ["enable-serde"] }
|
||||
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.69.0", features = ["enable-serde"] }
|
||||
wasmparser = "0.71"
|
||||
wasmparser = "0.72"
|
||||
indexmap = { version = "1.0.2", features = ["serde-1"] }
|
||||
thiserror = "1.0.4"
|
||||
serde = { version = "1.0.94", features = ["derive"] }
|
||||
|
||||
@@ -142,12 +142,6 @@ impl ModuleType {
|
||||
/// memory initializers.
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Module {
|
||||
/// The parent index of this module, used for the module linking proposal.
|
||||
///
|
||||
/// This index is into the list of modules returned from compilation of a
|
||||
/// single wasm file with nested modules.
|
||||
pub parent: Option<usize>,
|
||||
|
||||
/// The name of this wasm module, often found in the wasm file.
|
||||
pub name: Option<String>,
|
||||
|
||||
@@ -213,25 +207,26 @@ pub struct Module {
|
||||
pub enum Initializer {
|
||||
/// An imported item is required to be provided.
|
||||
Import {
|
||||
/// Module name of this import
|
||||
module: String,
|
||||
/// Optional field name of this import
|
||||
/// Name of this import
|
||||
name: String,
|
||||
/// The field name projection of this import. When module-linking is
|
||||
/// enabled this is always `None`. Otherwise this is always `Some`.
|
||||
field: Option<String>,
|
||||
/// Where this import will be placed, which also has type information
|
||||
/// about the import.
|
||||
index: EntityIndex,
|
||||
},
|
||||
|
||||
/// A module from the parent's declared modules is inserted into our own
|
||||
/// An export from a previously defined instance is being inserted into our
|
||||
/// index space.
|
||||
AliasParentModule(ModuleIndex),
|
||||
|
||||
/// A module from the parent's declared modules is inserted into our own
|
||||
/// index space.
|
||||
#[allow(missing_docs)]
|
||||
///
|
||||
/// Note that when the module linking proposal is enabled two-level imports
|
||||
/// will implicitly desugar to this initializer.
|
||||
AliasInstanceExport {
|
||||
/// The instance that we're referencing.
|
||||
instance: InstanceIndex,
|
||||
export: usize,
|
||||
/// Which export is being inserted into our index space.
|
||||
export: String,
|
||||
},
|
||||
|
||||
/// A module is being instantiated with previously configured intializers
|
||||
@@ -239,8 +234,9 @@ pub enum Initializer {
|
||||
Instantiate {
|
||||
/// The module that this instance is instantiating.
|
||||
module: ModuleIndex,
|
||||
/// The arguments provided to instantiation.
|
||||
args: Vec<EntityIndex>,
|
||||
/// The arguments provided to instantiation, along with their name in
|
||||
/// the instance being instantiated.
|
||||
args: IndexMap<String, EntityIndex>,
|
||||
},
|
||||
|
||||
/// A module is defined into the module index space, and which module is
|
||||
@@ -351,11 +347,9 @@ impl Module {
|
||||
/// module name, field name, and type that's being imported.
|
||||
pub fn imports(&self) -> impl Iterator<Item = (&str, Option<&str>, EntityType)> {
|
||||
self.initializers.iter().filter_map(move |i| match i {
|
||||
Initializer::Import {
|
||||
module,
|
||||
field,
|
||||
index,
|
||||
} => Some((module.as_str(), field.as_deref(), self.type_of(*index))),
|
||||
Initializer::Import { name, field, index } => {
|
||||
Some((name.as_str(), field.as_deref(), self.type_of(*index)))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
@@ -389,16 +383,16 @@ pub struct TypeTables {
|
||||
/// The type signature of known modules.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModuleSignature {
|
||||
/// All imports in this module, listed in order with their module/name and
|
||||
/// All imports in this module, listed in order with their name and
|
||||
/// what type they're importing.
|
||||
pub imports: Vec<(String, Option<String>, EntityType)>,
|
||||
pub imports: IndexMap<String, EntityType>,
|
||||
/// Exports are what an instance type conveys, so we go through an
|
||||
/// indirection over there.
|
||||
pub exports: InstanceTypeIndex,
|
||||
}
|
||||
|
||||
/// The type signature of known instances.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct InstanceSignature {
|
||||
/// The name of what's being exported as well as its type signature.
|
||||
pub exports: IndexMap<String, EntityType>,
|
||||
|
||||
@@ -14,7 +14,7 @@ use cranelift_wasm::{
|
||||
WasmError, WasmFuncType, WasmResult,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::convert::TryFrom;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
@@ -31,6 +31,10 @@ pub struct ModuleEnvironment<'data> {
|
||||
/// the module linking proposal.
|
||||
results: Vec<ModuleTranslation<'data>>,
|
||||
|
||||
/// Modules which are in-progress being translated, or otherwise also known
|
||||
/// as the outer modules of the current module being processed.
|
||||
in_progress: Vec<ModuleTranslation<'data>>,
|
||||
|
||||
/// How many modules that have not yet made their way into `results` which
|
||||
/// are coming at some point.
|
||||
modules_to_be: usize,
|
||||
@@ -38,13 +42,11 @@ pub struct ModuleEnvironment<'data> {
|
||||
/// Intern'd types for this entire translation, shared by all modules.
|
||||
types: TypeTables,
|
||||
|
||||
/// Where our module will get pushed into `results` after it's finished.
|
||||
cur: usize,
|
||||
|
||||
// Various bits and pieces of configuration
|
||||
features: WasmFeatures,
|
||||
target_config: TargetFrontendConfig,
|
||||
tunables: Tunables,
|
||||
first_module: bool,
|
||||
}
|
||||
|
||||
/// The result of translating via `ModuleEnvironment`. Function bodies are not
|
||||
@@ -72,15 +74,7 @@ pub struct ModuleTranslation<'data> {
|
||||
/// which function is currently being defined.
|
||||
code_index: u32,
|
||||
|
||||
/// When local modules are declared an entry is pushed onto this list which
|
||||
/// indicates that the initializer at the specified position needs to be
|
||||
/// rewritten with the module's final index in the global list of compiled
|
||||
/// modules.
|
||||
module_initializer_indexes: Vec<usize>,
|
||||
|
||||
/// Used as a pointer into the above list as the module code section is
|
||||
/// parsed.
|
||||
num_modules_defined: usize,
|
||||
implicit_instances: HashMap<&'data str, InstanceIndex>,
|
||||
}
|
||||
|
||||
/// Contains function data: byte code and its offset in the module.
|
||||
@@ -142,12 +136,13 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
Self {
|
||||
result: ModuleTranslation::default(),
|
||||
results: Vec::with_capacity(1),
|
||||
in_progress: Vec::new(),
|
||||
modules_to_be: 1,
|
||||
cur: 0,
|
||||
types: Default::default(),
|
||||
target_config,
|
||||
tunables: tunables.clone(),
|
||||
features: *features,
|
||||
first_module: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +158,8 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
/// Note that for MVP modules this will always be a list with one element,
|
||||
/// but with the module linking proposal this may have many elements.
|
||||
///
|
||||
/// For the module linking proposal the top-level module is at index 0.
|
||||
/// For the module linking proposal the top-level module is returned as the
|
||||
/// first return value.
|
||||
///
|
||||
/// The `TypeTables` structure returned contains intern'd versions of types
|
||||
/// referenced from each module translation. This primarily serves as the
|
||||
@@ -173,10 +169,10 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
pub fn translate(
|
||||
mut self,
|
||||
data: &'data [u8],
|
||||
) -> WasmResult<(Vec<ModuleTranslation<'data>>, TypeTables)> {
|
||||
) -> WasmResult<(usize, Vec<ModuleTranslation<'data>>, TypeTables)> {
|
||||
translate_module(data, &mut self)?;
|
||||
assert!(self.results.len() > 0);
|
||||
Ok((self.results, self.types))
|
||||
Ok((self.results.len() - 1, self.results, self.types))
|
||||
}
|
||||
|
||||
fn declare_export(&mut self, export: EntityIndex, name: &str) -> WasmResult<()> {
|
||||
@@ -224,6 +220,137 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
dwarf.ranges = gimli::RangeLists::new(info.debug_ranges, info.debug_rnglists);
|
||||
dwarf.locations = gimli::LocationLists::new(info.debug_loc, info.debug_loclists);
|
||||
}
|
||||
|
||||
/// Declares a new import with the `module` and `field` names, importing the
|
||||
/// `ty` specified.
|
||||
///
|
||||
/// Note that this method is somewhat tricky due to the implementation of
|
||||
/// the module linking proposal. In the module linking proposal two-level
|
||||
/// imports are recast as single-level imports of instances. That recasting
|
||||
/// happens here by recording an import of an instance for the first time
|
||||
/// we see a two-level import.
|
||||
///
|
||||
/// When the module linking proposal is disabled, however, disregard this
|
||||
/// logic and instead work directly with two-level imports since no
|
||||
/// instances are defined.
|
||||
fn declare_import(&mut self, module: &'data str, field: Option<&'data str>, ty: EntityType) {
|
||||
if !self.features.module_linking {
|
||||
assert!(field.is_some());
|
||||
let index = self.push_type(ty);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
name: module.to_owned(),
|
||||
field: field.map(|s| s.to_string()),
|
||||
index,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
match field {
|
||||
Some(field) => {
|
||||
// If this is a two-level import then this is actually an
|
||||
// implicit import of an instance, where each two-level import
|
||||
// is an alias directive from the original instance. The first
|
||||
// thing we do here is lookup our implicit instance, creating a
|
||||
// blank one if it wasn't already created.
|
||||
let instance = match self.result.implicit_instances.entry(module) {
|
||||
Entry::Occupied(e) => *e.get(),
|
||||
Entry::Vacant(v) => {
|
||||
let ty = self
|
||||
.types
|
||||
.instance_signatures
|
||||
.push(InstanceSignature::default());
|
||||
let idx = self.result.module.instances.push(ty);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
name: module.to_owned(),
|
||||
field: None,
|
||||
index: EntityIndex::Instance(idx),
|
||||
});
|
||||
*v.insert(idx)
|
||||
}
|
||||
};
|
||||
|
||||
// Update the implicit instance's type signature with this new
|
||||
// field and its type.
|
||||
self.types.instance_signatures[self.result.module.instances[instance]]
|
||||
.exports
|
||||
.insert(field.to_string(), ty.clone());
|
||||
|
||||
// Record our implicit alias annotation which corresponds to
|
||||
// this import that we're processing.
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::AliasInstanceExport {
|
||||
instance,
|
||||
export: field.to_string(),
|
||||
});
|
||||
|
||||
// And then record the type information for the item that we're
|
||||
// processing.
|
||||
self.push_type(ty);
|
||||
}
|
||||
None => {
|
||||
// Without a field then this is a single-level import (a feature
|
||||
// of module linking) which means we're simply importing that
|
||||
// name with the specified type. Record the type information and
|
||||
// then the name that we're importing.
|
||||
let index = self.push_type(ty);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
name: module.to_owned(),
|
||||
field: None,
|
||||
index,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_type(&mut self, ty: EntityType) -> EntityIndex {
|
||||
match ty {
|
||||
EntityType::Function(ty) => {
|
||||
EntityIndex::Function(self.result.module.functions.push(ty))
|
||||
}
|
||||
EntityType::Table(ty) => {
|
||||
let plan = TablePlan::for_table(ty, &self.tunables);
|
||||
EntityIndex::Table(self.result.module.table_plans.push(plan))
|
||||
}
|
||||
EntityType::Memory(ty) => {
|
||||
let plan = MemoryPlan::for_memory(ty, &self.tunables);
|
||||
EntityIndex::Memory(self.result.module.memory_plans.push(plan))
|
||||
}
|
||||
EntityType::Global(ty) => EntityIndex::Global(self.result.module.globals.push(ty)),
|
||||
EntityType::Instance(ty) => {
|
||||
EntityIndex::Instance(self.result.module.instances.push(ty))
|
||||
}
|
||||
EntityType::Module(ty) => EntityIndex::Module(self.result.module.modules.push(ty)),
|
||||
EntityType::Event(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_type_of_module(&mut self, module: usize) -> ModuleTypeIndex {
|
||||
let module = &self.results[module].module;
|
||||
let imports = module
|
||||
.imports()
|
||||
.map(|(s, field, ty)| {
|
||||
assert!(field.is_none());
|
||||
(s.to_string(), ty)
|
||||
})
|
||||
.collect();
|
||||
let exports = module
|
||||
.exports
|
||||
.iter()
|
||||
.map(|(name, idx)| (name.clone(), module.type_of(*idx)))
|
||||
.collect();
|
||||
|
||||
// FIXME(#2469): this instance/module signature insertion should likely
|
||||
// be deduplicated.
|
||||
let exports = self
|
||||
.types
|
||||
.instance_signatures
|
||||
.push(InstanceSignature { exports });
|
||||
self.types
|
||||
.module_signatures
|
||||
.push(ModuleSignature { imports, exports })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'data> TargetEnvironment for ModuleEnvironment<'data> {
|
||||
@@ -267,13 +394,29 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
|
||||
fn declare_type_module(
|
||||
&mut self,
|
||||
imports: &[(&'data str, Option<&'data str>, EntityType)],
|
||||
declared_imports: &[(&'data str, Option<&'data str>, EntityType)],
|
||||
exports: &[(&'data str, EntityType)],
|
||||
) -> WasmResult<()> {
|
||||
let imports = imports
|
||||
.iter()
|
||||
.map(|i| (i.0.to_string(), i.1.map(|s| s.to_string()), i.2.clone()))
|
||||
.collect();
|
||||
let mut imports = indexmap::IndexMap::new();
|
||||
let mut instance_types = HashMap::new();
|
||||
for (module, field, ty) in declared_imports {
|
||||
match field {
|
||||
Some(field) => {
|
||||
let idx = *instance_types
|
||||
.entry(module)
|
||||
.or_insert_with(|| self.types.instance_signatures.push(Default::default()));
|
||||
self.types.instance_signatures[idx]
|
||||
.exports
|
||||
.insert(field.to_string(), ty.clone());
|
||||
if !imports.contains_key(*module) {
|
||||
imports.insert(module.to_string(), EntityType::Instance(idx));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
imports.insert(module.to_string(), ty.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let exports = exports
|
||||
.iter()
|
||||
.map(|e| (e.0.to_string(), e.1.clone()))
|
||||
@@ -344,8 +487,8 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
fn declare_func_import(
|
||||
&mut self,
|
||||
index: TypeIndex,
|
||||
module: &str,
|
||||
field: Option<&str>,
|
||||
module: &'data str,
|
||||
field: Option<&'data str>,
|
||||
) -> WasmResult<()> {
|
||||
debug_assert_eq!(
|
||||
self.result.module.functions.len(),
|
||||
@@ -353,12 +496,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
"Imported functions must be declared first"
|
||||
);
|
||||
let sig_index = self.result.module.types[index].unwrap_function();
|
||||
let func_index = self.result.module.functions.push(sig_index);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
module: module.to_owned(),
|
||||
field: field.map(|s| s.to_owned()),
|
||||
index: EntityIndex::Function(func_index),
|
||||
});
|
||||
self.declare_import(module, field, EntityType::Function(sig_index));
|
||||
self.result.module.num_imported_funcs += 1;
|
||||
self.result.debuginfo.wasm_file.imported_func_count += 1;
|
||||
Ok(())
|
||||
@@ -367,21 +505,15 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
fn declare_table_import(
|
||||
&mut self,
|
||||
table: Table,
|
||||
module: &str,
|
||||
field: Option<&str>,
|
||||
module: &'data str,
|
||||
field: Option<&'data str>,
|
||||
) -> WasmResult<()> {
|
||||
debug_assert_eq!(
|
||||
self.result.module.table_plans.len(),
|
||||
self.result.module.num_imported_tables,
|
||||
"Imported tables must be declared first"
|
||||
);
|
||||
let plan = TablePlan::for_table(table, &self.tunables);
|
||||
let table_index = self.result.module.table_plans.push(plan);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
module: module.to_owned(),
|
||||
field: field.map(|s| s.to_owned()),
|
||||
index: EntityIndex::Table(table_index),
|
||||
});
|
||||
self.declare_import(module, field, EntityType::Table(table));
|
||||
self.result.module.num_imported_tables += 1;
|
||||
Ok(())
|
||||
}
|
||||
@@ -389,8 +521,8 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
fn declare_memory_import(
|
||||
&mut self,
|
||||
memory: Memory,
|
||||
module: &str,
|
||||
field: Option<&str>,
|
||||
module: &'data str,
|
||||
field: Option<&'data str>,
|
||||
) -> WasmResult<()> {
|
||||
debug_assert_eq!(
|
||||
self.result.module.memory_plans.len(),
|
||||
@@ -400,13 +532,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
if memory.shared {
|
||||
return Err(WasmError::Unsupported("shared memories".to_owned()));
|
||||
}
|
||||
let plan = MemoryPlan::for_memory(memory, &self.tunables);
|
||||
let memory_index = self.result.module.memory_plans.push(plan);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
module: module.to_owned(),
|
||||
field: field.map(|s| s.to_owned()),
|
||||
index: EntityIndex::Memory(memory_index),
|
||||
});
|
||||
self.declare_import(module, field, EntityType::Memory(memory));
|
||||
self.result.module.num_imported_memories += 1;
|
||||
Ok(())
|
||||
}
|
||||
@@ -414,20 +540,15 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
fn declare_global_import(
|
||||
&mut self,
|
||||
global: Global,
|
||||
module: &str,
|
||||
field: Option<&str>,
|
||||
module: &'data str,
|
||||
field: Option<&'data str>,
|
||||
) -> WasmResult<()> {
|
||||
debug_assert_eq!(
|
||||
self.result.module.globals.len(),
|
||||
self.result.module.num_imported_globals,
|
||||
"Imported globals must be declared first"
|
||||
);
|
||||
let global_index = self.result.module.globals.push(global);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
module: module.to_owned(),
|
||||
field: field.map(|s| s.to_owned()),
|
||||
index: EntityIndex::Global(global_index),
|
||||
});
|
||||
self.declare_import(module, field, EntityType::Global(global));
|
||||
self.result.module.num_imported_globals += 1;
|
||||
Ok(())
|
||||
}
|
||||
@@ -439,12 +560,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
field: Option<&'data str>,
|
||||
) -> WasmResult<()> {
|
||||
let signature = self.type_to_module_type(ty_index)?;
|
||||
let module_index = self.result.module.modules.push(signature);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
module: module.to_owned(),
|
||||
field: field.map(|s| s.to_owned()),
|
||||
index: EntityIndex::Module(module_index),
|
||||
});
|
||||
self.declare_import(module, field, EntityType::Module(signature));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -455,12 +571,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
field: Option<&'data str>,
|
||||
) -> WasmResult<()> {
|
||||
let signature = self.type_to_instance_type(ty_index)?;
|
||||
let instance_index = self.result.module.instances.push(signature);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
module: module.to_owned(),
|
||||
field: field.map(|s| s.to_owned()),
|
||||
index: EntityIndex::Instance(instance_index),
|
||||
});
|
||||
self.declare_import(module, field, EntityType::Instance(signature));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -755,62 +866,34 @@ and for re-adding support for interface types you can see this issue:
|
||||
self.result.module.initializers.reserve(amount as usize);
|
||||
}
|
||||
|
||||
fn declare_module(&mut self, ty: TypeIndex) -> WasmResult<()> {
|
||||
// Record the type signature of this module ...
|
||||
let signature = self.type_to_module_type(ty)?;
|
||||
self.result.module.modules.push(signature);
|
||||
|
||||
// ... and then record that in the initialization steps of this module
|
||||
// we're inserting this module into the module index space. At this
|
||||
// point we don't know the final index of the module we're defining, so
|
||||
// we leave a placeholder to get rewritten later.
|
||||
let loc = self.result.module.initializers.len();
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::DefineModule(usize::max_value()));
|
||||
self.result.module_initializer_indexes.push(loc);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn module_start(&mut self, index: usize) {
|
||||
// Reset the contents of `self.result` for a new module that's getting
|
||||
// translataed.
|
||||
let mut prev = mem::replace(&mut self.result, ModuleTranslation::default());
|
||||
|
||||
// If this is a nested submodule then we record the final destination of
|
||||
// the child in parent (we store `index` into `prev`) in the appropriate
|
||||
// initialization slot as dicated by `num_modules_defined` (our index of
|
||||
// iteration through the code section).
|
||||
// Record that the `num_modules_defined`-th module is defined at index
|
||||
// by updating the initializer entry.
|
||||
if index > 0 {
|
||||
let initializer_idx = prev.module_initializer_indexes[prev.num_modules_defined];
|
||||
prev.num_modules_defined += 1;
|
||||
debug_assert!(match &prev.module.initializers[initializer_idx] {
|
||||
Initializer::DefineModule(usize::MAX) => true,
|
||||
_ => false,
|
||||
});
|
||||
prev.module.initializers[initializer_idx] = Initializer::DefineModule(index);
|
||||
self.result.module.parent = Some(self.cur);
|
||||
fn module_start(&mut self) {
|
||||
// If this is the first time this method is called, nothing to do.
|
||||
if self.first_module {
|
||||
self.first_module = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update our current index counter and save our parent's translation
|
||||
// where this current translation will end up, which we'll swap back as
|
||||
// part of `module_end`.
|
||||
self.cur = index;
|
||||
assert_eq!(index, self.results.len());
|
||||
self.results.push(prev);
|
||||
// Reset our internal state for a new module by saving the current
|
||||
// module in `results`.
|
||||
let in_progress = mem::replace(&mut self.result, ModuleTranslation::default());
|
||||
self.in_progress.push(in_progress);
|
||||
self.modules_to_be -= 1;
|
||||
}
|
||||
|
||||
fn module_end(&mut self, index: usize) {
|
||||
assert!(self.result.num_modules_defined == self.result.module_initializer_indexes.len());
|
||||
|
||||
// Move our finished module into its final location, swapping it with
|
||||
// what was this module's parent.
|
||||
self.cur = self.result.module.parent.unwrap_or(0);
|
||||
mem::swap(&mut self.result, &mut self.results[index]);
|
||||
fn module_end(&mut self) {
|
||||
let (record_initializer, done) = match self.in_progress.pop() {
|
||||
Some(m) => (true, mem::replace(&mut self.result, m)),
|
||||
None => (false, mem::take(&mut self.result)),
|
||||
};
|
||||
self.results.push(done);
|
||||
if record_initializer {
|
||||
let index = self.results.len() - 1;
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::DefineModule(index));
|
||||
let sig = self.gen_type_of_module(index);
|
||||
self.result.module.modules.push(sig);
|
||||
}
|
||||
}
|
||||
|
||||
fn reserve_instances(&mut self, amt: u32) {
|
||||
@@ -818,7 +901,12 @@ and for re-adding support for interface types you can see this issue:
|
||||
self.result.module.initializers.reserve(amt as usize);
|
||||
}
|
||||
|
||||
fn declare_instance(&mut self, module: ModuleIndex, args: Vec<EntityIndex>) -> WasmResult<()> {
|
||||
fn declare_instance(
|
||||
&mut self,
|
||||
module: ModuleIndex,
|
||||
args: Vec<(&'data str, EntityIndex)>,
|
||||
) -> WasmResult<()> {
|
||||
let args = args.into_iter().map(|(s, i)| (s.to_string(), i)).collect();
|
||||
// Record the type of this instance with the type signature of the
|
||||
// module we're instantiating and then also add an initializer which
|
||||
// records that we'll be adding to the instance index space here.
|
||||
@@ -839,29 +927,32 @@ and for re-adding support for interface types you can see this issue:
|
||||
//
|
||||
// Note that we don't add an initializer for this alias because
|
||||
// we statically know where all types point to.
|
||||
Alias::ParentType(parent_idx) => {
|
||||
let ty = self.results[self.cur].module.types[parent_idx];
|
||||
Alias::OuterType {
|
||||
relative_depth,
|
||||
index,
|
||||
} => {
|
||||
let module_idx = self.in_progress.len() - 1 - (relative_depth as usize);
|
||||
let ty = self.in_progress[module_idx].module.types[index];
|
||||
self.result.module.types.push(ty);
|
||||
}
|
||||
|
||||
// This is similar to types in that it's easy for us to record the
|
||||
// type of the module that's being aliased, but we also need to add
|
||||
// an initializer so during instantiation we can prepare the index
|
||||
// space appropriately.
|
||||
Alias::ParentModule(parent_idx) => {
|
||||
let module_idx = self.results[self.cur].module.modules[parent_idx];
|
||||
self.result.module.modules.push(module_idx);
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::AliasParentModule(parent_idx));
|
||||
// FIXME(WebAssembly/module-linking#28) unsure how to implement this
|
||||
// at this time, if we can alias imported modules it's a lot harder,
|
||||
// otherwise we'll need to figure out how to translate `index` to a
|
||||
// `usize` for a defined module (creating Initializer::DefineModule)
|
||||
Alias::OuterModule {
|
||||
relative_depth,
|
||||
index,
|
||||
} => {
|
||||
drop((relative_depth, index));
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// This case is slightly more involved, we'll be recording all the
|
||||
// type information for each kind of entity, and then we also need
|
||||
// to record an initialization step to get the export from the
|
||||
// instance.
|
||||
Alias::Child { instance, export } => {
|
||||
Alias::InstanceExport { instance, export } => {
|
||||
let ty = self.result.module.instances[instance];
|
||||
match &self.types.instance_signatures[ty].exports[export] {
|
||||
EntityType::Global(g) => {
|
||||
@@ -894,7 +985,10 @@ and for re-adding support for interface types you can see this issue:
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::AliasInstanceExport { instance, export })
|
||||
.push(Initializer::AliasInstanceExport {
|
||||
instance,
|
||||
export: export.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ arbitrary = { version = "0.4.1", features = ["derive"] }
|
||||
env_logger = "0.8.1"
|
||||
log = "0.4.8"
|
||||
rayon = "1.2.1"
|
||||
wasmparser = "0.71"
|
||||
wasmparser = "0.72"
|
||||
wasmprinter = "0.2.17"
|
||||
wasmtime = { path = "../wasmtime" }
|
||||
wasmtime-wast = { path = "../wast" }
|
||||
wasm-encoder = "0.2"
|
||||
wasm-smith = "0.3.0"
|
||||
wasm-encoder = "0.4"
|
||||
wasm-smith = "0.3.1"
|
||||
wasmi = "0.7.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -28,7 +28,7 @@ rayon = { version = "1.0", optional = true }
|
||||
region = "2.1.0"
|
||||
thiserror = "1.0.4"
|
||||
target-lexicon = { version = "0.11.0", default-features = false }
|
||||
wasmparser = "0.71"
|
||||
wasmparser = "0.72"
|
||||
more-asserts = "0.2.1"
|
||||
anyhow = "1.0"
|
||||
cfg-if = "1.0"
|
||||
|
||||
@@ -105,8 +105,8 @@ impl CompilationArtifacts {
|
||||
pub fn build(
|
||||
compiler: &Compiler,
|
||||
data: &[u8],
|
||||
) -> Result<(Vec<CompilationArtifacts>, TypeTables), SetupError> {
|
||||
let (translations, types) = ModuleEnvironment::new(
|
||||
) -> Result<(usize, Vec<CompilationArtifacts>, TypeTables), SetupError> {
|
||||
let (main_module, translations, types) = ModuleEnvironment::new(
|
||||
compiler.frontend_config(),
|
||||
compiler.tunables(),
|
||||
compiler.features(),
|
||||
@@ -166,6 +166,7 @@ impl CompilationArtifacts {
|
||||
})
|
||||
.collect::<Result<Vec<_>, SetupError>>()?;
|
||||
Ok((
|
||||
main_module,
|
||||
list,
|
||||
TypeTables {
|
||||
wasm_signatures: types.wasm_signatures,
|
||||
|
||||
@@ -24,7 +24,7 @@ more-asserts = "0.2.1"
|
||||
smallvec = "1.6.1"
|
||||
thiserror = "1.0.9"
|
||||
typemap = "0.3"
|
||||
wasmparser = "0.71"
|
||||
wasmparser = "0.72"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.2"
|
||||
|
||||
@@ -13,6 +13,6 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
lightbeam = { path = "..", version = "0.22.0" }
|
||||
wasmparser = "0.71"
|
||||
wasmparser = "0.72"
|
||||
cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.69.0" }
|
||||
wasmtime-environ = { path = "../../environ", version = "0.22.0" }
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use crate::vmcontext::{
|
||||
VMCallerCheckedAnyfunc, VMContext, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
|
||||
};
|
||||
use crate::InstanceHandle;
|
||||
use crate::RuntimeInstance;
|
||||
use std::any::Any;
|
||||
use std::ptr::NonNull;
|
||||
use wasmtime_environ::wasm::Global;
|
||||
use wasmtime_environ::{MemoryPlan, TablePlan};
|
||||
|
||||
/// The value of an export passed from one instance to another.
|
||||
pub enum Export<'a> {
|
||||
pub enum Export {
|
||||
/// A function export value.
|
||||
Function(ExportFunction),
|
||||
|
||||
@@ -22,10 +22,10 @@ pub enum Export<'a> {
|
||||
Global(ExportGlobal),
|
||||
|
||||
/// An instance
|
||||
Instance(&'a InstanceHandle),
|
||||
Instance(RuntimeInstance),
|
||||
|
||||
/// A module
|
||||
Module(&'a dyn Any),
|
||||
Module(Box<dyn Any>),
|
||||
}
|
||||
|
||||
/// A function export value.
|
||||
@@ -38,8 +38,8 @@ pub struct ExportFunction {
|
||||
pub anyfunc: NonNull<VMCallerCheckedAnyfunc>,
|
||||
}
|
||||
|
||||
impl<'a> From<ExportFunction> for Export<'a> {
|
||||
fn from(func: ExportFunction) -> Export<'a> {
|
||||
impl From<ExportFunction> for Export {
|
||||
fn from(func: ExportFunction) -> Export {
|
||||
Export::Function(func)
|
||||
}
|
||||
}
|
||||
@@ -55,8 +55,8 @@ pub struct ExportTable {
|
||||
pub table: TablePlan,
|
||||
}
|
||||
|
||||
impl<'a> From<ExportTable> for Export<'a> {
|
||||
fn from(func: ExportTable) -> Export<'a> {
|
||||
impl From<ExportTable> for Export {
|
||||
fn from(func: ExportTable) -> Export {
|
||||
Export::Table(func)
|
||||
}
|
||||
}
|
||||
@@ -72,8 +72,8 @@ pub struct ExportMemory {
|
||||
pub memory: MemoryPlan,
|
||||
}
|
||||
|
||||
impl<'a> From<ExportMemory> for Export<'a> {
|
||||
fn from(func: ExportMemory) -> Export<'a> {
|
||||
impl From<ExportMemory> for Export {
|
||||
fn from(func: ExportMemory) -> Export {
|
||||
Export::Memory(func)
|
||||
}
|
||||
}
|
||||
@@ -89,8 +89,8 @@ pub struct ExportGlobal {
|
||||
pub global: Global,
|
||||
}
|
||||
|
||||
impl<'a> From<ExportGlobal> for Export<'a> {
|
||||
fn from(func: ExportGlobal) -> Export<'a> {
|
||||
impl From<ExportGlobal> for Export {
|
||||
fn from(func: ExportGlobal) -> Export {
|
||||
Export::Global(func)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
|
||||
use crate::InstanceHandle;
|
||||
use std::any::Any;
|
||||
use wasmtime_environ::entity::PrimaryMap;
|
||||
use wasmtime_environ::wasm::{InstanceIndex, ModuleIndex};
|
||||
|
||||
/// Resolved import pointers.
|
||||
///
|
||||
@@ -28,15 +24,4 @@ pub struct Imports<'a> {
|
||||
|
||||
/// Resolved addresses for imported globals.
|
||||
pub globals: &'a [VMGlobalImport],
|
||||
|
||||
/// Resolved imported instances.
|
||||
pub instances: PrimaryMap<InstanceIndex, InstanceHandle>,
|
||||
|
||||
/// Resolved imported modules.
|
||||
///
|
||||
/// Note that `Box<Any>` here is chosen to allow the embedder of this crate
|
||||
/// to pick an appropriate representation of what module type should be. For
|
||||
/// example for the `wasmtime` crate it's `wasmtime::Module` but that's not
|
||||
/// defined way down here in this low crate.
|
||||
pub modules: PrimaryMap<ModuleIndex, Box<dyn Any>>,
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::vmcontext::{
|
||||
VMSharedSignatureIndex, VMTableDefinition, VMTableImport,
|
||||
};
|
||||
use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable};
|
||||
use indexmap::IndexMap;
|
||||
use memoffset::offset_of;
|
||||
use more_asserts::assert_lt;
|
||||
use std::alloc::{self, Layout};
|
||||
@@ -22,17 +23,22 @@ use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::ptr::NonNull;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr, slice};
|
||||
use thiserror::Error;
|
||||
use wasmtime_environ::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap};
|
||||
use wasmtime_environ::wasm::{
|
||||
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
|
||||
ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, InstanceIndex, MemoryIndex,
|
||||
ModuleIndex, SignatureIndex, TableElementType, TableIndex, WasmType,
|
||||
ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex,
|
||||
TableElementType, TableIndex, WasmType,
|
||||
};
|
||||
use wasmtime_environ::{ir, DataInitializer, Module, ModuleType, TableElements, VMOffsets};
|
||||
|
||||
/// Runtime representation of an instance value, which erases all `Instance`
|
||||
/// information since instances are just a collection of values.
|
||||
pub type RuntimeInstance = Rc<IndexMap<String, Export>>;
|
||||
|
||||
/// A WebAssembly instance.
|
||||
///
|
||||
/// This is repr(C) to ensure that the vmctx field is last.
|
||||
@@ -50,15 +56,6 @@ pub(crate) struct Instance {
|
||||
/// WebAssembly table data.
|
||||
tables: BoxedSlice<DefinedTableIndex, Table>,
|
||||
|
||||
/// Instances our module defined and their handles.
|
||||
instances: PrimaryMap<InstanceIndex, InstanceHandle>,
|
||||
|
||||
/// Modules that are located in our index space.
|
||||
///
|
||||
/// For now these are `Box<Any>` so the caller can define the type of what a
|
||||
/// module looks like.
|
||||
modules: PrimaryMap<ModuleIndex, Box<dyn Any>>,
|
||||
|
||||
/// Passive elements in this instantiation. As `elem.drop`s happen, these
|
||||
/// entries get removed. A missing entry is considered equivalent to an
|
||||
/// empty slice.
|
||||
@@ -266,18 +263,8 @@ impl Instance {
|
||||
self.vmctx() as *const VMContext as *mut VMContext
|
||||
}
|
||||
|
||||
/// Lookup an export with the given name.
|
||||
pub fn lookup(&self, field: &str) -> Option<Export> {
|
||||
let export = if let Some(export) = self.module.exports.get(field) {
|
||||
export.clone()
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(self.lookup_by_declaration(&export))
|
||||
}
|
||||
|
||||
/// Lookup an export with the given export declaration.
|
||||
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export<'_> {
|
||||
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
|
||||
match export {
|
||||
EntityIndex::Function(index) => {
|
||||
let anyfunc = self.get_caller_checked_anyfunc(*index).unwrap();
|
||||
@@ -326,8 +313,9 @@ impl Instance {
|
||||
}
|
||||
.into(),
|
||||
|
||||
EntityIndex::Instance(index) => Export::Instance(&self.instances[*index]),
|
||||
EntityIndex::Module(index) => Export::Module(&*self.modules[*index]),
|
||||
EntityIndex::Instance(_) | EntityIndex::Module(_) => {
|
||||
panic!("can't use this api for modules/instances")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,8 +843,6 @@ impl InstanceHandle {
|
||||
passive_elements: Default::default(),
|
||||
passive_data,
|
||||
host_state,
|
||||
instances: imports.instances,
|
||||
modules: imports.modules,
|
||||
vmctx: VMContext {},
|
||||
};
|
||||
let layout = instance.alloc_layout();
|
||||
@@ -1015,11 +1001,6 @@ impl InstanceHandle {
|
||||
self.instance().module()
|
||||
}
|
||||
|
||||
/// Lookup an export with the given name.
|
||||
pub fn lookup(&self, field: &str) -> Option<Export> {
|
||||
self.instance().lookup(field)
|
||||
}
|
||||
|
||||
/// Lookup an export with the given export declaration.
|
||||
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
|
||||
self.instance().lookup_by_declaration(export)
|
||||
|
||||
@@ -37,7 +37,7 @@ pub mod libcalls;
|
||||
pub use crate::export::*;
|
||||
pub use crate::externref::*;
|
||||
pub use crate::imports::Imports;
|
||||
pub use crate::instance::{InstanceHandle, InstantiationError, LinkError};
|
||||
pub use crate::instance::{InstanceHandle, InstantiationError, LinkError, RuntimeInstance};
|
||||
pub use crate::jit_int::GdbJitImageRegistration;
|
||||
pub use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator};
|
||||
pub use crate::mmap::Mmap;
|
||||
|
||||
@@ -16,7 +16,7 @@ wasmtime-jit = { path = "../jit", version = "0.22.0" }
|
||||
wasmtime-cache = { path = "../cache", version = "0.22.0", optional = true }
|
||||
wasmtime-profiling = { path = "../profiling", version = "0.22.0" }
|
||||
target-lexicon = { version = "0.11.0", default-features = false }
|
||||
wasmparser = "0.71"
|
||||
wasmparser = "0.72"
|
||||
anyhow = "1.0.19"
|
||||
region = "2.2.0"
|
||||
libc = "0.2"
|
||||
|
||||
@@ -112,26 +112,25 @@ impl Extern {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_export(
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
instance: StoreInstanceHandle,
|
||||
pub(crate) unsafe fn from_wasmtime_export(
|
||||
wasmtime_export: &wasmtime_runtime::Export,
|
||||
store: &Store,
|
||||
) -> Extern {
|
||||
match wasmtime_export {
|
||||
wasmtime_runtime::Export::Function(f) => {
|
||||
Extern::Func(Func::from_wasmtime_function(f, instance))
|
||||
Extern::Func(Func::from_wasmtime_function(f, store))
|
||||
}
|
||||
wasmtime_runtime::Export::Memory(m) => {
|
||||
Extern::Memory(Memory::from_wasmtime_memory(m, instance))
|
||||
Extern::Memory(Memory::from_wasmtime_memory(m, store))
|
||||
}
|
||||
wasmtime_runtime::Export::Global(g) => {
|
||||
Extern::Global(Global::from_wasmtime_global(g, instance))
|
||||
Extern::Global(Global::from_wasmtime_global(g, store))
|
||||
}
|
||||
wasmtime_runtime::Export::Table(t) => {
|
||||
Extern::Table(Table::from_wasmtime_table(t, instance))
|
||||
Extern::Table(Table::from_wasmtime_table(t, store))
|
||||
}
|
||||
wasmtime_runtime::Export::Instance(i) => {
|
||||
let handle = unsafe { instance.store.existing_instance_handle(i.clone()) };
|
||||
Extern::Instance(Instance::from_wasmtime(handle))
|
||||
Extern::Instance(Instance::from_wasmtime(i, store))
|
||||
}
|
||||
wasmtime_runtime::Export::Module(m) => {
|
||||
Extern::Module(m.downcast_ref::<Module>().unwrap().clone())
|
||||
@@ -335,13 +334,13 @@ impl Global {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_global(
|
||||
wasmtime_export: wasmtime_runtime::ExportGlobal,
|
||||
instance: StoreInstanceHandle,
|
||||
pub(crate) unsafe fn from_wasmtime_global(
|
||||
wasmtime_export: &wasmtime_runtime::ExportGlobal,
|
||||
store: &Store,
|
||||
) -> Global {
|
||||
Global {
|
||||
instance,
|
||||
wasmtime_export,
|
||||
instance: store.existing_vmctx(wasmtime_export.vmctx),
|
||||
wasmtime_export: wasmtime_export.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +353,10 @@ impl Global {
|
||||
from: self.wasmtime_export.definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportGlobal {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
}
|
||||
|
||||
/// A WebAssembly `table`, or an array of values.
|
||||
@@ -576,13 +579,13 @@ impl Table {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_table(
|
||||
wasmtime_export: wasmtime_runtime::ExportTable,
|
||||
instance: StoreInstanceHandle,
|
||||
pub(crate) unsafe fn from_wasmtime_table(
|
||||
wasmtime_export: &wasmtime_runtime::ExportTable,
|
||||
store: &Store,
|
||||
) -> Table {
|
||||
Table {
|
||||
instance,
|
||||
wasmtime_export,
|
||||
instance: store.existing_vmctx(wasmtime_export.vmctx),
|
||||
wasmtime_export: wasmtime_export.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,6 +599,10 @@ impl Table {
|
||||
vmctx: self.wasmtime_export.vmctx,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportTable {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
}
|
||||
|
||||
/// A WebAssembly linear memory.
|
||||
@@ -987,13 +994,13 @@ impl Memory {
|
||||
.ok_or_else(|| anyhow!("failed to grow memory"))
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_memory(
|
||||
wasmtime_export: wasmtime_runtime::ExportMemory,
|
||||
instance: StoreInstanceHandle,
|
||||
pub(crate) unsafe fn from_wasmtime_memory(
|
||||
wasmtime_export: &wasmtime_runtime::ExportMemory,
|
||||
store: &Store,
|
||||
) -> Memory {
|
||||
Memory {
|
||||
instance,
|
||||
wasmtime_export,
|
||||
instance: store.existing_vmctx(wasmtime_export.vmctx),
|
||||
wasmtime_export: wasmtime_export.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1007,6 +1014,10 @@ impl Memory {
|
||||
vmctx: self.wasmtime_export.vmctx,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportMemory {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
}
|
||||
|
||||
/// A linear memory. This trait provides an interface for raw memory buffers which are used
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::store::StoreInner;
|
||||
use crate::trampoline::StoreInstanceHandle;
|
||||
use crate::{Extern, ExternRef, FuncType, Memory, Store, Trap, Val, ValType};
|
||||
use crate::{Extern, ExternRef, FuncType, Store, Trap, Val, ValType};
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::cmp::max;
|
||||
@@ -9,8 +9,9 @@ use std::mem;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::rc::Weak;
|
||||
use wasmtime_environ::wasm::EntityIndex;
|
||||
use wasmtime_runtime::{
|
||||
raise_user_trap, Export, InstanceHandle, VMContext, VMFunctionBody, VMSharedSignatureIndex,
|
||||
raise_user_trap, InstanceHandle, VMContext, VMFunctionBody, VMSharedSignatureIndex,
|
||||
VMTrampoline,
|
||||
};
|
||||
|
||||
@@ -331,11 +332,8 @@ impl Func {
|
||||
debug_assert!(
|
||||
anyfunc.as_ref().type_index != wasmtime_runtime::VMSharedSignatureIndex::default()
|
||||
);
|
||||
|
||||
let instance_handle = wasmtime_runtime::InstanceHandle::from_vmctx(anyfunc.as_ref().vmctx);
|
||||
let export = wasmtime_runtime::ExportFunction { anyfunc };
|
||||
let instance = store.existing_instance_handle(instance_handle);
|
||||
let f = Func::from_wasmtime_function(export, instance);
|
||||
let f = Func::from_wasmtime_function(&export, store);
|
||||
Some(f)
|
||||
}
|
||||
|
||||
@@ -649,24 +647,24 @@ impl Func {
|
||||
self.export.anyfunc
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_function(
|
||||
export: wasmtime_runtime::ExportFunction,
|
||||
instance: StoreInstanceHandle,
|
||||
pub(crate) unsafe fn from_wasmtime_function(
|
||||
export: &wasmtime_runtime::ExportFunction,
|
||||
store: &Store,
|
||||
) -> Self {
|
||||
// Each function signature in a module should have a trampoline stored
|
||||
// on that module as well, so unwrap the result here since otherwise
|
||||
// it's a bug in wasmtime.
|
||||
let trampoline = instance
|
||||
.store
|
||||
let anyfunc = export.anyfunc.as_ref();
|
||||
let trampoline = store
|
||||
.signatures()
|
||||
.borrow()
|
||||
.lookup_shared(unsafe { export.anyfunc.as_ref().type_index })
|
||||
.lookup_shared(anyfunc.type_index)
|
||||
.expect("failed to retrieve trampoline from module")
|
||||
.1;
|
||||
|
||||
Func {
|
||||
instance,
|
||||
export,
|
||||
instance: store.existing_vmctx(anyfunc.vmctx),
|
||||
export: export.clone(),
|
||||
trampoline,
|
||||
}
|
||||
}
|
||||
@@ -807,6 +805,10 @@ impl Func {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportFunction {
|
||||
&self.export
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Func {
|
||||
@@ -1506,10 +1508,16 @@ impl Caller<'_> {
|
||||
debug_assert!(self.store.upgrade().is_some());
|
||||
let handle =
|
||||
Store::from_inner(self.store.upgrade()?).existing_instance_handle(instance);
|
||||
let export = handle.lookup(name)?;
|
||||
match export {
|
||||
Export::Memory(m) => Some(Extern::Memory(Memory::from_wasmtime_memory(m, handle))),
|
||||
Export::Function(f) => Some(Extern::Func(Func::from_wasmtime_function(f, handle))),
|
||||
let index = handle.module().exports.get(name)?;
|
||||
match index {
|
||||
// Only allow memory/functions for now to emulate what interface
|
||||
// types will once provide
|
||||
EntityIndex::Memory(_) | EntityIndex::Function(_) => {
|
||||
Some(Extern::from_wasmtime_export(
|
||||
&handle.lookup_by_declaration(&index),
|
||||
&handle.store,
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
use crate::trampoline::StoreInstanceHandle;
|
||||
use crate::types::matching;
|
||||
use crate::{
|
||||
Engine, Export, Extern, ExternType, Func, Global, InstanceType, Memory, Module, Store, Table,
|
||||
Trap,
|
||||
Engine, Export, Extern, Func, Global, InstanceType, Memory, Module, Store, Table, Trap,
|
||||
};
|
||||
use anyhow::{bail, Context, Error, Result};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
use wasmtime_environ::entity::PrimaryMap;
|
||||
use wasmtime_environ::wasm::{
|
||||
EntityIndex, EntityType, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex,
|
||||
TableIndex,
|
||||
EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex,
|
||||
};
|
||||
use wasmtime_environ::Initializer;
|
||||
use wasmtime_jit::TypeTables;
|
||||
use wasmtime_runtime::{
|
||||
Imports, InstanceHandle, InstantiationError, StackMapRegistry, VMContext,
|
||||
Imports, InstantiationError, RuntimeInstance, StackMapRegistry, VMContext,
|
||||
VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport,
|
||||
VMTableImport,
|
||||
};
|
||||
@@ -36,8 +32,6 @@ use wasmtime_runtime::{
|
||||
/// itself.
|
||||
/// * `type` - the type tables produced during compilation which
|
||||
/// `compiled_module`'s metadata references.
|
||||
/// * `parent_modules` - this is the list of compiled modules the parent has.
|
||||
/// This is only applicable on recursive instantiations.
|
||||
/// * `define_import` - this function, like the name implies, defines an import
|
||||
/// into the provided builder. The expected entity that it's defining is also
|
||||
/// passed in for the top-level case where type-checking is performed. This is
|
||||
@@ -45,9 +39,13 @@ use wasmtime_runtime::{
|
||||
fn instantiate(
|
||||
store: &Store,
|
||||
module: &Module,
|
||||
parent_modules: &PrimaryMap<ModuleIndex, Module>,
|
||||
define_import: &mut dyn FnMut(&EntityIndex, &mut ImportsBuilder<'_>) -> Result<()>,
|
||||
) -> Result<StoreInstanceHandle, Error> {
|
||||
define_import: &mut dyn FnMut(
|
||||
&str,
|
||||
Option<&str>,
|
||||
&EntityIndex,
|
||||
&mut ImportsBuilder<'_>,
|
||||
) -> Result<()>,
|
||||
) -> Result<RuntimeInstance, Error> {
|
||||
let compiled_module = module.compiled_module();
|
||||
let env_module = compiled_module.module();
|
||||
|
||||
@@ -59,21 +57,9 @@ fn instantiate(
|
||||
// to fetching from the import list for the top-level module and
|
||||
// otherwise fetching from each nested instance's argument list for
|
||||
// submodules.
|
||||
Initializer::Import {
|
||||
index,
|
||||
module,
|
||||
field,
|
||||
} => {
|
||||
define_import(index, &mut imports).with_context(|| match field {
|
||||
Some(name) => format!("incompatible import type for `{}::{}`", module, name),
|
||||
None => format!("incompatible import type for `{}`", module),
|
||||
})?;
|
||||
}
|
||||
|
||||
// This one's pretty easy, we're just picking up our parent's module
|
||||
// and putting it into our own index space.
|
||||
Initializer::AliasParentModule(idx) => {
|
||||
imports.modules.push(parent_modules[*idx].clone());
|
||||
Initializer::Import { index, name, field } => {
|
||||
define_import(name, field.as_deref(), index, &mut imports)
|
||||
.with_context(|| format!("incompatible import type for `{}`", name))?;
|
||||
}
|
||||
|
||||
// Turns out defining any kind of module is pretty easy, we're just
|
||||
@@ -95,18 +81,8 @@ fn instantiate(
|
||||
// handle comes from our same store, but this should be true because
|
||||
// we acquired the handle from an instance in the store.
|
||||
Initializer::AliasInstanceExport { instance, export } => {
|
||||
let instance_ty = env_module.instances[*instance];
|
||||
let export_name = module.types().instance_signatures[instance_ty]
|
||||
.exports
|
||||
.get_index(*export)
|
||||
.expect("validation bug - should be valid")
|
||||
.0;
|
||||
let handle = &imports.instances[*instance];
|
||||
let entity_index = &handle.module().exports[export_name];
|
||||
let item = Extern::from_wasmtime_export(
|
||||
handle.lookup_by_declaration(entity_index),
|
||||
unsafe { store.existing_instance_handle(handle.clone()) },
|
||||
);
|
||||
let export = &imports.instances[*instance][export];
|
||||
let item = unsafe { Extern::from_wasmtime_export(export, store) };
|
||||
imports.push_extern(&item);
|
||||
}
|
||||
|
||||
@@ -127,13 +103,13 @@ fn instantiate(
|
||||
// we're doing all of this in the context of our `Store` argument
|
||||
// above so we should be safe here.
|
||||
Initializer::Instantiate { module, args } => {
|
||||
let mut args = args.iter();
|
||||
let handle = instantiate(
|
||||
store,
|
||||
&imports.modules[*module],
|
||||
&imports.modules,
|
||||
&mut |_, builder| {
|
||||
match *args.next().unwrap() {
|
||||
&mut |name, field, _, builder| {
|
||||
debug_assert!(field.is_none());
|
||||
let index = args.get(name).expect("should be present after validation");
|
||||
match *index {
|
||||
EntityIndex::Global(i) => {
|
||||
builder.globals.push(imports.globals[i]);
|
||||
}
|
||||
@@ -150,24 +126,17 @@ fn instantiate(
|
||||
builder.modules.push(imports.modules[i].clone());
|
||||
}
|
||||
EntityIndex::Instance(i) => {
|
||||
builder
|
||||
.instances
|
||||
.push(unsafe { imports.instances[i].clone() });
|
||||
builder.instances.push(imports.instances[i].clone());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
imports.instances.push(unsafe { (*handle).clone() });
|
||||
imports.instances.push(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// With the above initialization done we've now acquired the final set of
|
||||
// imports in all the right index spaces and everything. Time to carry on
|
||||
// with the creation of our own instance.
|
||||
let imports = imports.build();
|
||||
|
||||
// Register the module just before instantiation to ensure we have a
|
||||
// trampoline registered for every signature and to preserve the module's
|
||||
// compiled JIT code within the `Store`.
|
||||
@@ -176,11 +145,11 @@ fn instantiate(
|
||||
let config = store.engine().config();
|
||||
let instance = unsafe {
|
||||
let instance = compiled_module.instantiate(
|
||||
imports,
|
||||
imports.build(),
|
||||
&store.lookup_shared_signature(module.types()),
|
||||
config.memory_creator.as_ref().map(|a| a as _),
|
||||
store.interrupts(),
|
||||
Box::new(module.types().clone()),
|
||||
Box::new(()),
|
||||
store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,
|
||||
store.stack_map_registry() as *const StackMapRegistry as *mut _,
|
||||
)?;
|
||||
@@ -233,7 +202,29 @@ fn instantiate(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(instance)
|
||||
let exports = instance
|
||||
.handle
|
||||
.module()
|
||||
.exports
|
||||
.iter()
|
||||
.map(|(name, index)| {
|
||||
// Note that instances and modules are not handled by
|
||||
// `wasmtime_runtime`, they're handled by us in this crate. That
|
||||
// means we need to handle that here, otherwise we defer to the
|
||||
// instance to load the values.
|
||||
let item = match index {
|
||||
EntityIndex::Instance(i) => {
|
||||
wasmtime_runtime::Export::Instance(imports.instances[*i].clone())
|
||||
}
|
||||
EntityIndex::Module(i) => {
|
||||
wasmtime_runtime::Export::Module(Box::new(imports.modules[*i].clone()))
|
||||
}
|
||||
index => instance.handle.lookup_by_declaration(index),
|
||||
};
|
||||
(name.clone(), item)
|
||||
})
|
||||
.collect();
|
||||
Ok(Rc::new(exports))
|
||||
}
|
||||
|
||||
/// An instantiated WebAssembly module.
|
||||
@@ -254,7 +245,8 @@ fn instantiate(
|
||||
/// call any code or execute anything!
|
||||
#[derive(Clone)]
|
||||
pub struct Instance {
|
||||
pub(crate) handle: StoreInstanceHandle,
|
||||
pub(crate) store: Store,
|
||||
pub(crate) items: RuntimeInstance,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
@@ -316,7 +308,7 @@ impl Instance {
|
||||
bail!("cross-`Engine` instantiation is not currently supported");
|
||||
}
|
||||
|
||||
// Perform some pre-flight checks before we get into the meat of
|
||||
// Perform some pre-flight checks before we geet into the meat of
|
||||
// instantiation.
|
||||
let expected = module.compiled_module().module().imports().count();
|
||||
if expected != imports.len() {
|
||||
@@ -329,32 +321,26 @@ impl Instance {
|
||||
}
|
||||
|
||||
let mut imports = imports.iter();
|
||||
let handle = instantiate(store, module, &PrimaryMap::new(), &mut |idx, builder| {
|
||||
let items = instantiate(store, module, &mut |_name, _field, idx, builder| {
|
||||
let import = imports.next().expect("already checked the length");
|
||||
builder.define_extern(idx, import)
|
||||
builder.define_extern(idx, &import)
|
||||
})?;
|
||||
|
||||
Ok(Instance { handle })
|
||||
Ok(Instance::from_wasmtime(&items, store))
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime(handle: StoreInstanceHandle) -> Instance {
|
||||
Instance { handle }
|
||||
pub(crate) fn from_wasmtime(handle: &RuntimeInstance, store: &Store) -> Instance {
|
||||
Instance {
|
||||
items: handle.clone(),
|
||||
store: store.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type signature of this instance.
|
||||
pub fn ty(&self) -> InstanceType {
|
||||
let mut ty = InstanceType::new();
|
||||
let module = self.handle.module();
|
||||
let types = self
|
||||
.handle
|
||||
.host_state()
|
||||
.downcast_ref::<Arc<TypeTables>>()
|
||||
.unwrap();
|
||||
for (name, index) in module.exports.iter() {
|
||||
ty.add_named_export(
|
||||
name,
|
||||
ExternType::from_wasmtime(types, &module.type_of(*index)),
|
||||
);
|
||||
for export in self.exports() {
|
||||
ty.add_named_export(export.name(), export.ty());
|
||||
}
|
||||
ty
|
||||
}
|
||||
@@ -364,16 +350,15 @@ impl Instance {
|
||||
/// This is the [`Store`] that generally serves as a sort of global cache
|
||||
/// for various instance-related things.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.handle.store
|
||||
&self.store
|
||||
}
|
||||
|
||||
/// Returns the list of exported items from this [`Instance`].
|
||||
pub fn exports<'instance>(
|
||||
&'instance self,
|
||||
) -> impl ExactSizeIterator<Item = Export<'instance>> + 'instance {
|
||||
self.handle.exports().map(move |(name, entity_index)| {
|
||||
let export = self.handle.lookup_by_declaration(entity_index);
|
||||
let extern_ = Extern::from_wasmtime_export(export, self.handle.clone());
|
||||
self.items.iter().map(move |(name, item)| {
|
||||
let extern_ = unsafe { Extern::from_wasmtime_export(item, &self.store) };
|
||||
Export::new(name, extern_)
|
||||
})
|
||||
}
|
||||
@@ -385,8 +370,8 @@ impl Instance {
|
||||
///
|
||||
/// Returns `None` if there was no export named `name`.
|
||||
pub fn get_export(&self, name: &str) -> Option<Extern> {
|
||||
let export = self.handle.lookup(&name)?;
|
||||
Some(Extern::from_wasmtime_export(export, self.handle.clone()))
|
||||
let export = self.items.get(name)?;
|
||||
Some(unsafe { Extern::from_wasmtime_export(export, &self.store) })
|
||||
}
|
||||
|
||||
/// Looks up an exported [`Func`] value by name.
|
||||
@@ -427,7 +412,7 @@ struct ImportsBuilder<'a> {
|
||||
tables: PrimaryMap<TableIndex, VMTableImport>,
|
||||
memories: PrimaryMap<MemoryIndex, VMMemoryImport>,
|
||||
globals: PrimaryMap<GlobalIndex, VMGlobalImport>,
|
||||
instances: PrimaryMap<InstanceIndex, InstanceHandle>,
|
||||
instances: PrimaryMap<InstanceIndex, RuntimeInstance>,
|
||||
modules: PrimaryMap<ModuleIndex, Module>,
|
||||
|
||||
module: &'a wasmtime_environ::Module,
|
||||
@@ -452,36 +437,7 @@ impl<'a> ImportsBuilder<'a> {
|
||||
|
||||
fn define_extern(&mut self, expected: &EntityIndex, actual: &Extern) -> Result<()> {
|
||||
let expected_ty = self.module.type_of(*expected);
|
||||
let compatible = match &expected_ty {
|
||||
EntityType::Table(i) => match actual {
|
||||
Extern::Table(e) => self.matcher.table(i, e),
|
||||
_ => bail!("expected table, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Memory(i) => match actual {
|
||||
Extern::Memory(e) => self.matcher.memory(i, e),
|
||||
_ => bail!("expected memory, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Global(i) => match actual {
|
||||
Extern::Global(e) => self.matcher.global(i, e),
|
||||
_ => bail!("expected global, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Function(i) => match actual {
|
||||
Extern::Func(e) => self.matcher.func(*i, e),
|
||||
_ => bail!("expected func, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Instance(i) => match actual {
|
||||
Extern::Instance(e) => self.matcher.instance(*i, e),
|
||||
_ => bail!("expected instance, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Module(i) => match actual {
|
||||
Extern::Module(e) => self.matcher.module(*i, e),
|
||||
_ => bail!("expected module, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Event(_) => unimplemented!(),
|
||||
};
|
||||
if !compatible {
|
||||
bail!("{} types incompatible", actual.desc());
|
||||
}
|
||||
self.matcher.extern_(&expected_ty, actual)?;
|
||||
self.push_extern(actual);
|
||||
Ok(())
|
||||
}
|
||||
@@ -502,7 +458,7 @@ impl<'a> ImportsBuilder<'a> {
|
||||
}
|
||||
Extern::Instance(i) => {
|
||||
debug_assert!(Store::same(i.store(), self.matcher.store));
|
||||
self.instances.push(unsafe { (*i.handle).clone() });
|
||||
self.instances.push(i.items.clone());
|
||||
}
|
||||
Extern::Module(m) => {
|
||||
self.modules.push(m.clone());
|
||||
@@ -516,11 +472,40 @@ impl<'a> ImportsBuilder<'a> {
|
||||
globals: self.globals.values().as_slice(),
|
||||
memories: self.memories.values().as_slice(),
|
||||
functions: self.functions.values().as_slice(),
|
||||
instances: mem::take(&mut self.instances),
|
||||
modules: mem::take(&mut self.modules)
|
||||
.into_iter()
|
||||
.map(|(_, m)| Box::new(m) as Box<_>)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An internal structure to this crate to build an `Instance` from a list of
|
||||
/// items with names. This is intended to stay private for now, it'll need an
|
||||
/// audit of APIs if publicly exported.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct InstanceBuilder {
|
||||
items: RuntimeInstance,
|
||||
}
|
||||
|
||||
impl InstanceBuilder {
|
||||
pub(crate) fn new() -> InstanceBuilder {
|
||||
InstanceBuilder::default()
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&mut self, name: &str, item: impl Into<Extern>) {
|
||||
let items = Rc::get_mut(&mut self.items).unwrap();
|
||||
let export = match item.into() {
|
||||
Extern::Func(i) => wasmtime_runtime::Export::Function(i.wasmtime_export().clone()),
|
||||
Extern::Memory(i) => wasmtime_runtime::Export::Memory(i.wasmtime_export().clone()),
|
||||
Extern::Table(i) => wasmtime_runtime::Export::Table(i.wasmtime_export().clone()),
|
||||
Extern::Global(i) => wasmtime_runtime::Export::Global(i.wasmtime_export().clone()),
|
||||
Extern::Instance(i) => wasmtime_runtime::Export::Instance(i.items.clone()),
|
||||
Extern::Module(i) => wasmtime_runtime::Export::Module(Box::new(i.clone())),
|
||||
};
|
||||
items.insert(name.to_string(), export);
|
||||
}
|
||||
|
||||
pub(crate) fn finish(self, store: &Store) -> Instance {
|
||||
Instance {
|
||||
store: store.clone(),
|
||||
items: self.items,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::instance::InstanceBuilder;
|
||||
use crate::{
|
||||
Extern, ExternType, Func, FuncType, GlobalType, ImportType, Instance, IntoFunc, Module, Store,
|
||||
Trap,
|
||||
@@ -654,7 +655,37 @@ impl Linker {
|
||||
},
|
||||
kind: self.import_kind(import.ty()),
|
||||
};
|
||||
self.map.get(&key).cloned()
|
||||
if let Some(result) = self.map.get(&key).cloned() {
|
||||
return Some(result);
|
||||
}
|
||||
|
||||
// This is a key location where the module linking proposal is
|
||||
// implemented. This logic allows single-level imports of an instance to
|
||||
// get satisfied by multiple definitions of items within this `Linker`.
|
||||
//
|
||||
// The instance being import is iterated over to load the names from
|
||||
// this `Linker` (recursively calling `get`). If anything isn't defined
|
||||
// we return `None` since the entire value isn't defined. Otherwise when
|
||||
// all values are loaded it's assembled into an `Instance` and
|
||||
// returned`.
|
||||
//
|
||||
// Note that this isn't exactly the speediest implementation in the
|
||||
// world. Ideally we would pre-create the `Instance` instead of creating
|
||||
// it each time a module is instantiated. For now though while the
|
||||
// module linking proposal is under development this should hopefully
|
||||
// suffice.
|
||||
if let ExternType::Instance(t) = import.ty() {
|
||||
if import.name().is_none() {
|
||||
let mut builder = InstanceBuilder::new();
|
||||
for export in t.exports() {
|
||||
let item = self.get(&export.as_import(import.module()))?;
|
||||
builder.insert(export.name(), item);
|
||||
}
|
||||
return Some(builder.finish(&self.store).into());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns all items defined for the `module` and `name` pair.
|
||||
|
||||
@@ -245,12 +245,14 @@ impl Module {
|
||||
/// ```
|
||||
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
|
||||
#[cfg(feature = "cache")]
|
||||
let (artifacts, types) = ModuleCacheEntry::new("wasmtime", engine.cache_config())
|
||||
.get_data((engine.compiler(), binary), |(compiler, binary)| {
|
||||
CompilationArtifacts::build(compiler, binary)
|
||||
})?;
|
||||
let (main_module, artifacts, types) =
|
||||
ModuleCacheEntry::new("wasmtime", engine.cache_config())
|
||||
.get_data((engine.compiler(), binary), |(compiler, binary)| {
|
||||
CompilationArtifacts::build(compiler, binary)
|
||||
})?;
|
||||
#[cfg(not(feature = "cache"))]
|
||||
let (artifacts, types) = CompilationArtifacts::build(engine.compiler(), binary)?;
|
||||
let (main_module, artifacts, types) =
|
||||
CompilationArtifacts::build(engine.compiler(), binary)?;
|
||||
|
||||
let modules = CompiledModule::from_artifacts_list(
|
||||
artifacts,
|
||||
@@ -261,7 +263,7 @@ impl Module {
|
||||
let types = Arc::new(types);
|
||||
Ok(Module {
|
||||
engine: engine.clone(),
|
||||
index: 0,
|
||||
index: main_module,
|
||||
data: Arc::new(ModuleData { types, modules }),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ use std::sync::Arc;
|
||||
use wasmtime_environ::wasm;
|
||||
use wasmtime_jit::{CompiledModule, ModuleCode, TypeTables};
|
||||
use wasmtime_runtime::{
|
||||
InstanceHandle, RuntimeMemoryCreator, SignalHandler, StackMapRegistry, TrapInfo, VMExternRef,
|
||||
VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex,
|
||||
InstanceHandle, RuntimeMemoryCreator, SignalHandler, StackMapRegistry, TrapInfo, VMContext,
|
||||
VMExternRef, VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex,
|
||||
};
|
||||
|
||||
/// A `Store` is a collection of WebAssembly instances and host-defined items.
|
||||
@@ -234,6 +234,10 @@ impl Store {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn existing_vmctx(&self, cx: *mut VMContext) -> StoreInstanceHandle {
|
||||
self.existing_instance_handle(InstanceHandle::from_vmctx(cx))
|
||||
}
|
||||
|
||||
pub(crate) fn weak(&self) -> Weak<StoreInner> {
|
||||
Rc::downgrade(&self.inner)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
|
||||
module
|
||||
.initializers
|
||||
.push(wasmtime_environ::Initializer::Import {
|
||||
module: "".into(),
|
||||
name: "".into(),
|
||||
field: None,
|
||||
index: wasm::EntityIndex::Function(func_index),
|
||||
});
|
||||
@@ -80,7 +80,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
|
||||
)?;
|
||||
|
||||
if let Some(x) = externref_init {
|
||||
match handle.lookup("").unwrap() {
|
||||
match handle.lookup_by_declaration(&wasm::EntityIndex::Global(global_id)) {
|
||||
wasmtime_runtime::Export::Global(g) => unsafe {
|
||||
*(*g.definition).as_externref_mut() = Some(x.inner);
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::{FuncType, GlobalType, MemoryType, Store, TableType, Trap, Val};
|
||||
use anyhow::Result;
|
||||
use std::any::Any;
|
||||
use std::ops::Deref;
|
||||
use wasmtime_environ::wasm;
|
||||
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody, VMTrampoline};
|
||||
|
||||
/// A wrapper around `wasmtime_runtime::InstanceHandle` which pairs it with the
|
||||
@@ -55,7 +56,8 @@ pub fn generate_func_export(
|
||||
VMTrampoline,
|
||||
)> {
|
||||
let (instance, trampoline) = create_handle_with_function(ft, func, store)?;
|
||||
match instance.lookup("").expect("trampoline export") {
|
||||
let idx = wasm::EntityIndex::Function(wasm::FuncIndex::from_u32(0));
|
||||
match instance.lookup_by_declaration(&idx) {
|
||||
wasmtime_runtime::Export::Function(f) => Ok((instance, f, trampoline)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -72,7 +74,8 @@ pub unsafe fn generate_raw_func_export(
|
||||
state: Box<dyn Any>,
|
||||
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportFunction)> {
|
||||
let instance = func::create_handle_with_raw_function(ft, func, trampoline, store, state)?;
|
||||
match instance.lookup("").expect("trampoline export") {
|
||||
let idx = wasm::EntityIndex::Function(wasm::FuncIndex::from_u32(0));
|
||||
match instance.lookup_by_declaration(&idx) {
|
||||
wasmtime_runtime::Export::Function(f) => Ok((instance, f)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -84,7 +87,8 @@ pub fn generate_global_export(
|
||||
val: Val,
|
||||
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportGlobal)> {
|
||||
let instance = create_global(store, gt, val)?;
|
||||
match instance.lookup("").expect("global export") {
|
||||
let idx = wasm::EntityIndex::Global(wasm::GlobalIndex::from_u32(0));
|
||||
match instance.lookup_by_declaration(&idx) {
|
||||
wasmtime_runtime::Export::Global(g) => Ok((instance, g)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -95,7 +99,8 @@ pub fn generate_memory_export(
|
||||
m: &MemoryType,
|
||||
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportMemory)> {
|
||||
let instance = create_handle_with_memory(store, m)?;
|
||||
match instance.lookup("").expect("memory export") {
|
||||
let idx = wasm::EntityIndex::Memory(wasm::MemoryIndex::from_u32(0));
|
||||
match instance.lookup_by_declaration(&idx) {
|
||||
wasmtime_runtime::Export::Memory(m) => Ok((instance, m)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -106,7 +111,8 @@ pub fn generate_table_export(
|
||||
t: &TableType,
|
||||
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportTable)> {
|
||||
let instance = create_handle_with_table(store, t)?;
|
||||
match instance.lookup("").expect("table export") {
|
||||
let idx = wasm::EntityIndex::Table(wasm::TableIndex::from_u32(0));
|
||||
match instance.lookup_by_declaration(&idx) {
|
||||
wasmtime_runtime::Export::Table(t) => Ok((instance, t)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Stor
|
||||
|
||||
let table_plan = wasmtime_environ::TablePlan::for_table(table, &tunable);
|
||||
let table_id = module.table_plans.push(table_plan);
|
||||
// TODO: can this `exports.insert` get removed?
|
||||
module
|
||||
.exports
|
||||
.insert(String::new(), wasm::EntityIndex::Table(table_id));
|
||||
|
||||
@@ -466,21 +466,21 @@ impl ModuleType {
|
||||
}
|
||||
|
||||
/// Adds a new export to this `ModuleType`.
|
||||
pub fn add_named_export(&mut self, name: &str, ty: ExternType) {
|
||||
pub(crate) fn add_named_export(&mut self, name: &str, ty: ExternType) {
|
||||
self.exports.push((name.to_string(), ty));
|
||||
}
|
||||
|
||||
/// Adds a new import to this `ModuleType`.
|
||||
pub fn add_named_import(&mut self, module: &str, field: Option<&str>, ty: ExternType) {
|
||||
pub(crate) fn add_named_import(&mut self, module: &str, field: Option<&str>, ty: ExternType) {
|
||||
self.imports
|
||||
.push((module.to_string(), field.map(|f| f.to_string()), ty));
|
||||
}
|
||||
|
||||
/// Returns the list of imports associated with this module type.
|
||||
pub fn imports(&self) -> impl ExactSizeIterator<Item = ImportType<'_>> {
|
||||
self.imports.iter().map(|(module, name, ty)| ImportType {
|
||||
module,
|
||||
name: name.as_deref(),
|
||||
self.imports.iter().map(|(name, field, ty)| ImportType {
|
||||
module: name,
|
||||
name: field.as_deref(),
|
||||
ty: EntityOrExtern::Extern(ty),
|
||||
})
|
||||
}
|
||||
@@ -506,13 +506,7 @@ impl ModuleType {
|
||||
imports: ty
|
||||
.imports
|
||||
.iter()
|
||||
.map(|(m, name, ty)| {
|
||||
(
|
||||
m.to_string(),
|
||||
name.as_ref().map(|n| n.to_string()),
|
||||
ExternType::from_wasmtime(types, ty),
|
||||
)
|
||||
})
|
||||
.map(|(m, ty)| (m.to_string(), None, ExternType::from_wasmtime(types, ty)))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
@@ -615,8 +609,9 @@ impl<'module> ImportType<'module> {
|
||||
/// Returns the field name of the module that this import is expected to
|
||||
/// come from.
|
||||
///
|
||||
/// Note that the name can be `None` for the module linking proposal. If the
|
||||
/// module linking proposal is not enabled it's safe to unwrap this.
|
||||
/// Note that this is optional due to the module linking proposal. If the
|
||||
/// module linking proposal is enabled this is always `None`, otherwise this
|
||||
/// is always `Some`.
|
||||
pub fn name(&self) -> Option<&'module str> {
|
||||
self.name
|
||||
}
|
||||
@@ -683,6 +678,17 @@ impl<'module> ExportType<'module> {
|
||||
EntityOrExtern::Extern(e) => (*e).clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_import<'a>(&self, module: &'a str) -> ImportType<'a>
|
||||
where
|
||||
'module: 'a,
|
||||
{
|
||||
ImportType {
|
||||
module,
|
||||
name: Some(self.name),
|
||||
ty: self.ty.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'module> fmt::Debug for ExportType<'module> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::Store;
|
||||
use std::sync::Arc;
|
||||
use crate::{Extern, Store};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use wasmtime_environ::wasm::{
|
||||
EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table,
|
||||
};
|
||||
@@ -11,22 +11,27 @@ pub struct MatchCx<'a> {
|
||||
}
|
||||
|
||||
impl MatchCx<'_> {
|
||||
pub fn global(&self, expected: &Global, actual: &crate::Global) -> bool {
|
||||
pub fn global(&self, expected: &Global, actual: &crate::Global) -> Result<()> {
|
||||
self.global_ty(expected, actual.wasmtime_ty())
|
||||
}
|
||||
|
||||
fn global_ty(&self, expected: &Global, actual: &Global) -> bool {
|
||||
expected.ty == actual.ty
|
||||
fn global_ty(&self, expected: &Global, actual: &Global) -> Result<()> {
|
||||
if expected.ty == actual.ty
|
||||
&& expected.wasm_ty == actual.wasm_ty
|
||||
&& expected.mutability == actual.mutability
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("global types incompatible")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn table(&self, expected: &Table, actual: &crate::Table) -> bool {
|
||||
pub fn table(&self, expected: &Table, actual: &crate::Table) -> Result<()> {
|
||||
self.table_ty(expected, actual.wasmtime_ty())
|
||||
}
|
||||
|
||||
fn table_ty(&self, expected: &Table, actual: &Table) -> bool {
|
||||
expected.wasm_ty == actual.wasm_ty
|
||||
fn table_ty(&self, expected: &Table, actual: &Table) -> Result<()> {
|
||||
if expected.wasm_ty == actual.wasm_ty
|
||||
&& expected.ty == actual.ty
|
||||
&& expected.minimum <= actual.minimum
|
||||
&& match expected.maximum {
|
||||
@@ -36,14 +41,19 @@ impl MatchCx<'_> {
|
||||
},
|
||||
None => true,
|
||||
}
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("table types incompatible")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> bool {
|
||||
pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> Result<()> {
|
||||
self.memory_ty(expected, actual.wasmtime_ty())
|
||||
}
|
||||
|
||||
fn memory_ty(&self, expected: &Memory, actual: &Memory) -> bool {
|
||||
expected.shared == actual.shared
|
||||
fn memory_ty(&self, expected: &Memory, actual: &Memory) -> Result<()> {
|
||||
if expected.shared == actual.shared
|
||||
&& expected.minimum <= actual.minimum
|
||||
&& match expected.maximum {
|
||||
Some(expected) => match actual.maximum {
|
||||
@@ -52,10 +62,15 @@ impl MatchCx<'_> {
|
||||
},
|
||||
None => true,
|
||||
}
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("memory types incompatible")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> bool {
|
||||
match self
|
||||
pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> Result<()> {
|
||||
let matches = match self
|
||||
.store
|
||||
.signatures()
|
||||
.borrow()
|
||||
@@ -65,31 +80,50 @@ impl MatchCx<'_> {
|
||||
// If our expected signature isn't registered, then there's no way
|
||||
// that `actual` can match it.
|
||||
None => false,
|
||||
};
|
||||
if matches {
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("function types incompatible")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instance(&self, expected: InstanceTypeIndex, actual: &crate::Instance) -> bool {
|
||||
let module = actual.handle.module();
|
||||
self.exports_match(
|
||||
expected,
|
||||
actual
|
||||
.handle
|
||||
.host_state()
|
||||
.downcast_ref::<Arc<TypeTables>>()
|
||||
.unwrap(),
|
||||
|name| module.exports.get(name).map(|idx| module.type_of(*idx)),
|
||||
)
|
||||
pub fn instance(&self, expected: InstanceTypeIndex, actual: &crate::Instance) -> Result<()> {
|
||||
for (name, expected) in self.types.instance_signatures[expected].exports.iter() {
|
||||
match actual.items.get(name) {
|
||||
Some(item) => {
|
||||
let item = unsafe { Extern::from_wasmtime_export(item, self.store) };
|
||||
self.extern_(expected, &item)
|
||||
.with_context(|| format!("instance export {:?} incompatible", name))?;
|
||||
}
|
||||
None => bail!("instance type missing export {:?}", name),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that the type signature of `actual` matches the `expected`
|
||||
/// module type signature.
|
||||
pub fn module(&self, expected: ModuleTypeIndex, actual: &crate::Module) -> bool {
|
||||
pub fn module(&self, expected: ModuleTypeIndex, actual: &crate::Module) -> Result<()> {
|
||||
// This should only ever be invoked with module linking, and this is an
|
||||
// early check that our `field` assertion below should always work as
|
||||
// well.
|
||||
assert!(self.store.engine().config().features.module_linking);
|
||||
|
||||
let expected_sig = &self.types.module_signatures[expected];
|
||||
let module = actual.compiled_module().module();
|
||||
self.imports_match(expected, actual.types(), module.imports())
|
||||
&& self.exports_match(expected_sig.exports, actual.types(), |name| {
|
||||
module.exports.get(name).map(|idx| module.type_of(*idx))
|
||||
})
|
||||
self.imports_match(
|
||||
expected,
|
||||
actual.types(),
|
||||
module.imports().map(|(name, field, ty)| {
|
||||
assert!(field.is_none()); // should be true if module linking is enabled
|
||||
(name, ty)
|
||||
}),
|
||||
)?;
|
||||
self.exports_match(expected_sig.exports, actual.types(), |name| {
|
||||
module.exports.get(name).map(|idx| module.type_of(*idx))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that the `actual_imports` list of module imports matches the
|
||||
@@ -100,19 +134,25 @@ impl MatchCx<'_> {
|
||||
&self,
|
||||
expected: ModuleTypeIndex,
|
||||
actual_types: &TypeTables,
|
||||
mut actual_imports: impl Iterator<Item = (&'a str, Option<&'a str>, EntityType)>,
|
||||
) -> bool {
|
||||
actual_imports: impl Iterator<Item = (&'a str, EntityType)>,
|
||||
) -> Result<()> {
|
||||
// Imports match if all of the actual imports are satisfied by the
|
||||
// expected set of imports. Note that we're reversing the order of the
|
||||
// subtytpe matching here too.
|
||||
let expected_sig = &self.types.module_signatures[expected];
|
||||
for (_, _, expected) in expected_sig.imports.iter() {
|
||||
let (_, _, ty) = match actual_imports.next() {
|
||||
Some(e) => e,
|
||||
None => return false,
|
||||
for (name, actual_ty) in actual_imports {
|
||||
let expected_ty = match expected_sig.imports.get(name) {
|
||||
Some(ty) => ty,
|
||||
None => bail!("expected type doesn't import {:?}", name),
|
||||
};
|
||||
if !self.extern_ty_matches(expected, &ty, actual_types) {
|
||||
return false;
|
||||
MatchCx {
|
||||
types: actual_types,
|
||||
store: self.store,
|
||||
}
|
||||
.extern_ty_matches(&actual_ty, expected_ty, self.types)
|
||||
.with_context(|| format!("module import {:?} incompatible", name))?;
|
||||
}
|
||||
actual_imports.next().is_none()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that all exports in `expected` are defined by `lookup` within
|
||||
@@ -122,16 +162,19 @@ impl MatchCx<'_> {
|
||||
expected: InstanceTypeIndex,
|
||||
actual_types: &TypeTables,
|
||||
lookup: impl Fn(&str) -> Option<EntityType>,
|
||||
) -> bool {
|
||||
) -> Result<()> {
|
||||
// The `expected` type must be a subset of `actual`, meaning that all
|
||||
// names in `expected` must be present in `actual`. Note that we do
|
||||
// name-based lookup here instead of index-based lookup.
|
||||
self.types.instance_signatures[expected].exports.iter().all(
|
||||
|(name, expected)| match lookup(name) {
|
||||
Some(ty) => self.extern_ty_matches(expected, &ty, actual_types),
|
||||
None => false,
|
||||
},
|
||||
)
|
||||
for (name, expected) in self.types.instance_signatures[expected].exports.iter() {
|
||||
match lookup(name) {
|
||||
Some(ty) => self
|
||||
.extern_ty_matches(expected, &ty, actual_types)
|
||||
.with_context(|| format!("export {:?} incompatible", name))?,
|
||||
None => bail!("failed to find export {:?}", name),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that the `expected` entity matches the `actual_ty` defined
|
||||
@@ -141,34 +184,49 @@ impl MatchCx<'_> {
|
||||
expected: &EntityType,
|
||||
actual_ty: &EntityType,
|
||||
actual_types: &TypeTables,
|
||||
) -> bool {
|
||||
) -> Result<()> {
|
||||
let actual_desc = match actual_ty {
|
||||
EntityType::Global(_) => "global",
|
||||
EntityType::Module(_) => "module",
|
||||
EntityType::Memory(_) => "memory",
|
||||
EntityType::Event(_) => "event",
|
||||
EntityType::Instance(_) => "instance",
|
||||
EntityType::Table(_) => "table",
|
||||
EntityType::Function(_) => "function",
|
||||
};
|
||||
match expected {
|
||||
EntityType::Global(expected) => match actual_ty {
|
||||
EntityType::Global(actual) => self.global_ty(expected, actual),
|
||||
_ => false,
|
||||
_ => bail!("expected global, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Table(expected) => match actual_ty {
|
||||
EntityType::Table(actual) => self.table_ty(expected, actual),
|
||||
_ => false,
|
||||
_ => bail!("expected table, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Memory(expected) => match actual_ty {
|
||||
EntityType::Memory(actual) => self.memory_ty(expected, actual),
|
||||
_ => false,
|
||||
_ => bail!("expected memory, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Function(expected) => match *actual_ty {
|
||||
EntityType::Function(actual) => {
|
||||
self.types.wasm_signatures[*expected] == actual_types.wasm_signatures[actual]
|
||||
if self.types.wasm_signatures[*expected] == actual_types.wasm_signatures[actual]
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("function types incompatible")
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
_ => bail!("expected function, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Instance(expected) => match actual_ty {
|
||||
EntityType::Instance(actual) => {
|
||||
let sig = &actual_types.instance_signatures[*actual];
|
||||
self.exports_match(*expected, actual_types, |name| {
|
||||
sig.exports.get(name).cloned()
|
||||
})
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
_ => false,
|
||||
_ => bail!("expected instance, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Module(expected) => match actual_ty {
|
||||
EntityType::Module(actual) => {
|
||||
@@ -180,14 +238,48 @@ impl MatchCx<'_> {
|
||||
self.imports_match(
|
||||
*expected,
|
||||
actual_types,
|
||||
actual_module_sig.imports.iter().map(|(module, field, ty)| {
|
||||
(module.as_str(), field.as_deref(), ty.clone())
|
||||
}),
|
||||
) && self.exports_match(expected_module_sig.exports, actual_types, |name| {
|
||||
actual_module_sig
|
||||
.imports
|
||||
.iter()
|
||||
.map(|(module, ty)| (module.as_str(), ty.clone())),
|
||||
)?;
|
||||
self.exports_match(expected_module_sig.exports, actual_types, |name| {
|
||||
actual_instance_sig.exports.get(name).cloned()
|
||||
})
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
_ => false,
|
||||
_ => bail!("expected module, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Event(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that the `expected` type matches the type of `actual`
|
||||
pub fn extern_(&self, expected: &EntityType, actual: &Extern) -> Result<()> {
|
||||
match expected {
|
||||
EntityType::Global(expected) => match actual {
|
||||
Extern::Global(actual) => self.global(expected, actual),
|
||||
_ => bail!("expected global, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Table(expected) => match actual {
|
||||
Extern::Table(actual) => self.table(expected, actual),
|
||||
_ => bail!("expected table, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Memory(expected) => match actual {
|
||||
Extern::Memory(actual) => self.memory(expected, actual),
|
||||
_ => bail!("expected memory, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Function(expected) => match actual {
|
||||
Extern::Func(actual) => self.func(*expected, actual),
|
||||
_ => bail!("expected func, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Instance(expected) => match actual {
|
||||
Extern::Instance(actual) => self.instance(*expected, actual),
|
||||
_ => bail!("expected instance, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Module(expected) => match actual {
|
||||
Extern::Module(actual) => self.module(*expected, actual),
|
||||
_ => bail!("expected module, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Event(_) => unimplemented!(),
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
anyhow = "1.0.19"
|
||||
wasmtime = { path = "../wasmtime", version = "0.22.0", default-features = false }
|
||||
wast = "29.0.0"
|
||||
wast = "31.0.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -17,7 +17,7 @@ target-lexicon = "0.11"
|
||||
peepmatic-fuzzing = { path = "../cranelift/peepmatic/crates/fuzzing", optional = true }
|
||||
wasmtime = { path = "../crates/wasmtime" }
|
||||
wasmtime-fuzzing = { path = "../crates/fuzzing" }
|
||||
wasm-smith = "0.3.0"
|
||||
wasm-smith = "0.3.1"
|
||||
|
||||
[features]
|
||||
experimental_x64 = ["wasmtime-fuzzing/experimental_x64"]
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use std::time::Duration;
|
||||
use wasm_smith::{ConfiguredModule, SwarmConfig};
|
||||
use wasm_smith::{Config, ConfiguredModule, SwarmConfig};
|
||||
use wasmtime::Strategy;
|
||||
use wasmtime_fuzzing::oracles;
|
||||
|
||||
fuzz_target!(|module: ConfiguredModule<SwarmConfig>| {
|
||||
let mut cfg = wasmtime_fuzzing::fuzz_default_config(Strategy::Auto).unwrap();
|
||||
cfg.wasm_multi_memory(true);
|
||||
cfg.wasm_module_linking(module.config().module_linking_enabled());
|
||||
oracles::instantiate_with_config(&module.to_bytes(), true, cfg, Some(Duration::from_secs(20)));
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ pub fn compile_to_obj(
|
||||
);
|
||||
|
||||
let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables, &features);
|
||||
let (mut translation, types) = environ
|
||||
let (_main_module, mut translation, types) = environ
|
||||
.translate(wasm)
|
||||
.context("failed to translate module")?;
|
||||
assert_eq!(translation.len(), 1);
|
||||
|
||||
@@ -105,8 +105,13 @@ fn imports_exports() -> Result<()> {
|
||||
assert_eq!(i.len(), 1);
|
||||
let import = i.next().unwrap();
|
||||
assert_eq!(import.module(), "");
|
||||
assert_eq!(import.name(), Some("a"));
|
||||
let module_ty = match import.ty() {
|
||||
assert_eq!(import.name(), None);
|
||||
let instance_ty = match import.ty() {
|
||||
ExternType::Instance(t) => t,
|
||||
_ => panic!("unexpected type"),
|
||||
};
|
||||
assert_eq!(instance_ty.exports().len(), 1);
|
||||
let module_ty = match instance_ty.exports().next().unwrap().ty() {
|
||||
ExternType::Module(m) => m,
|
||||
_ => panic!("unexpected type"),
|
||||
};
|
||||
@@ -148,8 +153,13 @@ fn imports_exports() -> Result<()> {
|
||||
assert_eq!(i.len(), 1);
|
||||
let import = i.next().unwrap();
|
||||
assert_eq!(import.module(), "");
|
||||
assert_eq!(import.name(), Some("b"));
|
||||
assert_eq!(import.name(), None);
|
||||
let instance_ty = match import.ty() {
|
||||
ExternType::Instance(t) => t,
|
||||
_ => panic!("unexpected type"),
|
||||
};
|
||||
assert_eq!(instance_ty.exports().len(), 1);
|
||||
let instance_ty = match instance_ty.exports().next().unwrap().ty() {
|
||||
ExternType::Instance(m) => m,
|
||||
_ => panic!("unexpected type"),
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(instance $a (instantiate $m))
|
||||
|
||||
(func (export "get") (result i32)
|
||||
call $a.$foo)
|
||||
call (func $a "foo"))
|
||||
)
|
||||
(assert_return (invoke "get") (i32.const 1))
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
(instance $a (instantiate $m))
|
||||
|
||||
(func (export "get") (result i32)
|
||||
global.get $a.$g)
|
||||
global.get (global $a "g"))
|
||||
)
|
||||
(assert_return (invoke "get") (i32.const 2))
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
(data (i32.const 0) "\03\00\00\00")
|
||||
)
|
||||
(instance $a (instantiate $m))
|
||||
(alias (instance $a) (memory $m))
|
||||
(alias $m (memory $a "m"))
|
||||
|
||||
(func (export "get") (result i32)
|
||||
i32.const 0
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
(func (export "get") (result i32)
|
||||
i32.const 0
|
||||
call_indirect $a.$t (result i32))
|
||||
call_indirect (table $a "t") (result i32))
|
||||
)
|
||||
(assert_return (invoke "get") (i32.const 4))
|
||||
|
||||
@@ -62,11 +62,10 @@
|
||||
i32.const 5))
|
||||
)
|
||||
(instance $a (instantiate $m))
|
||||
(instance $b (instantiate $a.$sub))
|
||||
(alias $b.$f (instance $b) (func 0))
|
||||
(instance $b (instantiate (module $a "module")))
|
||||
|
||||
(func (export "get") (result i32)
|
||||
call $b.$f)
|
||||
call (func $b ""))
|
||||
)
|
||||
(assert_return (invoke "get") (i32.const 5))
|
||||
|
||||
@@ -79,11 +78,9 @@
|
||||
(instance $i (export "") (instantiate $sub))
|
||||
)
|
||||
(instance $a (instantiate $m))
|
||||
(alias $a.$i (instance $a) (instance 0))
|
||||
(alias $a.$i.$f (instance $a.$i) (func 0))
|
||||
|
||||
(func (export "get") (result i32)
|
||||
call $a.$i.$f)
|
||||
call (func $a "" ""))
|
||||
)
|
||||
(assert_return (invoke "get") (i32.const 6))
|
||||
|
||||
@@ -91,48 +88,51 @@
|
||||
(module
|
||||
(type $t (func))
|
||||
(module $m
|
||||
(func $f (type $t))
|
||||
(func $f (type outer 0 $t))
|
||||
)
|
||||
(instance $a (instantiate $m))
|
||||
)
|
||||
|
||||
;; alias parent -- module
|
||||
(; TODO
|
||||
(module
|
||||
(module $a)
|
||||
(module $m
|
||||
(instance (instantiate $a))
|
||||
(instance (instantiate (module outer 0 $a)))
|
||||
)
|
||||
(instance (instantiate $m))
|
||||
)
|
||||
;)
|
||||
|
||||
;; The alias, import, type, module, and instance sections can all be interleaved
|
||||
(module
|
||||
(module $ROOT
|
||||
(module $a)
|
||||
(type $t (func))
|
||||
(module $m
|
||||
;; alias
|
||||
(alias $thunk parent (type $t))
|
||||
(alias $thunk (type outer 0 $t))
|
||||
;; import
|
||||
(import "" "" (func (type $thunk)))
|
||||
;; module (referencing parent type)
|
||||
(module
|
||||
(func (type $thunk))
|
||||
(func (type outer $m $thunk))
|
||||
(func (type outer $ROOT $t))
|
||||
)
|
||||
;; type
|
||||
(type $thunk2 (func))
|
||||
;; module (referencing previous alias)
|
||||
(module $m2
|
||||
(func (export "") (type $thunk2))
|
||||
(func (export "") (type outer $m $thunk2))
|
||||
)
|
||||
;; instance
|
||||
(instance $i (instantiate $m2))
|
||||
;; alias that instance
|
||||
(alias $my_f (instance $i) (func 0))
|
||||
(alias $my_f (func $i ""))
|
||||
;; module
|
||||
(module $m3
|
||||
(import "" (func)))
|
||||
;; use our aliased function to create the module
|
||||
(instance $i2 (instantiate $m3 (func $my_f)))
|
||||
(instance $i2 (instantiate $m3 "" (func $my_f)))
|
||||
;; module
|
||||
(module $m4
|
||||
(import "" (func)))
|
||||
@@ -141,5 +141,5 @@
|
||||
;; instantiate the above module
|
||||
(module $smol (func $f (export "")))
|
||||
(instance $smol (instantiate $smol))
|
||||
(instance (instantiate $m (func $smol.$f)))
|
||||
(instance (instantiate $m "" (instance $smol)))
|
||||
)
|
||||
|
||||
@@ -9,22 +9,36 @@
|
||||
|
||||
(module
|
||||
(import "a" "m" (module))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "" (func))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "a" (func))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "b" (global i32))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module
|
||||
(export "" (func))
|
||||
(export "a" (func))
|
||||
))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module
|
||||
(export "a" (func))
|
||||
(export "" (func))
|
||||
))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module
|
||||
(export "a" (func))
|
||||
(export "" (func))
|
||||
(export "b" (global i32))
|
||||
))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module
|
||||
(export "b" (global i32))
|
||||
(export "a" (func))
|
||||
@@ -37,64 +51,60 @@
|
||||
(module (export "m")
|
||||
(func (export ""))))
|
||||
|
||||
(module
|
||||
(import "a" "m" (module))
|
||||
(import "a" "m" (module (export "" (func))))
|
||||
)
|
||||
(module (import "a" "m" (module)))
|
||||
(module (import "a" "m" (module (export "" (func)))))
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (func (param i32))))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (func (result i32))))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (global i32)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (table 1 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (memory 1)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (module)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (instance)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
|
||||
(module $a
|
||||
(module (export "m")
|
||||
(global (export "") i32 (i32.const 0))))
|
||||
|
||||
;; globals
|
||||
(module
|
||||
(import "a" "m" (module))
|
||||
(import "a" "m" (module (export "" (global i32))))
|
||||
)
|
||||
(module (import "a" "m" (module)))
|
||||
(module (import "a" "m" (module (export "" (global i32)))))
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(import "a" "m" (module (export "" (global (mut i32)))))
|
||||
)
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (global f32)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (func)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (table 1 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (memory 1)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (module)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (instance)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
|
||||
;; tables
|
||||
(module $a
|
||||
@@ -105,40 +115,52 @@
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "" (table 1 funcref))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "" (table 0 funcref))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "max" (table 1 10 funcref))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "max" (table 0 10 funcref))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "max" (table 0 11 funcref))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "max" (table 0 funcref))))
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (global f32)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (func)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (table 2 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (table 1 10 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "max" (table 2 10 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "max" (table 1 9 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (memory 1)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (module)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (instance)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
|
||||
;; memories
|
||||
(module $a
|
||||
@@ -149,40 +171,52 @@
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "" (memory 1))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "" (memory 0))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "max" (memory 1 10))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "max" (memory 0 10))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "max" (memory 0 11))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "max" (memory 0))))
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (global f32)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (func)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (table 1 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (memory 2)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (memory 1 10)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "max" (memory 2 10)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "max" (memory 2)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (module)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (instance)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
|
||||
;; modules
|
||||
(module $a
|
||||
@@ -206,72 +240,102 @@
|
||||
)
|
||||
;; import a mixture
|
||||
(module (export "e")
|
||||
(import "" (func))
|
||||
(import "" (func))
|
||||
(import "" (global i32))
|
||||
(import "a" (func))
|
||||
(import "b" (func))
|
||||
(import "c" (global i32))
|
||||
)
|
||||
)
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "a" (module))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "b" (module))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "b" (module (export "" (func))))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "c" (module))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "c" (module
|
||||
(export "a" (func))
|
||||
))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "c" (module
|
||||
(export "a" (func))
|
||||
(export "b" (func (result i32)))
|
||||
))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "c" (module
|
||||
(export "c" (global i32))
|
||||
))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "c" (module
|
||||
(export "c" (global i32))
|
||||
(export "a" (func))
|
||||
))))
|
||||
|
||||
;; for now import strings aren't matched at all, imports must simply pairwise
|
||||
;; line up
|
||||
(import "a" "m" (module (export "d" (module (import "" (func))))))
|
||||
(import "a" "m" (module (export "d" (module (import "x" (func))))))
|
||||
(import "a" "m" (module (export "d" (module (import "x" "y" (func))))))
|
||||
|
||||
(import "a" "m" (module (export "e" (module
|
||||
(import "x" "y" (func))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "d" (module
|
||||
(import "" (func))
|
||||
(import "a" (func))
|
||||
(import "z" (global i32))
|
||||
))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "d" (module (import "" (func))))))
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(import "a" "m" (module (export "d" (module (import "x" (func))))))
|
||||
)
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(import "a" "m" (module (export "d" (module (import "x" "y" (func))))))
|
||||
)
|
||||
"incompatible import type for `a`")
|
||||
(module
|
||||
(import "a" "m" (module (export "e" (module
|
||||
(import "a" (func))
|
||||
(import "b" (func))
|
||||
(import "c" (global i32))
|
||||
))))
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (module (export "a" (func)))))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "d" (module)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "d" (module (import "" (module)))))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (global f32)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (func)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (table 1 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (memory 2)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (module (export "foo" (func)))))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (instance)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
|
||||
;; instances
|
||||
(module $a
|
||||
@@ -303,46 +367,65 @@
|
||||
)
|
||||
(module
|
||||
(import "a" "a" (instance))
|
||||
)
|
||||
(module
|
||||
(import "a" "b" (instance))
|
||||
)
|
||||
(module
|
||||
(import "a" "b" (instance (export "" (func))))
|
||||
)
|
||||
(module
|
||||
(import "a" "c" (instance))
|
||||
)
|
||||
(module
|
||||
(import "a" "c" (instance (export "a" (func))))
|
||||
)
|
||||
(module
|
||||
(import "a" "c" (instance (export "b" (func (result i32)))))
|
||||
)
|
||||
(module
|
||||
(import "a" "c" (instance (export "c" (global i32))))
|
||||
)
|
||||
(module
|
||||
(import "a" "c" (instance
|
||||
(export "a" (func))
|
||||
(export "b" (func (result i32)))
|
||||
(export "c" (global i32))
|
||||
))
|
||||
)
|
||||
(module
|
||||
(import "a" "c" (instance
|
||||
(export "c" (global i32))
|
||||
(export "a" (func))
|
||||
))
|
||||
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "i" (instance))))
|
||||
)
|
||||
(module
|
||||
(import "a" "m" (module (export "i" (instance (export "" (func))))))
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module (import "a" "a" (instance (export "" (global f32)))))
|
||||
"instance types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "i" (instance (export "x" (func)))))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (func)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (table 1 funcref)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (memory 2)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (memory 1 10)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "max" (memory 2 10)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
(assert_unlinkable
|
||||
(module (import "a" "m" (module (export "" (module)))))
|
||||
"module types incompatible")
|
||||
"incompatible import type for `a`")
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
(module
|
||||
(import "" (func))
|
||||
(start 0))
|
||||
(instance $a (instantiate 0 (func $set)))
|
||||
(instance $a (instantiate 0 "" (func $set)))
|
||||
)
|
||||
|
||||
(assert_return (invoke $a "get") (i32.const 1))
|
||||
@@ -49,7 +49,7 @@
|
||||
global.set 0)
|
||||
(start 0))
|
||||
|
||||
(instance $a (instantiate 0 (global $g)))
|
||||
(instance $a (instantiate 0 "" (global $g)))
|
||||
)
|
||||
(assert_return (invoke $a "get") (i32.const 2))
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
call_indirect)
|
||||
(start 0))
|
||||
|
||||
(instance $a (instantiate 0 (table $t)))
|
||||
(instance $a (instantiate 0 "" (table $t)))
|
||||
)
|
||||
(assert_return (invoke $a "get") (i32.const 3))
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
i32.store)
|
||||
(start 0))
|
||||
|
||||
(instance $a (instantiate 0 (memory $m)))
|
||||
(instance $a (instantiate 0 "" (memory $m)))
|
||||
)
|
||||
(assert_return (invoke $a "load") (i32.const 100))
|
||||
|
||||
@@ -88,13 +88,13 @@
|
||||
|
||||
(module $m1
|
||||
(import "" (instance (export "" (func))))
|
||||
(alias (instance 0) (func 0))
|
||||
(alias (func 0 ""))
|
||||
(start 0))
|
||||
|
||||
(module $m2
|
||||
(func (export "") (import "")))
|
||||
(instance $i (instantiate $m2 (func $set)))
|
||||
(instance (instantiate $m1 (instance $i)))
|
||||
(instance $i (instantiate $m2 "" (func $set)))
|
||||
(instance (instantiate $m1 "" (instance $i)))
|
||||
)
|
||||
(assert_return (invoke $a "get") (i32.const 4))
|
||||
|
||||
@@ -106,14 +106,14 @@
|
||||
(import "" (module $m (export "" (func $f (result i32)))))
|
||||
(instance $i (instantiate $m))
|
||||
(func $get (export "") (result i32)
|
||||
call $i.$f))
|
||||
call (func $i "")))
|
||||
|
||||
(module $m2
|
||||
(func (export "") (result i32)
|
||||
i32.const 5))
|
||||
(instance $i (instantiate $m1 (module $m2)))
|
||||
(instance $i (instantiate $m1 "" (module $m2)))
|
||||
(func (export "get") (result i32)
|
||||
call $i.$get)
|
||||
call (func $i ""))
|
||||
)
|
||||
(assert_return (invoke "get") (i32.const 5))
|
||||
|
||||
@@ -122,16 +122,16 @@
|
||||
(module $m
|
||||
(import "" (module $m (export "get" (func (result i32)))))
|
||||
(instance $i (instantiate $m))
|
||||
(alias $f (instance $i) (func 0))
|
||||
(alias $f (func $i "get"))
|
||||
(export "" (func $f))
|
||||
)
|
||||
(module $m2
|
||||
(func (export "get") (result i32)
|
||||
i32.const 6))
|
||||
(instance $a (instantiate $m (module $m2)))
|
||||
(instance $a (instantiate $m "" (module $m2)))
|
||||
|
||||
(func (export "get") (result i32)
|
||||
call $a.$f)
|
||||
call (func $a ""))
|
||||
)
|
||||
(assert_return (invoke "get") (i32.const 6))
|
||||
|
||||
@@ -143,10 +143,10 @@
|
||||
(import "a" "memory" (memory $m 1))
|
||||
|
||||
(module
|
||||
(import "" (memory 1))
|
||||
(import "" (global (mut i32)))
|
||||
(import "" (table 1 funcref))
|
||||
(import "" (func))
|
||||
(import "m" (memory 1))
|
||||
(import "g" (global (mut i32)))
|
||||
(import "t" (table 1 funcref))
|
||||
(import "f" (func))
|
||||
(func $start
|
||||
call 0
|
||||
|
||||
@@ -163,10 +163,10 @@
|
||||
|
||||
(instance $a
|
||||
(instantiate 0
|
||||
(memory $m)
|
||||
(global $g)
|
||||
(table $t)
|
||||
(func $f)
|
||||
"m" (memory $m)
|
||||
"g" (global $g)
|
||||
"t" (table $t)
|
||||
"f" (func $f)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -183,10 +183,10 @@
|
||||
(module $mt (import "" (table 1 funcref)))
|
||||
(module $mg (import "" (global (mut i32))))
|
||||
|
||||
(instance (instantiate $mm (memory $m)))
|
||||
(instance (instantiate $mf (func $f)))
|
||||
(instance (instantiate $mt (table $t)))
|
||||
(instance (instantiate $mg (global $g)))
|
||||
(instance (instantiate $mm "" (memory $m)))
|
||||
(instance (instantiate $mf "" (func $f)))
|
||||
(instance (instantiate $mt "" (table $t)))
|
||||
(instance (instantiate $mg "" (global $g)))
|
||||
)
|
||||
|
||||
;; instantiate nested
|
||||
@@ -204,13 +204,13 @@
|
||||
(import "" (func))
|
||||
(start 0)
|
||||
)
|
||||
(instance (instantiate 0 (func 0)))
|
||||
(instance (instantiate 0 "" (func 0)))
|
||||
)
|
||||
(instance (instantiate 0 (func 0)))
|
||||
(instance (instantiate 0 "" (func 0)))
|
||||
)
|
||||
(instance (instantiate 0 (func 0)))
|
||||
(instance (instantiate 0 "" (func 0)))
|
||||
)
|
||||
(instance (instantiate 0 (func 0)))
|
||||
(instance (instantiate 0 "" (func 0)))
|
||||
)
|
||||
(assert_return (invoke $a "get") (i32.const 1))
|
||||
|
||||
@@ -219,20 +219,14 @@
|
||||
(module (export "m"))
|
||||
(instance (export "i") (instantiate 0))
|
||||
)
|
||||
(module
|
||||
(import "b" "m" (module))
|
||||
(import "b" "i" (instance))
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(import "b" "m" (module (import "" (func))))
|
||||
)
|
||||
"module types incompatible")
|
||||
(module (import "b" "m" (module)))
|
||||
(module (import "b" "m" (module (import "" (func)))))
|
||||
(module (import "b" "i" (instance)))
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(import "b" "i" (instance (export "" (func))))
|
||||
)
|
||||
"instance types incompatible")
|
||||
"incompatible import type")
|
||||
|
||||
;; ensure we ignore other exported items
|
||||
(module $b
|
||||
@@ -250,7 +244,7 @@
|
||||
))
|
||||
|
||||
(func (export "get") (result i32)
|
||||
global.get $i.$g)
|
||||
global.get (global $i "g"))
|
||||
)
|
||||
(assert_return (invoke "get") (i32.const 0xfeed))
|
||||
|
||||
@@ -270,15 +264,45 @@
|
||||
(module
|
||||
(import "b" "i" (instance $i
|
||||
;; notice that this order is swapped
|
||||
(export "g" (func $g (param i32) (result i32)))
|
||||
(export "f" (func $f (result i32)))
|
||||
(export "g" (func (param i32) (result i32)))
|
||||
(export "f" (func (result i32)))
|
||||
))
|
||||
|
||||
(func (export "f") (result i32)
|
||||
call $i.$f)
|
||||
call (func $i "f"))
|
||||
(func (export "g") (param i32) (result i32)
|
||||
local.get 0
|
||||
call $i.$g)
|
||||
call (func $i "g"))
|
||||
)
|
||||
(assert_return (invoke "f") (i32.const 300))
|
||||
(assert_return (invoke "g" (i32.const 3000)) (i32.const 3100))
|
||||
|
||||
(module $a
|
||||
(func (export "f")))
|
||||
|
||||
(module
|
||||
(import "a" "f" (func))
|
||||
|
||||
(module $m1
|
||||
(import "a" "f" (func)))
|
||||
(instance (instantiate $m1 "a" (instance 0)))
|
||||
)
|
||||
|
||||
(module
|
||||
(import "a" "f" (func))
|
||||
|
||||
;; this module provides nothing
|
||||
(module $m1)
|
||||
|
||||
;; this module imports a module which it says imports something
|
||||
(module $m2
|
||||
(module $a
|
||||
(func (export "")))
|
||||
(instance $i (instantiate $a))
|
||||
(import "m" (module $b (import "" (func))))
|
||||
(instance $b (instantiate $b "" (func $i ""))))
|
||||
|
||||
;; we should be able to instantiate m2 with m1 because m1 doesn't actually
|
||||
;; import anything (always safe to remove imports!)
|
||||
(instance (instantiate $m2 "m" (module $m1)))
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user