Skip to content

Commit

Permalink
Migrate to wasmi_v1 API (#422)
Browse files Browse the repository at this point in the history
  • Loading branch information
graydon authored Sep 10, 2022
1 parent ad11ffe commit 16402ef
Show file tree
Hide file tree
Showing 17 changed files with 926 additions and 479 deletions.
42 changes: 27 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ soroban-env-host = { path = "soroban-env-host" }
soroban-env-macros = { path = "soroban-env-macros" }
soroban-native-sdk-macros = { path = "soroban-native-sdk-macros" }
stellar-xdr = { git = "https://github.com/stellar/rs-stellar-xdr", rev = "fee9a436" }
wasmi = { package = "soroban-wasmi", git = "https://github.com/stellar/wasmi", rev = "7b1f2355" }
wasmi = { package = "soroban-wasmi", git = "https://github.com/stellar/wasmi", rev = "a61b6df" }
2 changes: 1 addition & 1 deletion soroban-env-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ rust-version = "1.63"
[dependencies]
soroban-env-macros = { version = "0.0.5" }
stellar-xdr = { version = "0.0.2", default-features = false, features = [ "next" ] }
wasmi = { package = "soroban-wasmi", version = "0.11.0", optional = true }
wasmi = { package = "soroban-wasmi", version = "0.16.0", optional = true }
static_assertions = "1.1.0"

[features]
Expand Down
2 changes: 1 addition & 1 deletion soroban-env-common/src/checked_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,5 +178,5 @@ macro_rules! impl_env_for_checked_env {
};
}

// Here we invoke the x-macro passing generate_env_trait as its callback macro.
// Here we invoke the x-macro passing impl_env_for_checked_env as its callback macro.
call_macro_with_all_host_functions! { impl_env_for_checked_env }
2 changes: 2 additions & 0 deletions soroban-env-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ mod symbol;
mod tuple;
mod unimplemented_env;
mod val;
mod vmcaller_checked_env;

// Re-export the XDR definitions
pub use stellar_xdr as xdr;
Expand All @@ -50,6 +51,7 @@ pub use convert::TryConvert;
pub use env::{Env, EnvBase};
pub use env_val::{EnvVal, FromVal, IntoVal, TryFromVal, TryIntoVal};
pub use unimplemented_env::UnimplementedEnv;
pub use vmcaller_checked_env::{VmCaller, VmCallerCheckedEnv};

// BitSet, Status and Symbol wrap RawVals.
// TODO: maybe these should wrap EnvVals?
Expand Down
15 changes: 9 additions & 6 deletions soroban-env-common/src/raw_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,17 +248,20 @@ declare_tryfrom!(Static);
declare_tryfrom!(Object);

