Temporarily remove support for interface types (#1292)

* Temporarily remove support for interface types

This commit temporarily removes support for interface types from the
`wasmtime` CLI and removes the `wasmtime-interface-types` crate. An
error is now printed for any input wasm modules that have wasm interface
types sections to indicate that support has been removed and references
to two issues are printed as well:

* #677 - tracking work for re-adding interface types support
* #1271 - rationale for removal and links to other discussions

Closes #1271

* Update the python extension
This commit is contained in:
Alex Crichton
2020-03-12 15:05:39 -05:00
committed by GitHub
parent 6e55c543e2
commit 34f768ddd5
20 changed files with 113 additions and 734 deletions

18
Cargo.lock generated
View File

@@ -2490,7 +2490,6 @@ dependencies = [
"wasmtime", "wasmtime",
"wasmtime-debug", "wasmtime-debug",
"wasmtime-environ", "wasmtime-environ",
"wasmtime-interface-types",
"wasmtime-jit", "wasmtime-jit",
"wasmtime-obj", "wasmtime-obj",
"wasmtime-profiling", "wasmtime-profiling",
@@ -2577,21 +2576,6 @@ dependencies = [
"wat", "wat",
] ]
[[package]]
name = "wasmtime-interface-types"
version = "0.12.0"
dependencies = [
"anyhow",
"walrus",
"wasm-webidl-bindings",
"wasmparser 0.51.2",
"wasmtime",
"wasmtime-environ",
"wasmtime-jit",
"wasmtime-runtime",
"wasmtime-wasi",
]
[[package]] [[package]]
name = "wasmtime-jit" name = "wasmtime-jit"
version = "0.12.0" version = "0.12.0"
@@ -2649,7 +2633,6 @@ dependencies = [
"target-lexicon", "target-lexicon",
"wasmparser 0.51.2", "wasmparser 0.51.2",
"wasmtime", "wasmtime",
"wasmtime-interface-types",
"wasmtime-wasi", "wasmtime-wasi",
] ]
@@ -2677,7 +2660,6 @@ version = "0.12.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"wasmtime", "wasmtime",
"wasmtime-interface-types",
"wasmtime-rust-macro", "wasmtime-rust-macro",
"wasmtime-wasi", "wasmtime-wasi",
] ]

View File

@@ -26,7 +26,6 @@ doc = false
wasmtime = { path = "crates/api" } wasmtime = { path = "crates/api" }
wasmtime-debug = { path = "crates/debug" } wasmtime-debug = { path = "crates/debug" }
wasmtime-environ = { path = "crates/environ" } wasmtime-environ = { path = "crates/environ" }
wasmtime-interface-types = { path = "crates/interface-types" }
wasmtime-jit = { path = "crates/jit" } wasmtime-jit = { path = "crates/jit" }
wasmtime-obj = { path = "crates/obj" } wasmtime-obj = { path = "crates/obj" }
wasmtime-profiling = { path = "crates/profiling" } wasmtime-profiling = { path = "crates/profiling" }

View File

@@ -418,6 +418,28 @@ impl Module {
} }
} }
} }
SectionCode::Custom {
name: "webidl-bindings",
..
}
| SectionCode::Custom {
name: "wasm-interface-types",
..
} => {
bail!(
"\
support for interface types has temporarily been removed from `wasmtime`
for more information about this temoprary you can read on the issue online:
https://github.com/bytecodealliance/wasmtime/issues/1271
and for re-adding support for interface types you can see this issue:
https://github.com/bytecodealliance/wasmtime/issues/677
"
);
}
_ => { _ => {
// skip other sections // skip other sections
} }

View File

@@ -1,25 +0,0 @@
[package]
name = "wasmtime-interface-types"
version = "0.12.0"
authors = ["The Wasmtime Project Developers"]
description = "Support for wasm interface types with wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
repository = "https://github.com/bytecodealliance/wasmtime"
readme = "README.md"
edition = "2018"
[dependencies]
anyhow = "1.0.19"
walrus = "0.15"
wasmparser = "0.51.2"
wasm-webidl-bindings = "0.8"
wasmtime = { path = "../api", version = "0.12.0" }
wasmtime-jit = { path = "../jit", version = "0.12.0" }
wasmtime-environ = { path = "../environ", version = "0.12.0" }
wasmtime-runtime = { path = "../runtime", version = "0.12.0" }
wasmtime-wasi = { path = "../wasi", version = "0.12.0" }
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -1,4 +0,0 @@
This crate implements a prototype of the wasm [interface types] proposal
for Wasmtime.
[interface types]: https://github.com/WebAssembly/interface-types

View File

