Merge pull request #2585 from alexcrichton/module-linking-update
Update support for the module linking proposal
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)
|
||||
}
|
||||
|
||||
@@ -373,8 +371,8 @@ impl Func {
|
||||
///
|
||||
/// Finally you can also optionally take [`Caller`] as the first argument of
|
||||
/// your closure. If inserted then you're able to inspect the caller's
|
||||
/// state, for example the [`Memory`] it has exported so you can read what
|
||||
/// pointers point to.
|
||||
/// state, for example the [`Memory`](crate::Memory) it has exported so you
|
||||
/// can read what pointers point to.
|
||||
///
|
||||
/// Note that when using this API, the intention is to create as thin of a
|
||||
/// layer as possible for when WebAssembly calls the function provided. With
|
||||
@@ -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 {
|
||||
@@ -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