Files
wasmtime/crates/api/src/ref.rs
Alex Crichton 6ef09359b0 Refactor and fill out wasmtime's C API (#1415)
* Refactor and improve safety of C API

This commit is intended to be a relatively large refactoring of the C
API which is targeted at improving the safety of our C API definitions.
Not all of the APIs have been updated yet but this is intended to be the
start.

The goal here is to make as many functions safe as we can, expressing
inputs/outputs as native Rust types rather than raw pointers wherever
possible. For example instead of `*const wasm_foo_t` we'd take
`&wasm_foo_t`. Instead of returning `*mut wasm_foo_t` we'd return
`Box<wasm_foo_t>`. No ABI/API changes are intended from this commit,
it's supposed to only change how we define all these functions
internally.

This commit also additionally implements a few more API bindings for
exposed vector types by unifying everything into one macro.

Finally, this commit moves many internal caches in the C API to the
`OnceCell` type which provides a safe interface for one-time
initialization.

* Split apart monolithic C API `lib.rs`

This commit splits the monolithic `src/lib.rs` in the C API crate into
lots of smaller files. The goal here is to make this a bit more readable
and digestable. Each module now contains only API bindings for a
particular type, roughly organized around the grouping in the wasm.h
header file already.

A few more extensions were added, such as filling out `*_as_*`
conversions with both const and non-const versions. Additionally many
APIs were made safer in the same style as the previous commit, generally
preferring Rust types rather than raw pointer types.

Overall no functional change is intended here, it should be mostly just
code movement and minor refactorings!

* Make a few wasi C bindings safer

Use safe Rust types where we can and touch up a few APIs here and there.

* Implement `wasm_*type_as_externtype*` APIs

This commit restructures `wasm_externtype_t` to be similar to
`wasm_extern_t` so type conversion between the `*_extern_*` variants to
the concrete variants are all simple casts. (checked in the case of
general to concrete, of course).

* Consistently imlpement host info functions in the API

This commit adds a small macro crate which is then used to consistently
define the various host-info-related functions in the C API. The goal
here is to try to mirror what the `wasm.h` header provides to provide a
full implementation of the header.
2020-03-27 09:50:32 -05:00

224 lines
6.6 KiB
Rust