@@ -1,490 +0,0 @@
//! A small crate to handle WebAssembly interface types in wasmtime.
//!
//! Note that this is intended to follow the [official proposal][proposal] and
//! is highly susceptible to change/breakage/etc.
//!
//! [proposal]: https://github.com/webassembly/webidl-bindings
#![deny(missing_docs)]
use anyhow::{bail, format_err, Result};
use std::convert::TryFrom;
use std::str;
use wasm_webidl_bindings::ast;
use wasmtime::Val;
use wasmtime_environ::ir;
use wasmtime_runtime::{Export, InstanceHandle};
mod value;
pub use value::Value;
/// A data structure intended to hold a parsed representation of the wasm
/// interface types of a module.
///
/// The expected usage pattern is to create this next to wasmtime data
/// structures and then use this to process arguments into wasm arguments as
/// appropriate for bound functions.
pub struct ModuleData {
inner: Option<Inner>,
wasi_module_name: Option<String>,
}
struct Inner {
module: walrus::Module,
}
/// Representation of a binding of an exported function.
///
/// Can be used to learn about binding expressions and/or binding types.
pub struct ExportBinding<'a> {
kind: ExportBindingKind<'a>,
}
enum ExportBindingKind<'a> {
Rich {
section: &'a ast::WebidlBindings,
binding: &'a ast::ExportBinding,
},
Raw(ir::Signature),
}
impl ModuleData {
/// Parses a raw binary wasm file, extracting information about wasm
/// interface types.
///
/// Returns an error if the wasm file is malformed.
pub fn new(wasm: &[u8]) -> Result<ModuleData> {
// Perform a fast search through the module for the right custom
// section. Actually parsing out the interface types data is currently a
// pretty expensive operation so we want to only do that if we actually
// find the right section.
let mut reader = wasmparser::ModuleReader::new(wasm)?;
let mut found = false;
let mut wasi_module_name = None;
while !reader.eof() {
let section = reader.read()?;
match section.code {
wasmparser::SectionCode::Custom { name, .. } => {
if name == "webidl-bindings" {
found = true;
break;
}
}
// If we see the import section then see if we can find a wasi
// module import which we can later use to register the wasi
// implementation automatically.
wasmparser::SectionCode::Import => {
let section = section.get_import_section_reader()?;
for import in section {
let import = import?;
if wasmtime_wasi::is_wasi_module(import.module) {
wasi_module_name = Some(import.module.to_string());
}
}
}
_ => {}
}
}
if !found {
return Ok(ModuleData {
inner: None,
wasi_module_name,
});
}
// Ok, perform the more expensive parsing. WebAssembly interface types
// are super experimental and under development. To get something
// quickly up and running we're using the same crate as `wasm-bindgen`,
// a producer of wasm interface types, the `wasm-webidl-bindings` crate.
// This crate relies on `walrus` which has its own IR for a wasm module.
// Ideally we'd do all this during cranelift's own parsing of the wasm
// module and we wouldn't have to reparse here purely for this one use
// case.
//
// For now though this is "fast enough" and good enough for some demos,
// but for full-on production quality engines we'll want to integrate
// this much more tightly with the rest of wasmtime.
let module = walrus::ModuleConfig::new()
.on_parse(wasm_webidl_bindings::binary::on_parse)
.parse(wasm)?;
Ok(ModuleData {
inner: Some(Inner { module }),
wasi_module_name,
})
}
/// Detects if WASI support is needed: returns module name that is requested.
pub fn find_wasi_module_name(&self) -> Option<String> {
self.wasi_module_name.clone()
}
/// Invokes wasmtime function with a `&[Value]` list. `Value` the set of
/// wasm interface types.
pub fn invoke_export(
&self,
instance: &wasmtime::Instance,
export: &str,
args: &[Value],
) -> Result<Vec<Value>> {
let mut handle = instance.handle().clone();
let binding = self.binding_for_export(&mut handle, export)?;
let incoming = binding.param_bindings()?;
let outgoing = binding.result_bindings()?;
let f = instance
.get_export(export)
.ok_or_else(|| format_err!("failed to find export `{}`", export))?
.func()
.ok_or_else(|| format_err!("`{}` is not a function", export))?
.clone();
let mut cx = InstanceTranslateContext(instance.clone());
let wasm_args = translate_incoming(&mut cx, &incoming, args)?
.into_iter()
.map(|rv| rv.into())
.collect::<Vec<_>>();
let wasm_results = f.call(&wasm_args)?;
translate_outgoing(&mut cx, &outgoing, &wasm_results)
}
/// Returns an appropriate binding for the `name` export in this module
/// which has also been instantiated as `instance` provided here.
///
/// Returns an error if `name` is not present in the module.
pub fn binding_for_export(
&self,
instance: &mut InstanceHandle,
name: &str,
) -> Result<ExportBinding<'_>> {
if let Some(binding) = self.interface_binding_for_export(name) {
return Ok(binding);
}
let signature = match instance.lookup(name) {
Some(Export::Function { signature, .. }) => signature,
Some(_) => bail!("`{}` is not a function", name),
None => bail!("failed to find export `{}`", name),
};
Ok(ExportBinding {
kind: ExportBindingKind::Raw(signature),
})
}
fn interface_binding_for_export(&self, name: &str) -> Option<ExportBinding<'_>> {
let inner = self.inner.as_ref()?;
let bindings = inner.module.customs.get_typed::<ast::WebidlBindings>()?;
let export = inner.module.exports.iter().find(|e| e.name == name)?;
let id = match export.item {
walrus::ExportItem::Function(f) => f,
_ => panic!(),
};
let (_, bind) = bindings.binds.iter().find(|(_, b)| b.func == id)?;
let binding = bindings.bindings.get(bind.binding)?;
let binding = match binding {
ast::FunctionBinding::Export(export) => export,
ast::FunctionBinding::Import(_) => return None,
};
Some(ExportBinding {
kind: ExportBindingKind::Rich {
binding,
section: bindings,
},
})
}
}
impl ExportBinding<'_> {
/// Returns the list of binding expressions used to create the parameters
/// for this binding.
pub fn param_bindings(&self) -> Result<Vec<ast::IncomingBindingExpression>> {
match &self.kind {
ExportBindingKind::Rich { binding, .. } => Ok(binding.params.bindings.clone()),
ExportBindingKind::Raw(sig) => sig
.params
.iter()
.skip(2) // skip the VMContext arguments
.enumerate()
.map(|(i, param)| default_incoming(i, param))
.collect(),
}
}
/// Returns the list of scalar types used for this binding
pub fn param_types(&self) -> Result<Vec<ast::WebidlScalarType>> {
match &self.kind {
ExportBindingKind::Rich {
binding, section, ..
} => {
let id = match binding.webidl_ty {
ast::WebidlTypeRef::Id(id) => id,
ast::WebidlTypeRef::Scalar(_) => {
bail!("webidl types for functions cannot be scalar")
}
};
let ty = section
.types
.get::<ast::WebidlCompoundType>(id)
.ok_or_else(|| format_err!("invalid webidl custom section"))?;
let func = match ty {
ast::WebidlCompoundType::Function(f) => f,
_ => bail!("webidl type for function must be of function type"),
};
func.params
.iter()
.map(|param| match param {
ast::WebidlTypeRef::Id(_) => bail!("function arguments cannot be compound"),
ast::WebidlTypeRef::Scalar(s) => Ok(*s),
})
.collect()
}
ExportBindingKind::Raw(sig) => sig.params.iter().skip(2).map(abi2ast).collect(),
}
}
/// Returns the list of binding expressions used to extract the return
/// values of this binding.
pub fn result_bindings(&self) -> Result<Vec<ast::OutgoingBindingExpression>> {
match &self.kind {
ExportBindingKind::Rich { binding, .. } => Ok(binding.result.bindings.clone()),
ExportBindingKind::Raw(sig) => sig
.returns
.iter()
.enumerate()
.map(|(i, param)| default_outgoing(i, param))
.collect(),
}
}
}
fn default_incoming(idx: usize, param: &ir::AbiParam) -> Result<ast::IncomingBindingExpression> {
let get = ast::IncomingBindingExpressionGet { idx: idx as u32 };
let ty = if param.value_type == ir::types::I32 {
walrus::ValType::I32
} else if param.value_type == ir::types::I64 {
walrus::ValType::I64
} else if param.value_type == ir::types::F32 {
walrus::ValType::F32
} else if param.value_type == ir::types::F64 {
walrus::ValType::F64
} else {
bail!("unsupported type {:?}", param.value_type)
};
Ok(ast::IncomingBindingExpressionAs {
ty,
expr: Box::new(get.into()),
}
.into())
}
fn default_outgoing(idx: usize, param: &ir::AbiParam) -> Result<ast::OutgoingBindingExpression> {
let ty = abi2ast(param)?;
Ok(ast::OutgoingBindingExpressionAs {
ty: ty.into(),
idx: idx as u32,
}
.into())
}
fn abi2ast(param: &ir::AbiParam) -> Result<ast::WebidlScalarType> {
Ok(if param.value_type == ir::types::I32 {
ast::WebidlScalarType::Long
} else if param.value_type == ir::types::I64 {
ast::WebidlScalarType::LongLong
} else if param.value_type == ir::types::F32 {
ast::WebidlScalarType::UnrestrictedFloat
} else if param.value_type == ir::types::F64 {
ast::WebidlScalarType::UnrestrictedDouble
} else {
bail!("unsupported type {:?}", param.value_type)
})
}
trait TranslateContext {
fn invoke_alloc(&mut self, alloc_func_name: &str, len: i32) -> Result<i32>;
unsafe fn get_memory(&mut self) -> Result<&mut [u8]>;
}
struct InstanceTranslateContext(pub wasmtime::Instance);
impl TranslateContext for InstanceTranslateContext {
fn invoke_alloc(&mut self, alloc_func_name: &str, len: i32) -> Result<i32> {
let alloc = self
.0
.get_export(alloc_func_name)
.ok_or_else(|| format_err!("failed to find alloc function `{}`", alloc_func_name))?
.func()
.ok_or_else(|| format_err!("`{}` is not a (alloc) function", alloc_func_name))?
.clone();
let alloc_args = vec![wasmtime::Val::I32(len)];
let results = alloc.call(&alloc_args)?;
if results.len() != 1 {
bail!("allocator function wrong number of results");
}
Ok(match results[0] {
wasmtime::Val::I32(i) => i,
_ => bail!("allocator function bad return type"),
})
}
unsafe fn get_memory(&mut self) -> Result<&mut [u8]> {
let memory = self
.0
.get_export("memory")
.ok_or_else(|| format_err!("failed to find `memory` export"))?
.memory()
.ok_or_else(|| format_err!("`memory` is not a memory"))?
.clone();
let ptr = memory.data_ptr();
let len = memory.data_size();
Ok(std::slice::from_raw_parts_mut(ptr, len))
}
}
fn translate_incoming(
cx: &mut dyn TranslateContext,
bindings: &[ast::IncomingBindingExpression],
args: &[Value],
) -> Result<Vec<Val>> {
let get = |expr: &ast::IncomingBindingExpression| match expr {
ast::IncomingBindingExpression::Get(g) => args
.get(g.idx as usize)
.ok_or_else(|| format_err!("argument index out of bounds: {}", g.idx)),
_ => bail!("unsupported incoming binding expr {:?}", expr),
};
let mut copy = |alloc_func_name: &str, bytes: &[u8]| -> Result<(i32, i32)> {
let len = i32::try_from(bytes.len()).map_err(|_| format_err!("length overflow"))?;
let ptr = cx.invoke_alloc(alloc_func_name, len)?;
unsafe {
let raw = cx.get_memory()?;
raw[ptr as usize..][..bytes.len()].copy_from_slice(bytes)
}
Ok((ptr, len))
};
let mut wasm = Vec::new();
for expr in bindings {
match expr {
ast::IncomingBindingExpression::AllocUtf8Str(g) => {
let val = match get(&g.expr)? {
Value::String(s) => s,
_ => bail!("expected a string"),
};
let (ptr, len) = copy(&g.alloc_func_name, val.as_bytes())?;
wasm.push(Val::I32(ptr));
wasm.push(Val::I32(len));
}
ast::IncomingBindingExpression::As(g) => {
let val = get(&g.expr)?;
match g.ty {
walrus::ValType::I32 => match val {
Value::I32(i) => wasm.push(Val::I32(*i)),
Value::U32(i) => wasm.push(Val::I32(*i as i32)),
_ => bail!("cannot convert {:?} to `i32`", val),
},
walrus::ValType::I64 => match val {
Value::I32(i) => wasm.push(Val::I64((*i).into())),
Value::U32(i) => wasm.push(Val::I64((*i).into())),
Value::I64(i) => wasm.push(Val::I64(*i)),
Value::U64(i) => wasm.push(Val::I64(*i as i64)),
_ => bail!("cannot convert {:?} to `i64`", val),
},
walrus::ValType::F32 => match val {
Value::F32(i) => wasm.push(Val::F32(i.to_bits())),
_ => bail!("cannot convert {:?} to `f32`", val),
},
walrus::ValType::F64 => match val {
Value::F32(i) => wasm.push(Val::F64((*i as f64).to_bits())),
Value::F64(i) => wasm.push(Val::F64(i.to_bits())),
_ => bail!("cannot convert {:?} to `f64`", val),
},
walrus::ValType::V128 | walrus::ValType::Anyref => {
bail!("unsupported `as` type {:?}", g.ty);
}
}
}
_ => bail!("unsupported incoming binding expr {:?}", expr),
}
}
Ok(wasm)
}
fn translate_outgoing(
cx: &mut dyn TranslateContext,
bindings: &[ast::OutgoingBindingExpression],
args: &[Val],
) -> Result<Vec<Value>> {
let mut values = Vec::new();
let get = |idx: u32| {
args.get(idx as usize)
.cloned()
.ok_or_else(|| format_err!("argument index out of bounds: {}", idx))
};
for expr in bindings {
match expr {
ast::OutgoingBindingExpression::As(a) => {
let arg = get(a.idx)?;
match a.ty {
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::UnsignedLong) => match arg {
Val::I32(a) => values.push(Value::U32(a as u32)),
_ => bail!("can't convert {:?} to unsigned long", arg),
},
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Long) => match arg {
Val::I32(a) => values.push(Value::I32(a)),
_ => bail!("can't convert {:?} to long", arg),
},
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::LongLong) => match arg {
Val::I32(a) => values.push(Value::I64(a as i64)),
Val::I64(a) => values.push(Value::I64(a)),
_ => bail!("can't convert {:?} to long long", arg),
},
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::UnsignedLongLong) => {
match arg {
Val::I32(a) => values.push(Value::U64(a as u64)),
Val::I64(a) => values.push(Value::U64(a as u64)),
_ => bail!("can't convert {:?} to unsigned long long", arg),
}
}
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Float) => match arg {
Val::F32(a) => values.push(Value::F32(f32::from_bits(a))),
_ => bail!("can't convert {:?} to float", arg),
},
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Double) => match arg {
Val::F32(a) => values.push(Value::F64(f32::from_bits(a) as f64)),
Val::F64(a) => values.push(Value::F64(f64::from_bits(a))),
_ => bail!("can't convert {:?} to double", arg),
},
_ => bail!("unsupported outgoing binding expr {:?}", expr),
}
}
ast::OutgoingBindingExpression::Utf8Str(e) => {
if e.ty != ast::WebidlScalarType::DomString.into() {
bail!("utf-8 strings must go into dom-string")
}
let offset = match get(e.offset)? {
Val::I32(a) => a,
_ => bail!("offset must be an i32"),
};
let length = match get(e.length)? {
Val::I32(a) => a,
_ => bail!("length must be an i32"),
};
let bytes = unsafe { &cx.get_memory()?[offset as usize..][..length as usize] };
values.push(Value::String(str::from_utf8(bytes).unwrap().to_string()));
}
_ => {
drop(cx);
bail!("unsupported outgoing binding expr {:?}", expr);
}
}
}
Ok(values)
}

