mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-13 00:51:20 +00:00
Merge branch 'master' into feat-runtime-core-field-offset
This commit is contained in:
@ -15,7 +15,11 @@ use crate::{
|
||||
},
|
||||
vm,
|
||||
};
|
||||
use std::{fmt::Debug, slice};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
ptr::{self, NonNull},
|
||||
slice,
|
||||
};
|
||||
|
||||
/// Size of the array for internal instance usage
|
||||
pub const INTERNALS_SIZE: usize = 256;
|
||||
@ -383,9 +387,9 @@ impl LocalBacking {
|
||||
vmctx,
|
||||
),
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
let vm::ImportedFunc { func, vmctx } =
|
||||
let vm::ImportedFunc { func, func_ctx } =
|
||||
imports.vm_functions[imported_func_index];
|
||||
(func, vmctx)
|
||||
(func, unsafe { func_ctx.as_ref() }.vmctx.as_ptr())
|
||||
}
|
||||
};
|
||||
|
||||
@ -416,9 +420,9 @@ impl LocalBacking {
|
||||
vmctx,
|
||||
),
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
let vm::ImportedFunc { func, vmctx } =
|
||||
let vm::ImportedFunc { func, func_ctx } =
|
||||
imports.vm_functions[imported_func_index];
|
||||
(func, vmctx)
|
||||
(func, unsafe { func_ctx.as_ref() }.vmctx.as_ptr())
|
||||
}
|
||||
};
|
||||
|
||||
@ -546,6 +550,15 @@ impl ImportBacking {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ImportBacking {
|
||||
fn drop(&mut self) {
|
||||
// Properly drop the `vm::FuncCtx` in `vm::ImportedFunc`.
|
||||
for (_imported_func_index, imported_func) in (*self.vm_functions).iter_mut() {
|
||||
let _: Box<vm::FuncCtx> = unsafe { Box::from_raw(imported_func.func_ctx.as_ptr()) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn import_functions(
|
||||
module: &ModuleInner,
|
||||
imports: &ImportObject,
|
||||
@ -569,6 +582,7 @@ fn import_functions(
|
||||
|
||||
let import =
|
||||
imports.maybe_with_namespace(namespace, |namespace| namespace.get_export(name));
|
||||
|
||||
match import {
|
||||
Some(Export::Function {
|
||||
func,
|
||||
@ -578,10 +592,28 @@ fn import_functions(
|
||||
if *expected_sig == *signature {
|
||||
functions.push(vm::ImportedFunc {
|
||||
func: func.inner(),
|
||||
vmctx: match ctx {
|
||||
Context::External(ctx) => ctx,
|
||||
Context::Internal => vmctx,
|
||||
},
|
||||
func_ctx: NonNull::new(Box::into_raw(Box::new(vm::FuncCtx {
|
||||
// ^^^^^^^^ `vm::FuncCtx` is purposely leaked.
|
||||
// It is dropped by the specific `Drop`
|
||||
// implementation of `ImportBacking`.
|
||||
vmctx: NonNull::new(match ctx {
|
||||
Context::External(vmctx) => vmctx,
|
||||
Context::ExternalWithEnv(vmctx_, _) => {
|
||||
if vmctx_.is_null() {
|
||||
vmctx
|
||||
} else {
|
||||
vmctx_
|
||||
}
|
||||
}
|
||||
Context::Internal => vmctx,
|
||||
})
|
||||
.expect("`vmctx` must not be null."),
|
||||
func_env: match ctx {
|
||||
Context::ExternalWithEnv(_, func_env) => func_env,
|
||||
_ => None,
|
||||
},
|
||||
})))
|
||||
.unwrap(),
|
||||
});
|
||||
} else {
|
||||
link_errors.push(LinkError::IncorrectImportSignature {
|
||||
@ -610,8 +642,8 @@ fn import_functions(
|
||||
None => {
|
||||
if imports.allow_missing_functions {
|
||||
functions.push(vm::ImportedFunc {
|
||||
func: ::std::ptr::null(),
|
||||
vmctx: ::std::ptr::null_mut(),
|
||||
func: ptr::null(),
|
||||
func_ctx: unsafe { NonNull::new_unchecked(ptr::null_mut()) }, // TODO: Non-sense…
|
||||
});
|
||||
} else {
|
||||
link_errors.push(LinkError::ImportNotFound {
|
||||
|
@ -6,13 +6,18 @@ use crate::{
|
||||
module::ModuleInner, table::Table, types::FuncSig, vm,
|
||||
};
|
||||
use indexmap::map::Iter as IndexMapIter;
|
||||
use std::sync::Arc;
|
||||
use std::{ptr::NonNull, sync::Arc};
|
||||
|
||||
/// A kind of Context.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Context {
|
||||
/// External context include a mutable pointer to `Ctx`.
|
||||
External(*mut vm::Ctx),
|
||||
|
||||
/// External context with an environment include a mutable pointer
|
||||
/// to `Ctx` and an optional non-null pointer to `FuncEnv`.
|
||||
ExternalWithEnv(*mut vm::Ctx, Option<NonNull<vm::FuncEnv>>),
|
||||
|
||||
/// Internal context.
|
||||
Internal,
|
||||
}
|
||||
|
@ -113,9 +113,13 @@ impl Instance {
|
||||
|
||||
let ctx_ptr = match start_index.local_or_import(&instance.module.info) {
|
||||
LocalOrImport::Local(_) => instance.inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
instance.inner.import_backing.vm_functions[imported_func_index].vmctx
|
||||
LocalOrImport::Import(imported_func_index) => unsafe {
|
||||
instance.inner.import_backing.vm_functions[imported_func_index]
|
||||
.func_ctx
|
||||
.as_ref()
|
||||
}
|
||||
.vmctx
|
||||
.as_ptr(),
|
||||
};
|
||||
|
||||
let sig_index = *instance
|
||||
@ -132,7 +136,7 @@ impl Instance {
|
||||
.expect("wasm trampoline");
|
||||
|
||||
let start_func: Func<(), (), Wasm> =
|
||||
unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, ctx_ptr) };
|
||||
unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, None, ctx_ptr) };
|
||||
|
||||
start_func.call()?;
|
||||
}
|
||||
@ -199,9 +203,13 @@ impl Instance {
|
||||
|
||||
let ctx = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(_) => self.inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
self.inner.import_backing.vm_functions[imported_func_index].vmctx
|
||||
LocalOrImport::Import(imported_func_index) => unsafe {
|
||||
self.inner.import_backing.vm_functions[imported_func_index]
|
||||
.func_ctx
|
||||
.as_ref()
|
||||
}
|
||||
.vmctx
|
||||
.as_ptr(),
|
||||
};
|
||||
|
||||
let func_wasm_inner = self
|
||||
@ -210,20 +218,26 @@ impl Instance {
|
||||
.get_trampoline(&self.module.info, sig_index)
|
||||
.unwrap();
|
||||
|
||||
let func_ptr = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(local_func_index) => self
|
||||
.module
|
||||
.runnable_module
|
||||
.get_func(&self.module.info, local_func_index)
|
||||
.unwrap(),
|
||||
LocalOrImport::Import(import_func_index) => NonNull::new(
|
||||
self.inner.import_backing.vm_functions[import_func_index].func as *mut _,
|
||||
)
|
||||
.unwrap(),
|
||||
let (func_ptr, func_env) = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
self.module
|
||||
.runnable_module
|
||||
.get_func(&self.module.info, local_func_index)
|
||||
.unwrap(),
|
||||
None,
|
||||
),
|
||||
LocalOrImport::Import(import_func_index) => {
|
||||
let imported_func = &self.inner.import_backing.vm_functions[import_func_index];
|
||||
|
||||
(
|
||||
NonNull::new(imported_func.func as *mut _).unwrap(),
|
||||
unsafe { imported_func.func_ctx.as_ref() }.func_env,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let typed_func: Func<Args, Rets, Wasm> =
|
||||
unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, ctx) };
|
||||
unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, func_env, ctx) };
|
||||
|
||||
Ok(typed_func)
|
||||
} else {
|
||||
@ -412,6 +426,7 @@ impl InstanceInner {
|
||||
ctx: match ctx {
|
||||
Context::Internal => Context::External(self.vmctx),
|
||||
ctx @ Context::External(_) => ctx,
|
||||
ctx @ Context::ExternalWithEnv(_, _) => ctx,
|
||||
},
|
||||
signature,
|
||||
}
|
||||
@ -454,15 +469,16 @@ impl InstanceInner {
|
||||
),
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
let imported_func = &self.import_backing.vm_functions[imported_func_index];
|
||||
let func_ctx = unsafe { imported_func.func_ctx.as_ref() };
|
||||
|
||||
(
|
||||
imported_func.func as *const _,
|
||||
Context::External(imported_func.vmctx),
|
||||
Context::ExternalWithEnv(func_ctx.vmctx.as_ptr(), func_ctx.func_env),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]);
|
||||
// let signature = &module.info.signatures[sig_index];
|
||||
|
||||
(unsafe { FuncPointer::new(func_ptr) }, ctx, signature)
|
||||
}
|
||||
@ -581,9 +597,13 @@ fn call_func_with_index(
|
||||
|
||||
let ctx_ptr = match func_index.local_or_import(info) {
|
||||
LocalOrImport::Local(_) => local_ctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
import_backing.vm_functions[imported_func_index].vmctx
|
||||
LocalOrImport::Import(imported_func_index) => unsafe {
|
||||
import_backing.vm_functions[imported_func_index]
|
||||
.func_ctx
|
||||
.as_ref()
|
||||
}
|
||||
.vmctx
|
||||
.as_ptr(),
|
||||
};
|
||||
|
||||
let wasm = runnable
|
||||
|
@ -192,7 +192,7 @@ where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// Conver to function pointer.
|
||||
fn to_raw(&self) -> NonNull<vm::Func>;
|
||||
fn to_raw(self) -> (NonNull<vm::Func>, Option<NonNull<vm::FuncEnv>>);
|
||||
}
|
||||
|
||||
/// Represents a TrapEarly type.
|
||||
@ -230,8 +230,9 @@ where
|
||||
/// Represents a function that can be used by WebAssembly.
|
||||
pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> {
|
||||
inner: Inner,
|
||||
f: NonNull<vm::Func>,
|
||||
ctx: *mut vm::Ctx,
|
||||
func: NonNull<vm::Func>,
|
||||
func_env: Option<NonNull<vm::FuncEnv>>,
|
||||
vmctx: *mut vm::Ctx,
|
||||
_phantom: PhantomData<(&'a (), Args, Rets)>,
|
||||
}
|
||||
|
||||
@ -245,20 +246,22 @@ where
|
||||
{
|
||||
pub(crate) unsafe fn from_raw_parts(
|
||||
inner: Wasm,
|
||||
f: NonNull<vm::Func>,
|
||||
ctx: *mut vm::Ctx,
|
||||
func: NonNull<vm::Func>,
|
||||
func_env: Option<NonNull<vm::FuncEnv>>,
|
||||
vmctx: *mut vm::Ctx,
|
||||
) -> Func<'a, Args, Rets, Wasm> {
|
||||
Func {
|
||||
inner,
|
||||
f,
|
||||
ctx,
|
||||
func,
|
||||
func_env,
|
||||
vmctx,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the underlying func pointer.
|
||||
pub fn get_vm_func(&self) -> NonNull<vm::Func> {
|
||||
self.f
|
||||
self.func
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,15 +271,18 @@ where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// Creates a new `Func`.
|
||||
pub fn new<F, Kind>(f: F) -> Func<'a, Args, Rets, Host>
|
||||
pub fn new<F, Kind>(func: F) -> Func<'a, Args, Rets, Host>
|
||||
where
|
||||
Kind: ExternalFunctionKind,
|
||||
F: ExternalFunction<Kind, Args, Rets>,
|
||||
{
|
||||
let (func, func_env) = func.to_raw();
|
||||
|
||||
Func {
|
||||
inner: Host(()),
|
||||
f: f.to_raw(),
|
||||
ctx: ptr::null_mut(),
|
||||
func,
|
||||
func_env,
|
||||
vmctx: ptr::null_mut(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -414,7 +420,7 @@ where
|
||||
{
|
||||
/// Call wasm function and return results.
|
||||
pub fn call(&self, a: A) -> Result<Rets, RuntimeError> {
|
||||
unsafe { <A as WasmTypeList>::call(a, self.f, self.inner, self.ctx) }
|
||||
unsafe { <A as WasmTypeList>::call(a, self.func, self.inner, self.vmctx) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,56 +512,113 @@ macro_rules! impl_traits {
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap,
|
||||
FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap + 'static,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(&self) -> NonNull<vm::Func> {
|
||||
if mem::size_of::<Self>() == 0 {
|
||||
/// This is required for the llvm backend to be able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x, )* Rets, Trap, FN>(
|
||||
vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )*
|
||||
) -> Rets::CStruct
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn(&mut vm::Ctx, $( $x, )*) -> Trap,
|
||||
{
|
||||
let f: FN = unsafe { mem::transmute_copy(&()) };
|
||||
fn to_raw(self) -> (NonNull<vm::Func>, Option<NonNull<vm::FuncEnv>>) {
|
||||
// The `wrap` function is a wrapper around the
|
||||
// imported function. It manages the argument passed
|
||||
// to the imported function (in this case, the
|
||||
// `vmctx` along with the regular WebAssembly
|
||||
// arguments), and it manages the trapping.
|
||||
//
|
||||
// It is also required for the LLVM backend to be
|
||||
// able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x, )* Rets, Trap, FN>(
|
||||
vmctx: &vm::Ctx $( , $x: <$x as WasmExternType>::Native )*
|
||||
) -> Rets::CStruct
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn(&mut vm::Ctx, $( $x, )*) -> Trap,
|
||||
{
|
||||
// Get the pointer to this `wrap` function.
|
||||
let self_pointer = wrap::<$( $x, )* Rets, Trap, FN> as *const vm::Func;
|
||||
|
||||
let err = match panic::catch_unwind(
|
||||
panic::AssertUnwindSafe(
|
||||
|| {
|
||||
f(vmctx $( , WasmExternType::from_native($x) )* ).report()
|
||||
}
|
||||
)
|
||||
) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
b as Box<dyn Any>
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
// Get the collection of imported functions.
|
||||
let vm_imported_functions = unsafe { &(*vmctx.import_backing).vm_functions };
|
||||
|
||||
unsafe {
|
||||
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||
}
|
||||
// Retrieve the `vm::FuncCtx`.
|
||||
let mut func_ctx: NonNull<vm::FuncCtx> = vm_imported_functions
|
||||
.iter()
|
||||
.find_map(|(_, imported_func)| {
|
||||
if imported_func.func == self_pointer {
|
||||
Some(imported_func.func_ctx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.expect("Import backing is not well-formed, cannot find `func_ctx`.");
|
||||
let func_ctx = unsafe { func_ctx.as_mut() };
|
||||
|
||||
// Extract `vm::Ctx` from `vm::FuncCtx`. The
|
||||
// pointer is always non-null.
|
||||
let vmctx = unsafe { func_ctx.vmctx.as_mut() };
|
||||
|
||||
// Extract `vm::FuncEnv` from `vm::FuncCtx`.
|
||||
let func_env = func_ctx.func_env;
|
||||
|
||||
let func: &FN = match func_env {
|
||||
// The imported function is a regular
|
||||
// function, a closure without a captured
|
||||
// environment, or a closure with a captured
|
||||
// environment.
|
||||
Some(func_env) => unsafe {
|
||||
let func: NonNull<FN> = func_env.cast();
|
||||
|
||||
&*func.as_ptr()
|
||||
},
|
||||
|
||||
// This branch is supposed to be unreachable.
|
||||
None => unreachable!()
|
||||
};
|
||||
|
||||
// Catch unwind in case of errors.
|
||||
let err = match panic::catch_unwind(
|
||||
panic::AssertUnwindSafe(
|
||||
|| {
|
||||
func(vmctx $( , WasmExternType::from_native($x) )* ).report()
|
||||
// ^^^^^ The imported function
|
||||
// expects `vm::Ctx` as first
|
||||
// argument; provide it.
|
||||
}
|
||||
)
|
||||
) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
b as Box<dyn Any>
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
// At this point, there is an error that needs to
|
||||
// be trapped.
|
||||
unsafe {
|
||||
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||
}
|
||||
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
|
||||
} else {
|
||||
assert_eq!(
|
||||
mem::size_of::<Self>(),
|
||||
mem::size_of::<usize>(),
|
||||
"you cannot use a closure that captures state for `Func`."
|
||||
);
|
||||
|
||||
NonNull::new(unsafe {
|
||||
mem::transmute_copy::<_, *mut vm::Func>(self)
|
||||
}).unwrap()
|
||||
}
|
||||
|
||||
// Extract the captured environment of the imported
|
||||
// function if any.
|
||||
let func_env: Option<NonNull<vm::FuncEnv>> =
|
||||
// `FN` is a function pointer, or a closure
|
||||
// _without_ a captured environment.
|
||||
if mem::size_of::<Self>() == 0 {
|
||||
NonNull::new(&self as *const _ as *mut vm::FuncEnv)
|
||||
}
|
||||
// `FN` is a closure _with_ a captured
|
||||
// environment.
|
||||
else {
|
||||
NonNull::new(Box::into_raw(Box::new(self))).map(NonNull::cast)
|
||||
};
|
||||
|
||||
(
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap(),
|
||||
func_env
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,56 +627,110 @@ macro_rules! impl_traits {
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn($( $x, )*) -> Trap,
|
||||
FN: Fn($( $x, )*) -> Trap + 'static,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(&self) -> NonNull<vm::Func> {
|
||||
if mem::size_of::<Self>() == 0 {
|
||||
/// This is required for the llvm backend to be able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x, )* Rets, Trap, FN>(
|
||||
vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )*
|
||||
) -> Rets::CStruct
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn($( $x, )*) -> Trap,
|
||||
{
|
||||
let f: FN = unsafe { mem::transmute_copy(&()) };
|
||||
fn to_raw(self) -> (NonNull<vm::Func>, Option<NonNull<vm::FuncEnv>>) {
|
||||
// The `wrap` function is a wrapper around the
|
||||
// imported function. It manages the argument passed
|
||||
// to the imported function (in this case, only the
|
||||
// regular WebAssembly arguments), and it manages the
|
||||
// trapping.
|
||||
//
|
||||
// It is also required for the LLVM backend to be
|
||||
// able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x, )* Rets, Trap, FN>(
|
||||
vmctx: &vm::Ctx $( , $x: <$x as WasmExternType>::Native )*
|
||||
) -> Rets::CStruct
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn($( $x, )*) -> Trap,
|
||||
{
|
||||
// Get the pointer to this `wrap` function.
|
||||
let self_pointer = wrap::<$( $x, )* Rets, Trap, FN> as *const vm::Func;
|
||||
|
||||
let err = match panic::catch_unwind(
|
||||
panic::AssertUnwindSafe(
|
||||
|| {
|
||||
f($( WasmExternType::from_native($x), )* ).report()
|
||||
}
|
||||
)
|
||||
) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
b as Box<dyn Any>
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
// Get the collection of imported functions.
|
||||
let vm_imported_functions = unsafe { &(*vmctx.import_backing).vm_functions };
|
||||
|
||||
unsafe {
|
||||
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||
}
|
||||
// Retrieve the `vm::FuncCtx`.
|
||||
let mut func_ctx: NonNull<vm::FuncCtx> = vm_imported_functions
|
||||
.iter()
|
||||
.find_map(|(_, imported_func)| {
|
||||
if imported_func.func == self_pointer {
|
||||
Some(imported_func.func_ctx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.expect("Import backing is not well-formed, cannot find `func_ctx`.");
|
||||
let func_ctx = unsafe { func_ctx.as_mut() };
|
||||
|
||||
// Extract `vm::Ctx` from `vm::FuncCtx`. The
|
||||
// pointer is always non-null.
|
||||
let vmctx = unsafe { func_ctx.vmctx.as_mut() };
|
||||
|
||||
// Extract `vm::FuncEnv` from `vm::FuncCtx`.
|
||||
let func_env = func_ctx.func_env;
|
||||
|
||||
let func: &FN = match func_env {
|
||||
// The imported function is a regular
|
||||
// function, a closure without a captured
|
||||
// environment, or a closure with a captured
|
||||
// environment.
|
||||
Some(func_env) => unsafe {
|
||||
let func: NonNull<FN> = func_env.cast();
|
||||
|
||||
&*func.as_ptr()
|
||||
},
|
||||
|
||||
// This branch is supposed to be unreachable.
|
||||
None => unreachable!()
|
||||
};
|
||||
|
||||
// Catch unwind in case of errors.
|
||||
let err = match panic::catch_unwind(
|
||||
panic::AssertUnwindSafe(
|
||||
|| {
|
||||
func($( WasmExternType::from_native($x), )* ).report()
|
||||
}
|
||||
)
|
||||
) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
b as Box<dyn Any>
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
// At this point, there is an error that needs to
|
||||
// be trapped.
|
||||
unsafe {
|
||||
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||
}
|
||||
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
|
||||
} else {
|
||||
assert_eq!(
|
||||
mem::size_of::<Self>(),
|
||||
mem::size_of::<usize>(),
|
||||
"you cannot use a closure that captures state for `Func`."
|
||||
);
|
||||
|
||||
NonNull::new(unsafe {
|
||||
mem::transmute_copy::<_, *mut vm::Func>(self)
|
||||
}).unwrap()
|
||||
}
|
||||
|
||||
// Extract the captured environment of the imported
|
||||
// function if any.
|
||||
let func_env: Option<NonNull<vm::FuncEnv>> =
|
||||
// `FN` is a function pointer, or a closure
|
||||
// _without_ a captured environment.
|
||||
if mem::size_of::<Self>() == 0 {
|
||||
NonNull::new(&self as *const _ as *mut vm::FuncEnv)
|
||||
}
|
||||
// `FN` is a closure _with_ a captured
|
||||
// environment.
|
||||
else {
|
||||
NonNull::new(Box::into_raw(Box::new(self))).map(NonNull::cast)
|
||||
};
|
||||
|
||||
(
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap(),
|
||||
func_env
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,9 +746,9 @@ macro_rules! impl_traits {
|
||||
unsafe {
|
||||
<( $( $x ),* ) as WasmTypeList>::call(
|
||||
( $( $x ),* ),
|
||||
self.f,
|
||||
self.func,
|
||||
self.inner,
|
||||
self.ctx
|
||||
self.vmctx
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -669,8 +786,11 @@ where
|
||||
Inner: Kind,
|
||||
{
|
||||
fn to_export(&self) -> Export {
|
||||
let func = unsafe { FuncPointer::new(self.f.as_ptr()) };
|
||||
let ctx = Context::Internal;
|
||||
let func = unsafe { FuncPointer::new(self.func.as_ptr()) };
|
||||
let ctx = match self.func_env {
|
||||
func_env @ Some(_) => Context::ExternalWithEnv(self.vmctx, func_env),
|
||||
None => Context::Internal,
|
||||
};
|
||||
let signature = Arc::new(FuncSig::new(Args::types(), Rets::types()));
|
||||
|
||||
Export::Function {
|
||||
|
@ -523,24 +523,65 @@ impl Ctx {
|
||||
}
|
||||
}
|
||||
|
||||
enum InnerFunc {}
|
||||
/// Used to provide type safety (ish) for passing around function pointers.
|
||||
/// The typesystem ensures this cannot be dereferenced since an
|
||||
/// empty enum cannot actually exist.
|
||||
#[repr(C)]
|
||||
pub struct Func(InnerFunc);
|
||||
/// Represents a function pointer. It is mostly used in the
|
||||
/// `typed_func` module within the `wrap` functions, to wrap imported
|
||||
/// functions.
|
||||
#[repr(transparent)]
|
||||
pub struct Func(pub(self) *mut c_void);
|
||||
|
||||
/// An imported function, which contains the vmctx that owns this function.
|
||||
/// Represents a function environment pointer, like a captured
|
||||
/// environment of a closure. It is mostly used in the `typed_func`
|
||||
/// module within the `wrap` functions, to wrap imported functions.
|
||||
#[repr(transparent)]
|
||||
pub struct FuncEnv(pub(self) *mut c_void);
|
||||
|
||||
/// Represents a function context. It is used by imported functions
|
||||
/// only.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct FuncCtx {
|
||||
/// The `Ctx` pointer.
|
||||
pub(crate) vmctx: NonNull<Ctx>,
|
||||
|
||||
/// A pointer to the function environment. It is used by imported
|
||||
/// functions only to store the pointer to the real host function,
|
||||
/// whether it is a regular function, or a closure with or without
|
||||
/// a captured environment.
|
||||
pub(crate) func_env: Option<NonNull<FuncEnv>>,
|
||||
}
|
||||
|
||||
impl FuncCtx {
|
||||
/// Offset to `vmctx`.
|
||||
pub fn offset_vmctx() -> u8 {
|
||||
0 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
/// Offset to `func_env`.
|
||||
pub fn offset_func_env() -> u8 {
|
||||
1 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
/// Size of a `FuncCtx`.
|
||||
pub fn size() -> u8 {
|
||||
mem::size_of::<Self>() as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// An imported function is a function pointer associated to a
|
||||
/// function context.
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct ImportedFunc {
|
||||
/// Const pointer to `Func`.
|
||||
pub func: *const Func,
|
||||
/// Mutable pointer to `Ctx`.
|
||||
pub vmctx: *mut Ctx,
|
||||
pub(crate) func: *const Func,
|
||||
|
||||
/// Mutable non-null pointer to `FuncCtx`.
|
||||
pub(crate) func_ctx: NonNull<FuncCtx>,
|
||||
}
|
||||
|
||||
// manually implemented because ImportedFunc contains raw pointers directly; `Func` is marked Send (But `Ctx` actually isn't! (TODO: review this, shouldn't `Ctx` be Send?))
|
||||
// Manually implemented because ImportedFunc contains raw pointers
|
||||
// directly; `Func` is marked Send (But `Ctx` actually isn't! (TODO:
|
||||
// review this, shouldn't `Ctx` be Send?))
|
||||
unsafe impl Send for ImportedFunc {}
|
||||
|
||||
impl ImportedFunc {
|
||||
@ -550,8 +591,8 @@ impl ImportedFunc {
|
||||
0 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
/// Offset to vmctx.
|
||||
pub fn offset_vmctx() -> u8 {
|
||||
/// Offset to func_ctx.
|
||||
pub fn offset_func_ctx() -> u8 {
|
||||
1 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
@ -709,7 +750,9 @@ impl Anyfunc {
|
||||
|
||||
#[cfg(test)]
|
||||
mod vm_offset_tests {
|
||||
use super::{Anyfunc, Ctx, ImportedFunc, InternalCtx, LocalGlobal, LocalMemory, LocalTable};
|
||||
use super::{
|
||||
Anyfunc, Ctx, FuncCtx, ImportedFunc, InternalCtx, LocalGlobal, LocalMemory, LocalTable,
|
||||
};
|
||||
|
||||
// Inspired by https://internals.rust-lang.org/t/discussion-on-offset-of/7440/2.
|
||||
macro_rules! offset_of {
|
||||
@ -844,6 +887,13 @@ mod vm_offset_tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn func_ctx() {
|
||||
assert_eq!(FuncCtx::offset_vmctx() as usize, 0,);
|
||||
|
||||
assert_eq!(FuncCtx::offset_func_env() as usize, 8,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imported_func() {
|
||||
assert_eq!(
|
||||
@ -852,8 +902,8 @@ mod vm_offset_tests {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ImportedFunc::offset_vmctx() as usize,
|
||||
offset_of!(ImportedFunc, vmctx),
|
||||
ImportedFunc::offset_func_ctx() as usize,
|
||||
offset_of!(ImportedFunc, func_ctx),
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user