#![allow(missing_docs)]
use std::any::Any;
use std::cell::{self, RefCell};
use std::fmt;
use std::rc::{Rc, Weak};
trait InternalRefBase: Any {
fn as_any(&self) -> &dyn Any;
fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;
fn set_host_info(&self, info: Option<Box<dyn Any>>);
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;
}
#[derive(Clone)]
pub struct InternalRef(Rc<dyn InternalRefBase>);
impl InternalRef {
pub fn is_ref<T: 'static>(&self) -> bool {
let r = self.0.as_any();
Any::is::<HostRef<T>>(r)
}
pub fn get_ref<T: 'static>(&self) -> HostRef<T> {
let r = self.0.as_any();
r.downcast_ref::<HostRef<T>>()
.expect("reference is not T type")
.clone()
}
}
struct AnyAndHostInfo {
any: Box<dyn Any>,
host_info: Option<Box<dyn Any>>,
}
#[derive(Clone)]
pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);
/// Represents an opaque reference to any data within WebAssembly.
#[derive(Clone)]
pub enum AnyRef {
/// A reference to no data.
Null,
/// A reference to data stored internally in `wasmtime`.
Ref(InternalRef),
/// A reference to data located outside of `wasmtime`.
Other(OtherRef),
}
impl AnyRef {
/// Creates a new instance of `AnyRef` from `Box<dyn Any>`.
pub fn new(data: Box<dyn Any>) -> Self {
let info = AnyAndHostInfo {
any: data,
host_info: None,
};
AnyRef::Other(OtherRef(Rc::new(RefCell::new(info))))
}
/// Creates a `Null` reference.
pub fn null() -> Self {
AnyRef::Null
}
/// Returns the data stored in the reference if available.
/// # Panics
/// Panics if the variant isn't `AnyRef::Other`.
pub fn data(&self) -> cell::Ref<Box<dyn Any>> {
match self {
AnyRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
_ => panic!("expected AnyRef::Other"),
}
}
/// Returns true if the two `AnyRef<T>`'s point to the same value (not just
/// values that compare as equal).
pub fn ptr_eq(&self, other: &AnyRef) -> bool {
match (self, other) {
(AnyRef::Null, AnyRef::Null) => true,
(AnyRef::Ref(InternalRef(ref a)), AnyRef::Ref(InternalRef(ref b))) => {
a.ptr_eq(b.as_ref())
}
(AnyRef::Other(OtherRef(ref a)), AnyRef::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b),
_ => false,
}
}
/// Returns a mutable reference to the host information if available.
/// # Panics
/// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`.
pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {
match self {
AnyRef::Null => panic!("null"),
AnyRef::Ref(r) => r.0.host_info(),
AnyRef::Other(r) => {
let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);
if info.is_none() {
return None;
}
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
}
}
}
/// Sets the host information for an `AnyRef`.
/// # Panics
/// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`.
pub fn set_host_info(&self, info: Option<Box<dyn Any>>) {
match self {
AnyRef::Null => panic!("null"),
AnyRef::Ref(r) => r.0.set_host_info(info),
AnyRef::Other(r) => {
r.0.borrow_mut().host_info = info;
}
}
}
}
impl fmt::Debug for AnyRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AnyRef::Null => write!(f, "null"),
AnyRef::Ref(_) => write!(f, "anyref"),
AnyRef::Other(_) => write!(f, "other ref"),
}
}
}
struct ContentBox<T> {
content: T,
host_info: Option<Box<dyn Any>>,
anyref_data: Weak<dyn InternalRefBase>,
}
/// Represents a piece of data located in the host environment.
pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>);
impl<T: 'static> HostRef<T> {
/// Creates a new `HostRef<T>` from `T`.
pub fn new(item: T) -> HostRef<T> {
let anyref_data: Weak<HostRef<T>> = Weak::new();
let content = ContentBox {
content: item,
host_info: None,
anyref_data,
};
HostRef(Rc::new(RefCell::new(content)))
}
/// Immutably borrows the wrapped data.
/// # Panics
/// Panics if the value is currently mutably borrowed.
pub fn borrow(&self) -> cell::Ref<T> {
cell::Ref::map(self.0.borrow(), |b| &b.content)
}
/// Mutably borrows the wrapped data.
/// # Panics
/// Panics if the `HostRef<T>` is already borrowed.
pub fn borrow_mut(&self) -> cell::RefMut<T> {
cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content)
}
/// Returns true if the two `HostRef<T>`'s point to the same value (not just
/// values that compare as equal).
pub fn ptr_eq(&self, other: &HostRef<T>) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
/// Returns an opaque reference to the wrapped data in the form of
/// an `AnyRef`.
/// # Panics
/// Panics if `HostRef<T>` is already mutably borrowed.
pub fn anyref(&self) -> AnyRef {
let r = self.0.borrow_mut().anyref_data.upgrade();
if let Some(r) = r {
return AnyRef::Ref(InternalRef(r));
}
let anyref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
self.0.borrow_mut().anyref_data = Rc::downgrade(&anyref_data);
AnyRef::Ref(InternalRef(anyref_data))
}
}
impl<T: 'static> InternalRefBase for HostRef<T> {
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool {
if let Some(other) = other.as_any().downcast_ref() {
self.ptr_eq(other)
} else {
false
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {
let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info);
if info.is_none() {
return None;
}
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
}
fn set_host_info(&self, info: Option<Box<dyn Any>>) {
self.0.borrow_mut().host_info = info;
}
}
impl<T> Clone for HostRef<T> {
fn clone(&self) -> HostRef<T> {
HostRef(self.0.clone())
}
}
impl<T: fmt::Debug> fmt::Debug for HostRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ref(")?;
self.0.borrow().content.fmt(f)?;
write!(f, ")")
}
}