View File

@@ -1,66 +0,0 @@
use std::convert::TryFrom;
use std::fmt;
/// The set of all possible WebAssembly Interface Types
#[derive(Debug, Clone)]
#[allow(missing_docs)]
pub enum Value {
String(String),
I32(i32),
U32(u32),
I64(i64),
U64(u64),
F32(f32),
F64(f64),
}
macro_rules! from {
($($a:ident => $b:ident,)*) => ($(
impl From<$a> for Value {
fn from(val: $a) -> Value {
Value::$b(val)
}
}
impl TryFrom<Value> for $a {
type Error = anyhow::Error;
fn try_from(val: Value) -> Result<$a, Self::Error> {
match val {
Value::$b(v) => Ok(v),
v => anyhow::bail!("cannot convert {:?} to {}", v, stringify!($a)),
}
}
}
)*)
}
from! {
String => String,
i32 => I32,
u32 => U32,
i64 => I64,
u64 => U64,
f32 => F32,
f64 => F64,
}
impl<'a> From<&'a str> for Value {
fn from(x: &'a str) -> Value {
x.to_string().into()
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Value::String(s) => s.fmt(f),
Value::I32(s) => s.fmt(f),
Value::U32(s) => s.fmt(f),
Value::I64(s) => s.fmt(f),
Value::U64(s) => s.fmt(f),
Value::F32(s) => s.fmt(f),
Value::F64(s) => s.fmt(f),
}
}
}

