Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 36 additions & 5 deletions crates/api/src/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ impl Linker {
pub fn instantiate(&self, module: &Module) -> Result<Instance> {
let mut imports = Vec::new();
for import in module.imports() {
if let Some(item) = self.import_get(import) {
if let Some(item) = self.get(import) {
imports.push(item.clone());
continue;
}
Expand Down Expand Up @@ -395,7 +395,26 @@ impl Linker {
Instance::new(module, &imports)
}

fn import_get(&self, import: &ImportType) -> Option<&Extern> {
/// Returns the [`Store`] that this linker is connected to.
pub fn store(&self) -> &Store {
&self.store
}

/// Returns an iterator over all items defined in this `Linker`.
Comment thread
alexcrichton marked this conversation as resolved.
///
/// Note that multiple `Extern` items may be defined for the same
/// module/name pair.
pub fn iter(&self) -> impl Iterator<Item = (&str, &str, &Extern)> {
self.map
.iter()
.map(move |(key, item)| (&*self.strings[key.module], &*self.strings[key.name], item))
}

/// Looks up a value in this `Linker` which matches the `import` type
/// provided.
///
/// Returns `None` if no match was found.
pub fn get(&self, import: &ImportType) -> Option<&Extern> {
let key = ImportKey {
module: *self.string2idx.get(import.module())?,
name: *self.string2idx.get(import.name())?,
Expand All @@ -404,8 +423,20 @@ impl Linker {
self.map.get(&key)
}

/// Returns the [`Store`] that this linker is connected to.
pub fn store(&self) -> &Store {
&self.store
/// Returns all items defined for the `module` and `name` pair.
///
/// This may return an empty iterator, but it may also return multiple items
/// if the module/name have been defined twice.
pub fn get_by_name<'a: 'p, 'p>(
&'a self,
module: &'p str,
name: &'p str,
) -> impl Iterator<Item = &'a Extern> + 'p {
self.map
.iter()
.filter(move |(key, _item)| {
&*self.strings[key.module] == module && &*self.strings[key.name] == name
})
.map(|(_, item)| item)
}
}
74 changes: 48 additions & 26 deletions crates/wast/src/wast.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::spectest::link_spectest;
use anyhow::{anyhow, bail, Context as _, Result};
use std::collections::HashMap;
use std::path::Path;
use std::str;
use wasmtime::*;
Expand Down Expand Up @@ -28,8 +27,9 @@ pub struct WastContext {
/// Wast files have a concept of a "current" module, which is the most
/// recently defined.
current: Option<Instance>,

instances: HashMap<String, Instance>,
Comment thread
sunfishcode marked this conversation as resolved.
// FIXME(#1479) this is only needed to retain correct trap information after
// we've dropped previous `Instance` values.
modules: Vec<Module>,
linker: Linker,
store: Store,
}
Expand Down Expand Up @@ -58,28 +58,36 @@ impl WastContext {
linker.allow_shadowing(true);
Self {
current: None,
instances: HashMap::new(),
linker,
store,
modules: Vec::new(),
}
}

fn get_instance(&self, instance_name: Option<&str>) -> Result<Instance> {
match instance_name {
Some(name) => self
.instances
.get(name)
.cloned()
.ok_or_else(|| anyhow!("failed to find instance named `{}`", name)),
fn get_export(&self, module: Option<&str>, name: &str) -> Result<&Extern> {
match module {
Some(module) => {
let mut items = self.linker.get_by_name(module, name);
let ret = items
.next()
.ok_or_else(|| anyhow!("no item named `{}` in `{}`", name, module))?;
if items.next().is_some() {
bail!("too many items named `{}` in `{}`", name, module);
}
return Ok(ret);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we factor this out into a method on Linker, something like self.linker.get_one_by_name(module, name) or so? I realize it'll require a special error type to be able to report both "no item named {}" and "too many items named {}", but this seems like it'll be a common use case -- I'm even picturing something like the wasmtime CLI but backed by a Linker so that you can provide multiple modules and then do --invoke.

}
None => self
.current
.clone()
.ok_or_else(|| anyhow!("no previous instance found")),
.as_ref()
.ok_or_else(|| anyhow!("no previous instance found"))?
.get_export(name)
.ok_or_else(|| anyhow!("no item named `{}` found", name)),
}
}

fn instantiate(&self, module: &[u8]) -> Result<Outcome<Instance>> {
fn instantiate(&mut self, module: &[u8]) -> Result<Outcome<Instance>> {
let module = Module::new(&self.store, module)?;
self.modules.push(module.clone());
let instance = match self.linker.instantiate(&module) {
Ok(i) => i,
Err(e) => return e.downcast::<Trap>().map(Outcome::Trap),
Expand Down Expand Up @@ -125,7 +133,6 @@ impl WastContext {
Outcome::Trap(e) => bail!("instantiation failed with: {}", e.message()),
};
if let Some(name) = instance_name {
self.instances.insert(name.to_string(), instance.clone());
self.linker.instance(name, &instance)?;
}
self.current = Some(instance);
Expand All @@ -134,9 +141,26 @@ impl WastContext {

/// Register an instance to make it available for performing actions.
fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> {
let instance = self.get_instance(name)?.clone();
self.linker.instance(as_name, &instance)?;
self.instances.insert(as_name.to_string(), instance);
match name {
Some(name) => {
let items = self
.linker
.iter()
.filter(|(module, _, _)| *module == name)
.map(|(_, name, item)| (name.to_string(), item.clone()))
.collect::<Vec<_>>();
for (name, item) in items {
self.linker.define(as_name, &name, item)?;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we factor out this logic too, with something like self.linker.alias(name, as_name) or so?

}
None => {
let current = self
.current
.as_ref()
.ok_or(anyhow!("no previous instance"))?;
self.linker.instance(as_name, current)?;
}
}
Ok(())
}

Expand All @@ -147,10 +171,9 @@ impl WastContext {
field: &str,
args: &[Val],
) -> Result<Outcome> {
let instance = self.get_instance(instance_name.as_ref().map(|x| &**x))?;
let func = instance
.get_export(field)
.and_then(|e| e.func())
let func = self
.get_export(instance_name, field)?
.func()
.ok_or_else(|| anyhow!("no function named `{}`", field))?;
Ok(match func.call(args) {
Ok(result) => Outcome::Ok(result.into()),
Expand All @@ -160,10 +183,9 @@ impl WastContext {

/// Get the value of an exported global from an instance.
fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<Outcome> {
let instance = self.get_instance(instance_name.as_ref().map(|x| &**x))?;
let global = instance
.get_export(field)
.and_then(|e| e.global())
let global = self
.get_export(instance_name, field)?
.global()
.ok_or_else(|| anyhow!("no global named `{}`", field))?;
Ok(Outcome::Ok(vec![global.get()]))
}
Expand Down