#[cfg(feature = "vm")]
impl wasmi::FromValue for RawVal {
fn from_value(val: wasmi::RuntimeValue) -> Option<Self> {
let maybe: Option<u64> = val.try_into();
maybe.map(RawVal::from_payload)
impl wasmi::core::FromValue for RawVal {
fn from_value(val: wasmi::core::Value) -> Option<Self> {
if let wasmi::core::Value::I64(i) = val {
Some(RawVal::from_payload(i as u64))
} else {
None
}
}
}

#[cfg(feature = "vm")]
impl From<RawVal> for wasmi::RuntimeValue {
impl From<RawVal> for wasmi::core::Value {
fn from(v: RawVal) -> Self {
wasmi::RuntimeValue::I64(v.get_payload() as i64)
wasmi::core::Value::I64(v.get_payload() as i64)
}
}

Expand Down
10 changes: 5 additions & 5 deletions soroban-env-common/src/val_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ macro_rules! decl_tagged_val_wrapper_methods {

// wasmi / VM argument support
#[cfg(feature = "vm")]
impl wasmi::FromValue for $tagname {
fn from_value(val: wasmi::RuntimeValue) -> Option<Self> {
let maybe: Option<u64> = val.try_into();
impl wasmi::core::FromValue for $tagname {
fn from_value(val: wasmi::core::Value) -> Option<Self> {
let maybe: Option<u64> = <u64 as wasmi::core::FromValue>::from_value(val);
match maybe {
Some(u) => {
let raw = RawVal::from_payload(u);
Expand All @@ -89,9 +89,9 @@ macro_rules! decl_tagged_val_wrapper_methods {
}
}
#[cfg(feature = "vm")]
impl From<$tagname> for wasmi::RuntimeValue {
impl From<$tagname> for wasmi::core::Value {
fn from(v: $tagname) -> Self {
wasmi::RuntimeValue::I64(v.as_raw().get_payload() as i64)
wasmi::core::Value::I64(v.as_raw().get_payload() as i64)
}
}

Expand Down
228 changes: 228 additions & 0 deletions soroban-env-common/src/vmcaller_checked_env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#[cfg(feature = "vm")]
use crate::xdr::ScHostContextErrorCode;

use crate::call_macro_with_all_host_functions;
use crate::{Object, RawVal, Symbol};
use core::fmt::Debug;
#[cfg(not(feature = "vm"))]
use core::marker::PhantomData;

/// The VmCallerCheckedEnv trait is similar to the CheckedEnv trait -- it
/// provides all the same-named methods -- but they have a form that takes an
/// initial [`VmCaller`] argument by `&mut` that may or may-not wrap a
/// `wasmi::Caller` structure, depending on whether it was invoked from a wasmi
/// host-function wrapper.
///
/// There is a blanket `impl<T:VmCallerCheckedEnv> CheckedEnv for T` so that any
/// type (eg. `Host`) that implements `VmCallerCheckedEnv` automatically also
/// implements `CheckedEnv`, just by calling the corresponding
/// `VmCallerCheckedEnv` method with the [`VmCaller::none()`] argument. This
/// allows code to import and use `CheckedEnv` directly (such as the native
/// contract) to call host methods without having to write `VmCaller::none()`
/// everywhere.
#[cfg(feature = "vm")]
pub struct VmCaller<'a, T>(pub Option<wasmi::Caller<'a, T>>);
#[cfg(feature = "vm")]
impl<'a, T> VmCaller<'a, T> {
pub fn none() -> Self {
VmCaller(None)
}
pub fn try_ref(&self) -> Result<&wasmi::Caller<'a, T>, ScHostContextErrorCode> {
match &self.0 {
Some(caller) => Ok(caller),
None => Err(ScHostContextErrorCode::NoContractRunning),
}
}
pub fn try_mut(&mut self) -> Result<&mut wasmi::Caller<'a, T>, ScHostContextErrorCode> {
match &mut self.0 {
Some(caller) => Ok(caller),
None => Err(ScHostContextErrorCode::NoContractRunning),
}
}
}

#[cfg(not(feature = "vm"))]
pub struct VmCaller<'a, T> {
_nothing: PhantomData<&'a T>,
}
#[cfg(not(feature = "vm"))]
impl<'a, T> VmCaller<'a, T> {
pub fn none() -> Self {
VmCaller {
_nothing: PhantomData,
}
}
}

///////////////////////////////////////////////////////////////////////////////
/// X-macro use: defining trait VmCallerCheckedEnv
///////////////////////////////////////////////////////////////////////////////

// This is a helper macro used only by generate_vmcaller_checked_env_trait
// below. It consumes a token-tree of the form:
//
// {fn $fn_id:ident $args:tt -> $ret:ty}
//
// and produces the the corresponding method declaration to be used in the Env
// trait.
macro_rules! host_function_helper {
{
$(#[$attr:meta])*
fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty
}
=>
{
$(#[$attr])*
fn $fn_id(&self, vmcaller: &mut VmCaller<Self::VmUserState>, $($arg:$type),*) -> Result<$ret, Self::Error>;
};
}

// This is a callback macro that pattern-matches the token-tree passed by the
// x-macro (call_macro_with_all_host_functions) and produces a suite of method
// declarations, which it places in the body of the declaration of the
// VmCallerCheckedEnv trait.
macro_rules! generate_vmcaller_checked_env_trait {
{
$(
// This outer pattern matches a single 'mod' block of the token-tree
// passed from the x-macro to this macro. It is embedded in a `$()*`
// pattern-repetition matcher so that it will match all provided
// 'mod' blocks provided.
$(#[$mod_attr:meta])*
mod $mod_id:ident $mod_str:literal
{
$(
// This inner pattern matches a single function description
// inside a 'mod' block in the token-tree passed from the
// x-macro to this macro. It is embedded in a `$()*`
// pattern-repetition matcher so that it will match all such
// descriptions.
$(#[$fn_attr:meta])*
{ $fn_str:literal, fn $fn_id:ident $args:tt -> $ret:ty }
)*
}
)*
}

=> // The part of the macro above this line is a matcher; below is its expansion.

{
// This macro expands to a single item: the VmCallerCheckedEnv trait

/// This trait is a variant of the [Env](crate::Env) trait used to
/// define the interface implemented by Host. The wasmi VM dispatch
/// functions call methods on `VmCallerCheckedEnv`, passing a
/// [`VmCaller`] that wraps the wasmi Caller context, and then convert
/// any `Result::Err(...)` return value into a VM trap, halting VM
/// execution.
///
/// There is also a blanket `impl<T:VmCallerCheckedEnv> CheckedEnv for
/// T` that implements the `CheckedEnv` for any `VmCallerCheckedEnv` by
/// passing [`VmCaller::none()`] for the first argument, allowing user
/// code such as the native contract to avoid writing `VmCaller::none()`
/// everywhere.
pub trait VmCallerCheckedEnv
{
type VmUserState;
type Error: Debug;
$(
$(
// This invokes the host_function_helper! macro above
// passing only the relevant parts of the declaration
// matched by the inner pattern above. It is embedded in two
// nested `$()*` pattern-repetition expanders that
// correspond to the pattern-repetition matchers in the
// match section, but we ignore the structure of the 'mod'
// block repetition-level from the outer pattern in the
// expansion, flattening all functions from all 'mod' blocks
// into the VmCallerCheckedEnv trait.
host_function_helper!{$(#[$fn_attr])* fn $fn_id $args -> $ret}
)*
)*
}
};
}

// Here we invoke the x-macro passing generate_env_trait as its callback macro.
call_macro_with_all_host_functions! { generate_vmcaller_checked_env_trait }

///////////////////////////////////////////////////////////////////////////////
/// X-macro use: impl<E> CheckedEnv for VmCallerCheckedEnv<E>
///////////////////////////////////////////////////////////////////////////////

// This is a helper macro used only by
// generate_impl_checked_env_for_vmcaller_checked_env below. It consumes a
// token-tree of the form:
//
// {fn $fn_id:ident $args:tt -> $ret:ty}
//
// and produces the the corresponding method declaration to be used in the Env
// trait.
macro_rules! vmcaller_none_function_helper {
{fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty}
=>
{
fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error> {
<Self as VmCallerCheckedEnv>::$fn_id(self, &mut VmCaller::none(), $($arg),*)
}
};
}

// This is a callback macro that pattern-matches the token-tree passed by the
// x-macro (call_macro_with_all_host_functions) and produces a suite of method
// declarations, which it places in the body of the blanket impl of Env for
// T:CheckedEnv
macro_rules! impl_checked_env_for_vmcaller_checked_env {
{
$(
// This outer pattern matches a single 'mod' block of the token-tree
// passed from the x-macro to this macro. It is embedded in a `$()*`
// pattern-repetition matcher so that it will match all provided
// 'mod' blocks provided.
$(#[$mod_attr:meta])*
mod $mod_id:ident $mod_str:literal
{
$(
// This inner pattern matches a single function description
// inside a 'mod' block in the token-tree passed from the
// x-macro to this macro. It is embedded in a `$()*`
// pattern-repetition matcher so that it will match all such
// descriptions.
$(#[$fn_attr:meta])*
{ $fn_str:literal, fn $fn_id:ident $args:tt -> $ret:ty }
)*
}
)*
}

=> // The part of the macro above this line is a matcher; below is its expansion.

{
// This macro expands to a single item: a blanket impl that makes all
// `VmCallerCheckedEnv` types automatically `CheckedEnv` types, just
// passing [`VmCaller::none()`] as their first argument.
impl<T:VmCallerCheckedEnv> $crate::CheckedEnv for T
{
type Error = <Self as VmCallerCheckedEnv>::Error;
$(
$(
// This invokes the vmcaller_none_function_helper! macro above
// passing only the relevant parts of the declaration
// matched by the inner pattern above. It is embedded in two
// nested `$()*` pattern-repetition expanders that
// correspond to the pattern-repetition matchers in the
// match section, but we ignore the structure of the 'mod'
// block repetition-level from the outer pattern in the
// expansion, flattening all functions from all 'mod' blocks
// into the impl.
vmcaller_none_function_helper!{fn $fn_id $args -> $ret}
)*
)*
}
};
}

// Here we invoke the x-macro passing
// generate_checked_env_for_vmcaller_checked_env as its callback macro.
call_macro_with_all_host_functions! { impl_checked_env_for_vmcaller_checked_env }
Loading

0 comments on commit 16402ef

Please sign in to comment.