View File

@@ -18,7 +18,6 @@ doc = false
[dependencies] [dependencies]
wasmtime = { path = "../../api", version = "0.12.0" } wasmtime = { path = "../../api", version = "0.12.0" }
wasmtime-interface-types = { path = "../../interface-types", version = "0.12.0" }
wasmtime-wasi = { path = "../../wasi", version = "0.12.0" } wasmtime-wasi = { path = "../../wasi", version = "0.12.0" }
target-lexicon = { version = "0.10.0", default-features = false } target-lexicon = { version = "0.10.0", default-features = false }
anyhow = "1.0.19" anyhow = "1.0.19"

View File

@@ -5,25 +5,16 @@ use pyo3::exceptions::Exception;
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::{PyAny, PyDict, PyTuple}; use pyo3::types::{PyAny, PyDict, PyTuple};
use std::rc::Rc; use std::rc::Rc;
use wasmtime_interface_types::ModuleData;
// TODO support non-export functions // TODO support non-export functions
#[pyclass] #[pyclass]
pub struct Function { pub struct Function {
pub instance: wasmtime::Instance, pub func: wasmtime::Func,
pub export_name: String,
pub args_types: Vec<wasmtime::ValType>,
pub data: Rc<ModuleData>,
} }
impl Function { impl Function {
pub fn func(&self) -> wasmtime::Func { pub fn func(&self) -> wasmtime::Func {
let e = self self.func.clone()
.instance
.get_export(&self.export_name)
.expect("named export")
.clone();
e.func().expect("function export").clone()
} }
} }
@@ -37,11 +28,11 @@ impl Function {
runtime_args.push(pyobj_to_value(py, item)?); runtime_args.push(pyobj_to_value(py, item)?);
} }
let results = self let results = self
.data .func
.invoke_export(&self.instance, self.export_name.as_str(), &runtime_args) .call(&runtime_args)
.map_err(crate::err2py)?; .map_err(|e| crate::err2py(e.into()))?;
let mut py_results = Vec::new(); let mut py_results = Vec::new();
for result in results { for result in results.into_vec() {
py_results.push(value_to_pyobj(py, result)?); py_results.push(value_to_pyobj(py, result)?);
} }
if py_results.len() == 1 { if py_results.len() == 1 {

View File

@@ -4,13 +4,10 @@ use crate::function::Function;
use crate::memory::Memory; use crate::memory::Memory;
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::PyDict; use pyo3::types::PyDict;
use std::rc::Rc;
use wasmtime_interface_types::ModuleData;
#[pyclass] #[pyclass]
pub struct Instance { pub struct Instance {
pub instance: wasmtime::Instance, pub instance: wasmtime::Instance,
pub data: Rc<ModuleData>,
} }
#[pymethods] #[pymethods]
@@ -31,10 +28,7 @@ impl Instance {
let f = Py::new( let f = Py::new(
py, py,
Function { Function {
instance: self.instance.clone(), func: self.instance.exports()[i].func().unwrap().clone(),
data: self.data.clone(),
export_name: e.name().to_string(),
args_types,
}, },
)?; )?;
exports.set_item(e.name().to_string(), f)?; exports.set_item(e.name().to_string(), f)?;

View File

@@ -6,8 +6,6 @@ use pyo3::exceptions::Exception;
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::{PyAny, PyBytes, PyDict, PySet}; use pyo3::types::{PyAny, PyBytes, PyDict, PySet};
use pyo3::wrap_pyfunction; use pyo3::wrap_pyfunction;
use std::rc::Rc;
use wasmtime_interface_types::ModuleData;
mod function; mod function;
mod instance; mod instance;
@@ -84,36 +82,35 @@ pub fn instantiate(
let module = wasmtime::Module::new(&store, wasm_data).map_err(err2py)?; let module = wasmtime::Module::new(&store, wasm_data).map_err(err2py)?;
let data = Rc::new(ModuleData::new(wasm_data).map_err(err2py)?);
// If this module expects to be able to use wasi then go ahead and hook // If this module expects to be able to use wasi then go ahead and hook
// that up into the imported crates. // that up into the imported crates.
let wasi = if let Some(module_name) = data.find_wasi_module_name() {
let cx = wasmtime_wasi::WasiCtxBuilder::new() let cx = wasmtime_wasi::WasiCtxBuilder::new()
.build() .build()
.map_err(|e| err2py(e.into()))?; .map_err(|e| err2py(e.into()))?;
let wasi = wasmtime_wasi::Wasi::new(&store, cx); let wasi_snapshot_preview1 = wasmtime_wasi::Wasi::new(&store, cx);
Some((module_name, wasi)) let cx = wasmtime_wasi::old::snapshot_0::WasiCtxBuilder::new()
} else { .build()
None .map_err(|e| err2py(e.into()))?;
}; let wasi_snapshot = wasmtime_wasi::old::snapshot_0::Wasi::new(&store, cx);
let mut imports: Vec<wasmtime::Extern> = Vec::new(); let mut imports: Vec<wasmtime::Extern> = Vec::new();
for i in module.imports() { for i in module.imports() {
if i.module() == "wasi_snapshot" {
if let Some(func) = wasi_snapshot.get_export(i.name()) {
imports.push(func.clone().into());
continue;
}
}
if i.module() == "wasi_snapshot_preview1" {
if let Some(func) = wasi_snapshot_preview1.get_export(i.name()) {
imports.push(func.clone().into());
continue;
}
}
let module_name = i.module(); let module_name = i.module();
if let Some(m) = import_obj.get_item(module_name) { if let Some(m) = import_obj.get_item(module_name) {
let e = find_export_in(m, &store, i.name())?; let e = find_export_in(m, &store, i.name())?;
imports.push(e); imports.push(e);
} else if wasi.is_some() && module_name == wasi.as_ref().unwrap().0 {
let e = wasi
.as_ref()
.unwrap()
.1
.get_export(i.name())
.ok_or_else(|| {
PyErr::new::<Exception, _>(format!("wasi export {} is not found", i.name(),))
})?;
imports.push(e.clone().into());
} else { } else {
return Err(PyErr::new::<Exception, _>(format!( return Err(PyErr::new::<Exception, _>(format!(
"imported module {} is not found", "imported module {} is not found",
@@ -127,7 +124,7 @@ pub fn instantiate(
let module = Py::new(py, Module { module })?; let module = Py::new(py, Module { module })?;
let instance = Py::new(py, Instance { instance, data })?; let instance = Py::new(py, Instance { instance })?;
Py::new(py, InstantiateResultObject { instance, module }) Py::new(py, InstantiateResultObject { instance, module })
} }

View File

@@ -3,36 +3,30 @@
use pyo3::exceptions::Exception; use pyo3::exceptions::Exception;
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::PyAny; use pyo3::types::PyAny;
use wasmtime_interface_types::Value; use wasmtime::Val;
pub fn pyobj_to_value(_: Python, p: &PyAny) -> PyResult<Value> { pub fn pyobj_to_value(_: Python, p: &PyAny) -> PyResult<Val> {
if let Ok(n) = p.extract() { if let Ok(n) = p.extract() {
Ok(Value::I32(n)) Ok(Val::I32(n))
} else if let Ok(n) = p.extract() { } else if let Ok(n) = p.extract() {
Ok(Value::U32(n)) Ok(Val::I64(n))
} else if let Ok(n) = p.extract() { } else if let Ok(n) = p.extract() {
Ok(Value::I64(n)) Ok(Val::F64(n))
} else if let Ok(n) = p.extract() { } else if let Ok(n) = p.extract() {
Ok(Value::U64(n)) Ok(Val::F32(n))
} else if let Ok(n) = p.extract() {
Ok(Value::F64(n))
} else if let Ok(n) = p.extract() {
Ok(Value::F32(n))
} else if let Ok(s) = p.extract() {
Ok(Value::String(s))
} else { } else {
Err(PyErr::new::<Exception, _>("unsupported value type")) Err(PyErr::new::<Exception, _>("unsupported value type"))
} }
} }
pub fn value_to_pyobj(py: Python, value: Value) -> PyResult<PyObject> { pub fn value_to_pyobj(py: Python, value: Val) -> PyResult<PyObject> {
Ok(match value { Ok(match value {
Value::I32(i) => i.into_py(py), Val::I32(i) => i.into_py(py),
Value::U32(i) => i.into_py(py), Val::I64(i) => i.into_py(py),
Value::I64(i) => i.into_py(py), Val::F32(i) => i.into_py(py),
Value::U64(i) => i.into_py(py), Val::F64(i) => i.into_py(py),
Value::F32(i) => i.into_py(py), Val::AnyRef(_) | Val::FuncRef(_) | Val::V128(_) => {
Value::F64(i) => i.into_py(py), return Err(PyErr::new::<Exception, _>("unsupported value type"))
Value::String(i) => i.into_py(py), }
}) })
} }

View File

@@ -15,7 +15,6 @@ test = false
doctest = false doctest = false
[dependencies] [dependencies]
wasmtime-interface-types = { path = "../../interface-types", version = "0.12.0" }
wasmtime-rust-macro = { path = "./macro", version = "0.12.0" } wasmtime-rust-macro = { path = "./macro", version = "0.12.0" }
wasmtime-wasi = { path = "../../wasi", version = "0.12.0" } wasmtime-wasi = { path = "../../wasi", version = "0.12.0" }
wasmtime = { path = "../../api", version = "0.12.0" } wasmtime = { path = "../../api", version = "0.12.0" }

View File

@@ -1,13 +0,0 @@
use wasmtime_rust::wasmtime;
#[wasmtime]
trait WasmMarkdown {
fn render(&mut self, input: &str) -> String;
}
fn main() -> anyhow::Result<()> {
let mut markdown = WasmMarkdown::load_file("markdown.wasm")?;
println!("{}", markdown.render("# Hello, Rust!"));
Ok(())
}

View File

@@ -5,23 +5,21 @@ pub use wasmtime_rust_macro::wasmtime;
pub mod __rt { pub mod __rt {
pub use anyhow; pub use anyhow;
pub use wasmtime; pub use wasmtime;
pub use wasmtime_interface_types;
pub use wasmtime_wasi; pub use wasmtime_wasi;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use wasmtime_interface_types::Value;
pub trait FromVecValue: Sized { pub trait FromVecValue: Sized {
fn from(list: Vec<Value>) -> anyhow::Result<Self>; fn from(list: Vec<wasmtime::Val>) -> anyhow::Result<Self>;
} }
macro_rules! tuple { macro_rules! tuple {
($(($($a:ident),*),)*) => ($( ($(($($a:ident),*),)*) => ($(
impl<$($a: TryFrom<Value>),*> FromVecValue for ($($a,)*) impl<$($a: TryFrom<wasmtime::Val>),*> FromVecValue for ($($a,)*)
where $(anyhow::Error: From<$a::Error>,)* where $(anyhow::Error: From<$a::Error>,)*
{ {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn from(list: Vec<Value>) -> anyhow::Result<Self> { fn from(list: Vec<wasmtime::Val>) -> anyhow::Result<Self> {
let mut iter = list.into_iter(); let mut iter = list.into_iter();
$( $(
let $a = iter.next() let $a = iter.next()

View File

@@ -1,5 +1,5 @@
use wasi_common::old::snapshot_0::hostcalls; use wasi_common::old::snapshot_0::hostcalls;
use wasi_common::old::snapshot_0::WasiCtx; pub use wasi_common::old::snapshot_0::{WasiCtx, WasiCtxBuilder};
// Defines a `struct Wasi` with member fields and appropriate APIs for dealing // Defines a `struct Wasi` with member fields and appropriate APIs for dealing
// with all the various WASI exports. // with all the various WASI exports.

View File

@@ -133,6 +133,11 @@ interface types.
## WebAssembly Interface Types ## WebAssembly Interface Types
> **Note**: support for interface types has temporarily removed from Wasmtime.
> This documentation is somewhat up to date but will no longer work with recent
> versions of Wasmtime. For more information see
> https://github.com/bytecodealliance/wasmtime/issues/677
Working with WebAssembly modules at the bare-bones level means that you're only Working with WebAssembly modules at the bare-bones level means that you're only
dealing with integers and floats. Many APIs, however, want to work with things dealing with integers and floats. Many APIs, however, want to work with things
like byte arrays, strings, structures, etc. To facilitate these interactions the like byte arrays, strings, structures, etc. To facilitate these interactions the

View File

@@ -27,7 +27,6 @@ for cargo_toml in \
crates/api/Cargo.toml \ crates/api/Cargo.toml \
crates/wasi/Cargo.toml \ crates/wasi/Cargo.toml \
crates/wast/Cargo.toml \ crates/wast/Cargo.toml \
crates/interface-types/Cargo.toml \
crates/misc/py/Cargo.toml \ crates/misc/py/Cargo.toml \
crates/misc/rust/macro/Cargo.toml \ crates/misc/rust/macro/Cargo.toml \
crates/misc/rust/Cargo.toml \ crates/misc/rust/Cargo.toml \

View File

@@ -59,7 +59,6 @@ RUST_BACKTRACE=1 cargo test \
--package wasmtime-environ \ --package wasmtime-environ \
--package wasmtime-runtime \ --package wasmtime-runtime \
--package wasmtime-jit \ --package wasmtime-jit \
--package wasmtime-interface-types \
--package wasmtime-obj \ --package wasmtime-obj \
--package wiggle \ --package wiggle \
--package wiggle-generate \ --package wiggle-generate \

View File

@@ -10,8 +10,7 @@ use std::{
}; };
use structopt::{clap::AppSettings, StructOpt}; use structopt::{clap::AppSettings, StructOpt};
use wasi_common::preopen_dir; use wasi_common::preopen_dir;
use wasmtime::{Engine, Instance, Module, Store, Trap}; use wasmtime::{Engine, Instance, Module, Store, Trap, Val, ValType};
use wasmtime_interface_types::ModuleData;
use wasmtime_wasi::{old::snapshot_0::Wasi as WasiSnapshot0, Wasi}; use wasmtime_wasi::{old::snapshot_0::Wasi as WasiSnapshot0, Wasi};
fn parse_module(s: &OsStr) -> Result<PathBuf, OsString> { fn parse_module(s: &OsStr) -> Result<PathBuf, OsString> {
@@ -191,7 +190,7 @@ impl RunCommand {
store: &Store, store: &Store,
module_registry: &ModuleRegistry, module_registry: &ModuleRegistry,
path: &Path, path: &Path,
) -> Result<(Instance, Module, Vec<u8>)> { ) -> Result<(Instance, Module)> {
// Read the wasm module binary either as `*.wat` or a raw binary // Read the wasm module binary either as `*.wat` or a raw binary
let data = wat::parse_file(path)?; let data = wat::parse_file(path)?;
@@ -223,47 +222,46 @@ impl RunCommand {
let instance = Instance::new(&module, &imports) let instance = Instance::new(&module, &imports)
.context(format!("failed to instantiate {:?}", path))?; .context(format!("failed to instantiate {:?}", path))?;
Ok((instance, module, data)) Ok((instance, module))
} }
fn handle_module(&self, store: &Store, module_registry: &ModuleRegistry) -> Result<()> { fn handle_module(&self, store: &Store, module_registry: &ModuleRegistry) -> Result<()> {
let (instance, module, data) = let (instance, module) = Self::instantiate_module(store, module_registry, &self.module)?;
Self::instantiate_module(store, module_registry, &self.module)?;
// If a function to invoke was given, invoke it. // If a function to invoke was given, invoke it.
if let Some(name) = self.invoke.as_ref() { if let Some(name) = self.invoke.as_ref() {
let data = ModuleData::new(&data)?; self.invoke_export(instance, name)?;
self.invoke_export(instance, &data, name)?;
} else if module } else if module
.exports() .exports()
.iter() .iter()
.any(|export| export.name().is_empty()) .any(|export| export.name().is_empty())
{ {
// Launch the default command export. // Launch the default command export.
let data = ModuleData::new(&data)?; self.invoke_export(instance, "")?;
self.invoke_export(instance, &data, "")?;
} else { } else {
// If the module doesn't have a default command export, launch the // If the module doesn't have a default command export, launch the
// _start function if one is present, as a compatibility measure. // _start function if one is present, as a compatibility measure.
let data = ModuleData::new(&data)?; self.invoke_export(instance, "_start")?;
self.invoke_export(instance, &data, "_start")?;
} }
Ok(()) Ok(())
} }
fn invoke_export(&self, instance: Instance, data: &ModuleData, name: &str) -> Result<()> { fn invoke_export(&self, instance: Instance, name: &str) -> Result<()> {
use wasm_webidl_bindings::ast; let pos = instance
use wasmtime_interface_types::Value; .module()
.exports()
let mut handle = instance.handle().clone(); .iter()
.enumerate()
// Use the binding information in `ModuleData` to figure out what arguments .find(|(_, e)| e.name() == name);
// need to be passed to the function that we're invoking. Currently we take let (ty, export) = match pos {
// the CLI parameters and attempt to parse them into function arguments for Some((i, ty)) => match (ty.ty(), &instance.exports()[i]) {
// the function we'll invoke. (wasmtime::ExternType::Func(ty), wasmtime::Extern::Func(f)) => (ty, f),
let binding = data.binding_for_export(&mut handle, name)?; _ => bail!("export of `{}` wasn't a function", name),
if !binding.param_types()?.is_empty() { },
None => bail!("failed to find export of `{}` in module", name),
};
if ty.params().len() > 0 {
eprintln!( eprintln!(
"warning: using `--invoke` with a function that takes arguments \ "warning: using `--invoke` with a function that takes arguments \
is experimental and may break in the future" is experimental and may break in the future"
@@ -271,7 +269,7 @@ impl RunCommand {
} }
let mut args = self.module_args.iter(); let mut args = self.module_args.iter();
let mut values = Vec::new(); let mut values = Vec::new();
for ty in binding.param_types()? { for ty in ty.params() {
let val = match args.next() { let val = match args.next() {
Some(s) => s, Some(s) => s,
None => bail!("not enough arguments for `{}`", name), None => bail!("not enough arguments for `{}`", name),
@@ -280,26 +278,18 @@ impl RunCommand {
// TODO: integer parsing here should handle hexadecimal notation // TODO: integer parsing here should handle hexadecimal notation
// like `0x0...`, but the Rust standard library currently only // like `0x0...`, but the Rust standard library currently only
// parses base-10 representations. // parses base-10 representations.
ast::WebidlScalarType::Long => Value::I32(val.parse()?), ValType::I32 => Val::I32(val.parse()?),
ast::WebidlScalarType::LongLong => Value::I64(val.parse()?), ValType::I64 => Val::I64(val.parse()?),
ast::WebidlScalarType::UnsignedLong => Value::U32(val.parse()?), ValType::F32 => Val::F32(val.parse()?),
ast::WebidlScalarType::UnsignedLongLong => Value::U64(val.parse()?), ValType::F64 => Val::F64(val.parse()?),
ast::WebidlScalarType::Float | ast::WebidlScalarType::UnrestrictedFloat => {
Value::F32(val.parse()?)
}
ast::WebidlScalarType::Double | ast::WebidlScalarType::UnrestrictedDouble => {
Value::F64(val.parse()?)
}
ast::WebidlScalarType::DomString => Value::String(val.to_string()),
t => bail!("unsupported argument type {:?}", t), t => bail!("unsupported argument type {:?}", t),
}); });
} }
// Invoke the function and then afterwards print all the results that came // Invoke the function and then afterwards print all the results that came
// out, if there are any. // out, if there are any.
let results = data let results = export
.invoke_export(&instance, name, &values) .call(&values)
.with_context(|| format!("failed to invoke `{}`", name))?; .with_context(|| format!("failed to invoke `{}`", name))?;
if !results.is_empty() { if !results.is_empty() {
eprintln!( eprintln!(
@@ -307,8 +297,17 @@ impl RunCommand {
is experimental and may break in the future" is experimental and may break in the future"
); );
} }
for result in results {
println!("{}", result); for result in results.into_vec() {
match result {
Val::I32(i) => println!("{}", i),
Val::I64(i) => println!("{}", i),
Val::F32(f) => println!("{}", f),
Val::F64(f) => println!("{}", f),
Val::AnyRef(_) => println!("<anyref>"),
Val::FuncRef(_) => println!("<anyref>"),
Val::V128(i) => println!("{}", i),
}
} }
Ok(()) Ok(())