Skip to content

Commit

Permalink
copy wasmer2 imports module into near_vm (near#8845)
Browse files Browse the repository at this point in the history
This is just a copy-paste extracted from near#8323 for now, changes to actually make it be for near-vm will come in later.
  • Loading branch information
Ekleog-NEAR authored and nikurt committed Apr 5, 2023
1 parent b16bd3f commit 0a04e3a
Showing 1 changed file with 165 additions and 0 deletions.
165 changes: 165 additions & 0 deletions runtime/near-vm-runner/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,171 @@ pub(crate) mod wasmer2 {
}
}

// TODO: this is currently just a copy-paste of the wasmer2_vm block
// This will have to actually become near_vm bindings
#[allow(dead_code)]
#[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))]
pub(crate) mod near_vm {
use std::sync::Arc;

use super::str_eq;
use near_vm_logic::{ProtocolVersion, VMLogic};
use wasmer_engine::Engine;
use wasmer_engine_universal::UniversalEngine;
use wasmer_vm::{
ExportFunction, ExportFunctionMetadata, Resolver, VMFunction, VMFunctionKind, VMMemory,
};

pub(crate) struct Wasmer2Imports<'engine, 'vmlogic, 'vmlogic_refs> {
pub(crate) memory: VMMemory,
// Note: this same object is also referenced by the `metadata` field!
pub(crate) vmlogic: &'vmlogic mut VMLogic<'vmlogic_refs>,
pub(crate) metadata: Arc<ExportFunctionMetadata>,
pub(crate) protocol_version: ProtocolVersion,
pub(crate) engine: &'engine UniversalEngine,
}

trait Wasmer2Type {
type Wasmer;
fn to_wasmer(self) -> Self::Wasmer;
fn ty() -> wasmer_types::Type;
}
macro_rules! wasmer_types {
($($native:ty as $wasmer:ty => $type_expr:expr;)*) => {
$(impl Wasmer2Type for $native {
type Wasmer = $wasmer;
fn to_wasmer(self) -> $wasmer {
self as _
}
fn ty() -> wasmer_types::Type {
$type_expr
}
})*
}
}
wasmer_types! {
u32 as i32 => wasmer_types::Type::I32;
u64 as i64 => wasmer_types::Type::I64;
}

macro_rules! return_ty {
($return_type: ident = [ ]) => {
type $return_type = ();
fn make_ret() -> () {}
};
($return_type: ident = [ $($returns: ident),* ]) => {
#[repr(C)]
struct $return_type($(<$returns as Wasmer2Type>::Wasmer),*);
fn make_ret($($returns: $returns),*) -> Ret { Ret($($returns.to_wasmer()),*) }
}
}

impl<'e, 'l, 'lr> Resolver for Wasmer2Imports<'e, 'l, 'lr> {
fn resolve(&self, _index: u32, module: &str, field: &str) -> Option<wasmer_vm::Export> {
if module == "env" && field == "memory" {
return Some(wasmer_vm::Export::Memory(self.memory.clone()));
}

macro_rules! add_import {
(
$mod:ident / $name:ident : $func:ident <
[ $( $arg_name:ident : $arg_type:ident ),* ]
-> [ $( $returns:ident ),* ]
>
) => {
return_ty!(Ret = [ $($returns),* ]);

extern "C" fn $name(env: *mut VMLogic<'_>, $( $arg_name: $arg_type ),* )
-> Ret {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
const IS_GAS: bool = str_eq(stringify!($name), "gas");
let _span = if IS_GAS {
None
} else {
Some(tracing::trace_span!(
target: "host-function",
stringify!($name)
).entered())
};

// SAFETY: This code should only be executable within `'vmlogic`
// lifetime and so it is safe to dereference the `env` pointer which is
// known to be derived from a valid `&'vmlogic mut VMLogic<'_>` in the
// first place.
unsafe { (*env).$func( $( $arg_name, )* ) }
}));
// We want to ensure that the only kind of error that host function calls
// return are VMLogicError. This is important because we later attempt to
// downcast the `RuntimeError`s into `VMLogicError`.
let result: Result<Result<_, near_vm_errors::VMLogicError>, _> = result;
#[allow(unused_parens)]
match result {
Ok(Ok(($($returns),*))) => make_ret($($returns),*),
Ok(Err(trap)) => unsafe {
// SAFETY: this can only be called by a WASM contract, so all the
// necessary hooks are known to be in place.
wasmer_vm::raise_user_trap(Box::new(trap))
},
Err(e) => unsafe {
// SAFETY: this can only be called by a WASM contract, so all the
// necessary hooks are known to be in place.
wasmer_vm::resume_panic(e)
},
}
}
// TODO: a phf hashmap would probably work better here.
if module == stringify!($mod) && field == stringify!($name) {
let args = [$(<$arg_type as Wasmer2Type>::ty()),*];
let rets = [$(<$returns as Wasmer2Type>::ty()),*];
let signature = wasmer_types::FunctionTypeRef::new(&args[..], &rets[..]);
let signature = self.engine.register_signature(signature);
return Some(wasmer_vm::Export::Function(ExportFunction {
vm_function: VMFunction {
address: $name as *const _,
// SAFETY: here we erase the lifetime of the `vmlogic` reference,
// but we believe that the lifetimes on `Wasmer2Imports` enforce
// sufficiently that it isn't possible to call this exported
// function when vmlogic is no loger live.
vmctx: wasmer_vm::VMFunctionEnvironment {
host_env: self.vmlogic as *const _ as *mut _
},
signature,
kind: VMFunctionKind::Static,
call_trampoline: None,
instance_ref: None,
},
metadata: Some(Arc::clone(&self.metadata)),
}));
}
};
}
for_each_available_import!(self.protocol_version, add_import);
return None;
}
}

pub(crate) fn build<'e, 'a, 'b>(
memory: VMMemory,
logic: &'a mut VMLogic<'b>,
protocol_version: ProtocolVersion,
engine: &'e UniversalEngine,
) -> Wasmer2Imports<'e, 'a, 'b> {
let metadata = unsafe {
// SAFETY: the functions here are thread-safe. We ensure that the lifetime of `VMLogic`
// is sufficiently long by tying the lifetime of VMLogic to the return type which
// contains this metadata.
ExportFunctionMetadata::new(logic as *mut _ as *mut _, None, |ptr| ptr, |_| {})
};
Wasmer2Imports {
memory,
vmlogic: logic,
metadata: Arc::new(metadata),
protocol_version,
engine,
}
}
}

#[cfg(feature = "wasmtime_vm")]
pub(crate) mod wasmtime {
use super::str_eq;
Expand Down

0 comments on commit 0a04e3a

Please sign in to comment.