Tweak the API of the Val type (#679)

* Tweak the API of the `Val` type

A few updates to the API of the `Val` type:

* Added a payload for `V128`.
* Replace existing accessor methods with `Option`-returning versions.
* Add `unwrap_xxx` family of methods to extract a value and panic.
* Remove `Into` conversions which panic, since panicking in `From` or
  `Into` isn't idiomatic in Rust
* Add documentation to all methods/values/enums/etc.
* Rename `Val::default` to `Val::null`

* Run rustfmt

* Review comments
This commit is contained in:
Alex Crichton
2019-12-06 16:19:37 -06:00
committed by GitHub
parent 2597468b30
commit 3d69e04659
7 changed files with 103 additions and 107 deletions

View File

@@ -51,7 +51,7 @@ macro_rules! call {
($func:expr, $($p:expr),*) => {
match $func.borrow().call(&[$($p.into()),*]) {
Ok(result) => {
let result: i32 = result[0].clone().into();
let result: i32 = result[0].unwrap_i32();
result
}
Err(_) => { bail!("> Error on result, expected return"); }

View File

@@ -9,10 +9,10 @@ struct Callback;
impl Callable for Callback {
fn call(&self, args: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
println!("Calling back...");
println!("> {} {}", args[0].i32(), args[1].i64());
println!("> {} {}", args[0].unwrap_i32(), args[1].unwrap_i64());
results[0] = Val::I64(args[1].i64() + 1);
results[1] = Val::I32(args[0].i32() + 1);
results[0] = Val::I64(args[1].unwrap_i64() + 1);
results[1] = Val::I32(args[0].unwrap_i32() + 1);
Ok(())
}
}
@@ -88,10 +88,10 @@ fn main() -> Result<()> {
.map_err(|e| format_err!("> Error calling g! {:?}", e))?;
println!("Printing result...");
println!("> {} {}", results[0].i64(), results[1].i32());
println!("> {} {}", results[0].unwrap_i64(), results[1].unwrap_i32());
debug_assert_eq!(results[0].i64(), 4);
debug_assert_eq!(results[1].i32(), 2);
debug_assert_eq!(results[0].unwrap_i64(), 4);
debug_assert_eq!(results[1].unwrap_i32(), 2);
// Call `$round_trip_many`.
println!("Calling export \"round_trip_many\"...");
@@ -115,7 +115,7 @@ fn main() -> Result<()> {
println!("Printing result...");
print!(">");
for r in results.iter() {
print!(" {}", r.i64());
print!(" {}", r.unwrap_i64());
}
println!();

View File

@@ -148,7 +148,7 @@ impl Func {
}
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, HostRef<Trap>> {
let mut results = vec![Val::default(); self.result_arity()];
let mut results = vec![Val::null(); self.result_arity()];
self.callable.call(params, &mut results)?;
Ok(results.into_boxed_slice())
}
@@ -215,8 +215,8 @@ impl Global {
match self.r#type().content() {
ValType::I32 => Val::from(*definition.as_i32()),
ValType::I64 => Val::from(*definition.as_i64()),
ValType::F32 => Val::from_f32_bits(*definition.as_u32()),
ValType::F64 => Val::from_f64_bits(*definition.as_u64()),
ValType::F32 => Val::F32(*definition.as_u32()),
ValType::F64 => Val::F64(*definition.as_u64()),
_ => unimplemented!("Global::get for {:?}", self.r#type().content()),
}
}

View File

@@ -80,7 +80,7 @@ unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *m
(args, signature.returns.len())
};
let mut returns = vec![Val::default(); returns_len];
let mut returns = vec![Val::null(); returns_len];
let func = &instance
.host_state()
.downcast_mut::<TrampolineState>()

View File

@@ -6,21 +6,71 @@ use std::ptr;
use wasmtime_environ::ir;
use wasmtime_jit::RuntimeValue;
/// Possible runtime values that a WebAssembly module can either consume or
/// produce.
#[derive(Debug, Clone)]
pub enum Val {
/// A 32-bit integer
I32(i32),
/// A 64-bit integer
I64(i64),
/// A 32-bit float.
///
/// Note that the raw bits of the float are stored here, and you can use
/// `f32::from_bits` to create an `f32` value.
F32(u32),
/// A 64-bit float.
///
/// Note that the raw bits of the float are stored here, and you can use
/// `f64::from_bits` to create an `f64` value.
F64(u64),
/// An `anyref` value which can hold opaque data to the wasm instance itself.
///
/// Note that this is a nullable value as well.
AnyRef(AnyRef),
/// A first-class reference to a WebAssembly function.
FuncRef(HostRef<Func>),
/// A 128-bit number
V128([u8; 16]),
}
macro_rules! accessors {
($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
/// Attempt to access the underlying value of this `Val`, returning
/// `None` if it is not the correct type.
pub fn $get(&self) -> Option<$ty> {
if let Val::$variant($bind) = self {
Some($cvt)
} else {
None
}
}
/// Returns the underlying value of this `Val`, panicking if it's the
/// wrong type.
///
/// # Panics
///
/// Panics if `self` is not of the right type.
pub fn $unwrap(&self) -> $ty {
self.$get().expect(concat!("expected ", stringify!($ty)))
}
)*)
}
impl Val {
pub fn default() -> Val {
/// Returns a null `anyref` value.
pub fn null() -> Val {
Val::AnyRef(AnyRef::null())
}
/// Returns the corresponding [`ValType`] for this `Val`.
pub fn r#type(&self) -> ValType {
match self {
Val::I32(_) => ValType::I32,
@@ -29,6 +79,7 @@ impl Val {
Val::F64(_) => ValType::F64,
Val::AnyRef(_) => ValType::AnyRef,
Val::FuncRef(_) => ValType::FuncRef,
Val::V128(_) => ValType::V128,
}
}
@@ -52,52 +103,36 @@ impl Val {
}
}
pub fn from_f32_bits(v: u32) -> Val {
Val::F32(v)
accessors! {
e
(I32(i32) i32 unwrap_i32 *e)
(I64(i64) i64 unwrap_i64 *e)
(F32(f32) f32 unwrap_f32 f32::from_bits(*e))
(F64(f64) f64 unwrap_f64 f64::from_bits(*e))
(FuncRef(&HostRef<Func>) funcref unwrap_funcref e)
(V128(&[u8; 16]) v128 unwrap_v128 e)
}
pub fn from_f64_bits(v: u64) -> Val {
Val::F64(v)
}
pub fn i32(&self) -> i32 {
if let Val::I32(i) = self {
*i
} else {
panic!("Invalid conversion of {:?} to i32.", self);
/// Attempt to access the underlying value of this `Val`, returning
/// `None` if it is not the correct type.
///
/// This will return `Some` for both the `AnyRef` and `FuncRef` types.
pub fn anyref(&self) -> Option<AnyRef> {
match self {
Val::AnyRef(e) => Some(e.clone()),
Val::FuncRef(e) => Some(e.anyref()),
_ => None,
}
}
pub fn i64(&self) -> i64 {
if let Val::I64(i) = self {
*i
} else {
panic!("Invalid conversion of {:?} to i64.", self);
}
}
pub fn f32(&self) -> f32 {
RuntimeValue::F32(self.f32_bits()).unwrap_f32()
}
pub fn f64(&self) -> f64 {
RuntimeValue::F64(self.f64_bits()).unwrap_f64()
}
pub fn f32_bits(&self) -> u32 {
if let Val::F32(i) = self {
*i
} else {
panic!("Invalid conversion of {:?} to f32.", self);
}
}
pub fn f64_bits(&self) -> u64 {
if let Val::F64(i) = self {
*i
} else {
panic!("Invalid conversion of {:?} to f64.", self);
}
/// Returns the underlying value of this `Val`, panicking if it's the
/// wrong type.
///
/// # Panics
///
/// Panics if `self` is not of the right type.
pub fn unwrap_anyref(&self) -> AnyRef {
self.anyref().expect("expected anyref")
}
}
@@ -125,30 +160,6 @@ impl From<f64> for Val {
}
}
impl Into<i32> for Val {
fn into(self) -> i32 {
self.i32()
}
}
impl Into<i64> for Val {
fn into(self) -> i64 {
self.i64()
}
}
impl Into<f32> for Val {
fn into(self) -> f32 {
self.f32()
}
}
impl Into<f64> for Val {
fn into(self) -> f64 {
self.f64()
}
}
impl From<AnyRef> for Val {
fn from(val: AnyRef) -> Val {
match &val {
@@ -170,16 +181,6 @@ impl From<HostRef<Func>> for Val {
}
}
impl Into<AnyRef> for Val {
fn into(self) -> AnyRef {
match self {
Val::AnyRef(r) => r,
Val::FuncRef(f) => f.anyref(),
_ => panic!("Invalid conversion of {:?} to anyref.", self),
}
}
}
impl From<RuntimeValue> for Val {
fn from(rv: RuntimeValue) -> Self {
match rv {
@@ -187,23 +188,7 @@ impl From<RuntimeValue> for Val {
RuntimeValue::I64(i) => Val::I64(i),
RuntimeValue::F32(u) => Val::F32(u),
RuntimeValue::F64(u) => Val::F64(u),
x => {
panic!("unsupported {:?}", x);
}
}
}
}
impl Into<RuntimeValue> for Val {
fn into(self) -> RuntimeValue {
match self {
Val::I32(i) => RuntimeValue::I32(i),
Val::I64(i) => RuntimeValue::I64(i),
Val::F32(u) => RuntimeValue::F32(u),
Val::F64(u) => RuntimeValue::F64(u),
x => {
panic!("unsupported {:?}", x);
}
RuntimeValue::V128(u) => Val::V128(u),
}
}
}

View File

@@ -1553,7 +1553,11 @@ unsafe fn into_funcref(val: Val) -> *mut wasm_ref_t {
if let Val::AnyRef(AnyRef::Null) = val {
return ptr::null_mut();
}
let r = Box::new(wasm_ref_t { r: val.into() });
let anyref = match val.anyref() {
Some(anyref) => anyref,
None => return ptr::null_mut(),
};
let r = Box::new(wasm_ref_t { r: anyref });
Box::into_raw(r)
}

View File

@@ -152,7 +152,14 @@ impl ModuleData {
Ok(values) => values
.to_vec()
.into_iter()
.map(|v: wasmtime::Val| v.into())
.map(|v: wasmtime::Val| match v {
wasmtime::Val::I32(i) => RuntimeValue::I32(i),
wasmtime::Val::I64(i) => RuntimeValue::I64(i),
wasmtime::Val::F32(i) => RuntimeValue::F32(i),
wasmtime::Val::F64(i) => RuntimeValue::F64(i),
wasmtime::Val::V128(i) => RuntimeValue::V128(i),
_ => panic!("unsupported value {:?}", v),
})
.collect::<Vec<RuntimeValue>>(),
Err(trap) => bail!("trapped: {:?}", trap),
};