mirror of
https://github.com/fluencelabs/marine.git
synced 2025-03-14 21:40:49 +00:00
getting rid of wasmer's Type, Value and function-related types
This commit is contained in:
parent
d9f9288e1e
commit
7fdb10ba5f
@ -14,4 +14,6 @@ wasmer-it = { package = "wasmer-interface-types-fl", version = "0.21.1" }
|
|||||||
wasmer-runtime = { package = "wasmer-runtime-fl", version = "=0.17.1" }
|
wasmer-runtime = { package = "wasmer-runtime-fl", version = "=0.17.1" }
|
||||||
# dynamicfunc-fat-closures allows using state inside DynamicFunc
|
# dynamicfunc-fat-closures allows using state inside DynamicFunc
|
||||||
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1", features = ["dynamicfunc-fat-closures"] }
|
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1", features = ["dynamicfunc-fat-closures"] }
|
||||||
wasmer-wasi = { package = "wasmer-wasi-fl", version = "0.17.1" }
|
wasmer-wasi = { package = "wasmer-wasi-fl", version = "0.17.1" }
|
||||||
|
|
||||||
|
tuple_list = "0.1.0"
|
@ -1,13 +1,93 @@
|
|||||||
//pub mod errors;
|
//pub mod errors;
|
||||||
//pub mod it_memory_traits;
|
//pub mod it_memory_traits;
|
||||||
|
//pub mod wasm_type_list;
|
||||||
|
|
||||||
|
//pub use wasm_type_list::WasmTypeList;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt::Display;
|
||||||
//use std::fmt::Display;
|
//use std::fmt::Display;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wasmer_core::types::FuncSig;
|
|
||||||
use it_memory_traits::{SequentialMemoryView};
|
use it_memory_traits::{SequentialMemoryView};
|
||||||
|
//use wasmer_it::IValue;
|
||||||
|
|
||||||
pub struct Value {}
|
//use wasmer_core::types::FuncSig;
|
||||||
|
use wasmer_core::error::CallResult;
|
||||||
|
use wasmer_core::typed_func::WasmTypeList;
|
||||||
|
use wasmer_core::types::WasmExternType;
|
||||||
|
//use wasmer_core::typed_func::WasmTypeList;
|
||||||
|
//use wasmer_core::types::FuncSig as Wasme;
|
||||||
|
//pub use tuple_list::Tuple;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum WValue {
|
||||||
|
/// The `i32` type.
|
||||||
|
I32(i32),
|
||||||
|
/// The `i64` type.
|
||||||
|
I64(i64),
|
||||||
|
/// The `f32` type.
|
||||||
|
F32(f32),
|
||||||
|
/// The `f64` type.
|
||||||
|
F64(f64),
|
||||||
|
// /// The `v128` type.
|
||||||
|
//V128(u128),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for WValue {
|
||||||
|
fn from(value: i32) -> Self {
|
||||||
|
WValue::I32(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for WValue {
|
||||||
|
fn from(value: i64) -> Self {
|
||||||
|
WValue::I64(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f32> for WValue {
|
||||||
|
fn from(value: f32) -> Self {
|
||||||
|
WValue::F32(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f64> for WValue {
|
||||||
|
fn from(value: f64) -> Self {
|
||||||
|
WValue::F64(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum WType {
|
||||||
|
/// The `i32` type.
|
||||||
|
I32,
|
||||||
|
/// The `i64` type.
|
||||||
|
I64,
|
||||||
|
/// The `f32` type.
|
||||||
|
F32,
|
||||||
|
/// The `f64` type.
|
||||||
|
F64,
|
||||||
|
// /// The `v128` type.
|
||||||
|
// V128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WValue {
|
||||||
|
pub fn to_u128(&self) -> u128 {
|
||||||
|
match *self {
|
||||||
|
Self::I32(x) => x as u128,
|
||||||
|
Self::I64(x) => x as u128,
|
||||||
|
Self::F32(x) => f32::to_bits(x) as u128,
|
||||||
|
Self::F64(x) => f64::to_bits(x) as u128,
|
||||||
|
//Self::V128(x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for WType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum WasmBackendError {
|
pub enum WasmBackendError {
|
||||||
@ -31,7 +111,8 @@ pub trait WasmBackend: Clone + 'static {
|
|||||||
type I: Instance<Self>;
|
type I: Instance<Self>;
|
||||||
type Wasi: WasiImplementation<Self>;
|
type Wasi: WasiImplementation<Self>;
|
||||||
type Namespace: Namespace<Self>;
|
type Namespace: Namespace<Self>;
|
||||||
type ExportContext: ExportContext<Self>;
|
type ExportContext: for<'c> ExportContext<'c, Self>;
|
||||||
|
type ExportedDynFunc: ExportedDynFunc<Self>;
|
||||||
|
|
||||||
fn compile(wasm: &[u8]) -> WasmBackendResult<Self::M>;
|
fn compile(wasm: &[u8]) -> WasmBackendResult<Self::M>;
|
||||||
}
|
}
|
||||||
@ -61,15 +142,17 @@ pub trait Instance<WB: WasmBackend> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Exports<WB: WasmBackend> {
|
pub trait Exports<WB: WasmBackend> {
|
||||||
fn get<'a, T: wasmer_core::export::Exportable<'a>>(
|
fn get_func_no_args_no_rets<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> wasmer_core::error::ResolveResult<T>;
|
) -> wasmer_core::error::ResolveResult<
|
||||||
|
Box<dyn Fn() -> wasmer_core::error::RuntimeResult<()> + 'a>,
|
||||||
|
>;
|
||||||
|
|
||||||
fn get_func_no_args<'a, Rets: wasmer_core::typed_func::WasmTypeList + 'a>(
|
fn get_dyn_func<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> wasmer_core::error::ResolveResult<Box<dyn Fn() -> wasmer_core::error::RuntimeResult<Rets> + 'a>>;
|
) -> wasmer_core::error::ResolveResult<<WB as WasmBackend>::ExportedDynFunc>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Export<M: MemoryExport, F: FunctionExport> {
|
pub enum Export<M: MemoryExport, F: FunctionExport> {
|
||||||
@ -125,13 +208,9 @@ pub trait Memory<WB: WasmBackend> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait DynamicFunc<'a, WB: WasmBackend> {
|
pub trait DynamicFunc<'a, WB: WasmBackend> {
|
||||||
fn new<'c, F>(sig: std::sync::Arc<FuncSig>, func: F) -> Self
|
fn new<'c, F>(sig: FuncSig, func: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(
|
F: Fn(&mut <WB as WasmBackend>::ExportContext, &[WValue]) -> Vec<WValue> + 'static;
|
||||||
&mut <WB as WasmBackend>::ExportContext,
|
|
||||||
&[wasmer_core::types::Value],
|
|
||||||
) -> Vec<wasmer_core::types::Value>
|
|
||||||
+ 'static;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Namespace<WB: WasmBackend>: LikeNamespace<WB> {
|
pub trait Namespace<WB: WasmBackend>: LikeNamespace<WB> {
|
||||||
@ -142,14 +221,46 @@ pub trait Namespace<WB: WasmBackend>: LikeNamespace<WB> {
|
|||||||
|
|
||||||
pub trait LikeNamespace<WB: WasmBackend> {}
|
pub trait LikeNamespace<WB: WasmBackend> {}
|
||||||
|
|
||||||
pub trait ExportContext<WB: WasmBackend> {
|
pub trait ExportContext<'c, WB: WasmBackend> {
|
||||||
fn memory(&self, memory_index: u32) -> <WB as WasmBackend>::WITMemory;
|
fn memory(&self, memory_index: u32) -> <WB as WasmBackend>::WITMemory;
|
||||||
|
|
||||||
unsafe fn get_export_func_by_name<'a, Args, Rets>(
|
unsafe fn get_export_func_by_name<Args, Rets>(
|
||||||
&mut self,
|
&'c mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Result<wasmer_runtime::Func<'a, Args, Rets>, wasmer_runtime::error::ResolveError>
|
) -> Result<Box<dyn FnMut(Args) -> Result<Rets, wasmer_runtime::error::RuntimeError> + 'c>, wasmer_runtime::error::ResolveError>
|
||||||
where
|
where
|
||||||
Args: wasmer_core::typed_func::WasmTypeList,
|
Args: WasmTypeList,
|
||||||
Rets: wasmer_core::typed_func::WasmTypeList;
|
Rets: WasmTypeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ExportedDynFunc<WB: WasmBackend> {
|
||||||
|
fn signature(&self) -> &FuncSig;
|
||||||
|
|
||||||
|
fn call(&self, args: &[WValue]) -> CallResult<Vec<WValue>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FuncSig {
|
||||||
|
params: Cow<'static, [WType]>,
|
||||||
|
returns: Cow<'static, [WType]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FuncSig {
|
||||||
|
pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
|
||||||
|
where
|
||||||
|
Params: Into<Cow<'static, [WType]>>,
|
||||||
|
Returns: Into<Cow<'static, [WType]>>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
params: params.into(),
|
||||||
|
returns: returns.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn params(&self) -> impl Iterator<Item = &WType> {
|
||||||
|
self.params.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn returns(&self) -> impl Iterator<Item = &WType> {
|
||||||
|
self.returns.iter()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
272
crates/wasm-backend-traits/src/wasm_type_list.rs
Normal file
272
crates/wasm-backend-traits/src/wasm_type_list.rs
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
//use wasmer_core::types::NativeWasmType;
|
||||||
|
use crate::WType;
|
||||||
|
use crate::WValue;
|
||||||
|
|
||||||
|
|
||||||
|
/// Represents a native wasm type.
|
||||||
|
pub unsafe trait NativeWasmType: Copy + Into<WValue>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
/// Type for this `NativeWasmType`.
|
||||||
|
const TYPE: WType;
|
||||||
|
|
||||||
|
/// Convert from u64 bites to self.
|
||||||
|
fn from_binary(bits: u64) -> Self;
|
||||||
|
|
||||||
|
/// Convert self to u64 binary representation.
|
||||||
|
fn to_binary(self) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl NativeWasmType for i32 {
|
||||||
|
const TYPE: WType = WType::I32;
|
||||||
|
|
||||||
|
fn from_binary(bits: u64) -> Self {
|
||||||
|
bits as _
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_binary(self) -> u64 {
|
||||||
|
self as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl NativeWasmType for i64 {
|
||||||
|
const TYPE: WType = WType::I64;
|
||||||
|
|
||||||
|
fn from_binary(bits: u64) -> Self {
|
||||||
|
bits as _
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_binary(self) -> u64 {
|
||||||
|
self as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl NativeWasmType for f32 {
|
||||||
|
const TYPE: WType = WType::F32;
|
||||||
|
|
||||||
|
fn from_binary(bits: u64) -> Self {
|
||||||
|
f32::from_bits(bits as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_binary(self) -> u64 {
|
||||||
|
self.to_bits() as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl NativeWasmType for f64 {
|
||||||
|
const TYPE: WType = WType::F64;
|
||||||
|
|
||||||
|
fn from_binary(bits: u64) -> Self {
|
||||||
|
f64::from_bits(bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_binary(self) -> u64 {
|
||||||
|
self.to_bits()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub unsafe trait WasmExternType: Copy
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
/// Native wasm type for this `WasmExternType`.
|
||||||
|
type Native: NativeWasmType;
|
||||||
|
|
||||||
|
/// Convert from given `Native` type to self.
|
||||||
|
fn from_native(native: Self::Native) -> Self;
|
||||||
|
|
||||||
|
/// Convert self to `Native` type.
|
||||||
|
fn to_native(self) -> Self::Native;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a list of WebAssembly values.
|
||||||
|
pub trait WasmTypeList {
|
||||||
|
/// CStruct type.
|
||||||
|
type CStruct;
|
||||||
|
|
||||||
|
/// Array of return values.
|
||||||
|
type RetArray: AsMut<[u64]>;
|
||||||
|
|
||||||
|
const NTypes: usize;
|
||||||
|
const Types: [WType];
|
||||||
|
|
||||||
|
/// Construct `Self` based on an array of returned values.
|
||||||
|
fn from_ret_array(array: Self::RetArray) -> Self;
|
||||||
|
|
||||||
|
/// Generates an empty array that will hold the returned values of
|
||||||
|
/// the WebAssembly function.
|
||||||
|
fn empty_ret_array() -> Self::RetArray;
|
||||||
|
|
||||||
|
/// Transforms C values into Rust values.
|
||||||
|
fn from_c_struct(c_struct: Self::CStruct) -> Self;
|
||||||
|
|
||||||
|
/// Transforms Rust values into C values.
|
||||||
|
fn into_c_struct(self) -> Self::CStruct;
|
||||||
|
|
||||||
|
/// Get types of the current values.
|
||||||
|
fn types() -> &'static [WType];
|
||||||
|
|
||||||
|
/*
|
||||||
|
/// This method is used to distribute the values onto a function,
|
||||||
|
/// e.g. `(1, 2).call(func, …)`. This form is unlikely to be used
|
||||||
|
/// directly in the code, see the `Func::call` implementation.
|
||||||
|
unsafe fn call<Rets>(
|
||||||
|
self,
|
||||||
|
f: std::ptr::NonNull<wasmer_core::vm::Func>,
|
||||||
|
wasm: wasmer_core::typed_func::Wasm,
|
||||||
|
ctx: *mut wasmer_core::vm::Ctx,
|
||||||
|
) -> Result<Rets, wasmer_core::error::RuntimeError>
|
||||||
|
where
|
||||||
|
Rets: WasmTypeList;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! impl_traits {
|
||||||
|
( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => {
|
||||||
|
/// Struct for typed funcs.
|
||||||
|
#[repr($repr)]
|
||||||
|
pub struct $struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* )
|
||||||
|
where
|
||||||
|
$( $x: WasmExternType ),*;
|
||||||
|
|
||||||
|
#[allow(unused_parens)]
|
||||||
|
impl< $( $x ),* > WasmTypeList for ( $( $x ),* )
|
||||||
|
where
|
||||||
|
$( $x: WasmExternType ),*
|
||||||
|
{
|
||||||
|
type CStruct = $struct_name<$( $x ),*>;
|
||||||
|
|
||||||
|
type RetArray = [u64; count_idents!( $( $x ),* )];
|
||||||
|
|
||||||
|
const NTypes: usize = count_idents!( $( $x ),* );
|
||||||
|
const Types: [WType] = [$( $x::Native::TYPE ),*];
|
||||||
|
|
||||||
|
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let [ $( $x ),* ] = array;
|
||||||
|
|
||||||
|
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty_ret_array() -> Self::RetArray {
|
||||||
|
[0; count_idents!( $( $x ),* )]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_c_struct(c_struct: Self::CStruct) -> Self {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let $struct_name ( $( $x ),* ) = c_struct;
|
||||||
|
|
||||||
|
( $( WasmExternType::from_native($x) ),* )
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_parens, non_snake_case)]
|
||||||
|
fn into_c_struct(self) -> Self::CStruct {
|
||||||
|
let ( $( $x ),* ) = self;
|
||||||
|
|
||||||
|
$struct_name ( $( WasmExternType::to_native($x) ),* )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn types() -> &'static [WType] {
|
||||||
|
&Self::Types
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[allow(unused_parens, non_snake_case)]
|
||||||
|
unsafe fn call<Rets>(
|
||||||
|
self,
|
||||||
|
f: NonNull<vm::Func>,
|
||||||
|
wasm: Wasm,
|
||||||
|
ctx: *mut vm::Ctx,
|
||||||
|
) -> Result<Rets, RuntimeError>
|
||||||
|
where
|
||||||
|
Rets: WasmTypeList
|
||||||
|
{
|
||||||
|
let ( $( $x ),* ) = self;
|
||||||
|
let args = [ $( $x.to_native().to_binary()),* ];
|
||||||
|
let mut rets = Rets::empty_ret_array();
|
||||||
|
let mut error_out = None;
|
||||||
|
|
||||||
|
if (wasm.invoke)(
|
||||||
|
wasm.trampoline,
|
||||||
|
ctx,
|
||||||
|
f,
|
||||||
|
args.as_ptr(),
|
||||||
|
rets.as_mut().as_mut_ptr(),
|
||||||
|
&mut error_out,
|
||||||
|
wasm.invoke_env
|
||||||
|
) {
|
||||||
|
Ok(Rets::from_ret_array(rets))
|
||||||
|
} else {
|
||||||
|
Err(error_out.map_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError), Into::into))
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! count_idents {
|
||||||
|
( $($idents:ident),* ) => {{
|
||||||
|
#[allow(dead_code, non_camel_case_types)]
|
||||||
|
enum Idents { $($idents,)* __CountIdentsLast }
|
||||||
|
const COUNT: usize = Idents::__CountIdentsLast as usize;
|
||||||
|
COUNT
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! wasm_extern_type {
|
||||||
|
($type:ty => $native_type:ty) => {
|
||||||
|
unsafe impl WasmExternType for $type {
|
||||||
|
type Native = $native_type;
|
||||||
|
|
||||||
|
fn from_native(native: Self::Native) -> Self {
|
||||||
|
native as _
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_native(self) -> Self::Native {
|
||||||
|
self as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_extern_type!(i8 => i32);
|
||||||
|
wasm_extern_type!(u8 => i32);
|
||||||
|
wasm_extern_type!(i16 => i32);
|
||||||
|
wasm_extern_type!(u16 => i32);
|
||||||
|
wasm_extern_type!(i32 => i32);
|
||||||
|
wasm_extern_type!(u32 => i32);
|
||||||
|
wasm_extern_type!(i64 => i64);
|
||||||
|
wasm_extern_type!(u64 => i64);
|
||||||
|
wasm_extern_type!(f32 => f32);
|
||||||
|
wasm_extern_type!(f64 => f64);
|
||||||
|
|
||||||
|
impl_traits!([C] S0,);
|
||||||
|
impl_traits!([transparent] S1, A);
|
||||||
|
impl_traits!([C] S2, A, B);
|
||||||
|
impl_traits!([C] S3, A, B, C);
|
||||||
|
impl_traits!([C] S4, A, B, C, D);
|
||||||
|
impl_traits!([C] S5, A, B, C, D, E);
|
||||||
|
impl_traits!([C] S6, A, B, C, D, E, F);
|
||||||
|
impl_traits!([C] S7, A, B, C, D, E, F, G);
|
||||||
|
impl_traits!([C] S8, A, B, C, D, E, F, G, H);
|
||||||
|
impl_traits!([C] S9, A, B, C, D, E, F, G, H, I);
|
||||||
|
impl_traits!([C] S10, A, B, C, D, E, F, G, H, I, J);
|
||||||
|
impl_traits!([C] S11, A, B, C, D, E, F, G, H, I, J, K);
|
||||||
|
impl_traits!([C] S12, A, B, C, D, E, F, G, H, I, J, K, L);
|
||||||
|
impl_traits!([C] S13, A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||||
|
impl_traits!([C] S14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||||
|
impl_traits!([C] S15, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||||
|
impl_traits!([C] S16, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||||
|
impl_traits!([C] S17, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
|
||||||
|
impl_traits!([C] S18, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
|
||||||
|
impl_traits!([C] S19, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
|
||||||
|
impl_traits!([C] S20, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
|
||||||
|
impl_traits!([C] S21, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
|
||||||
|
impl_traits!([C] S22, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
|
||||||
|
impl_traits!([C] S23, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);
|
||||||
|
impl_traits!([C] S24, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);
|
||||||
|
impl_traits!([C] S25, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y);
|
||||||
|
impl_traits!([C] S26, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
|
||||||
|
|
@ -1,7 +1,6 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
//use std::marker::PhantomData;
|
//use std::marker::PhantomData;
|
||||||
use marine_wasm_backend_traits::{
|
use marine_wasm_backend_traits::{DynamicFunc, Export, ExportContext, ExportedDynFunc, LikeNamespace, Memory, Namespace, WValue, WasmBackend};
|
||||||
DynamicFunc, Export, ExportContext, LikeNamespace, Memory, Namespace, Value, WasmBackend,
|
|
||||||
};
|
|
||||||
use marine_wasm_backend_traits::WasmBackendResult;
|
use marine_wasm_backend_traits::WasmBackendResult;
|
||||||
use marine_wasm_backend_traits::WasmBackendError;
|
use marine_wasm_backend_traits::WasmBackendError;
|
||||||
use marine_wasm_backend_traits::Module;
|
use marine_wasm_backend_traits::Module;
|
||||||
@ -11,27 +10,40 @@ use marine_wasm_backend_traits::FunctionExport;
|
|||||||
use marine_wasm_backend_traits::MemoryExport;
|
use marine_wasm_backend_traits::MemoryExport;
|
||||||
use marine_wasm_backend_traits::Exports;
|
use marine_wasm_backend_traits::Exports;
|
||||||
use marine_wasm_backend_traits::WasiImplementation;
|
use marine_wasm_backend_traits::WasiImplementation;
|
||||||
|
use marine_wasm_backend_traits::FuncSig;
|
||||||
|
use marine_wasm_backend_traits::Tuple;
|
||||||
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::ptr::NonNull;
|
||||||
use std::slice::Windows;
|
use std::slice::Windows;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wasmer_core::backend::SigRegistry;
|
use wasmer_core::backend::SigRegistry;
|
||||||
use wasmer_core::error::{ResolveError, ResolveResult};
|
use wasmer_core::error::{CallResult, ResolveError, ResolveResult, RuntimeError};
|
||||||
use wasmer_core::fault::raw::longjmp;
|
use wasmer_core::fault::raw::longjmp;
|
||||||
use wasmer_core::Func;
|
use wasmer_core::{DynFunc, Func};
|
||||||
use wasmer_core::module::ExportIndex;
|
use wasmer_core::module::ExportIndex;
|
||||||
use wasmer_core::typed_func::WasmTypeList;
|
use wasmer_core::prelude::Type;
|
||||||
//use wasmer_core::prelude::vm::Ctx;
|
use wasmer_core::prelude::vm::Ctx;
|
||||||
use wasmer_core::types::{FuncSig, LocalOrImport, WasmExternType};
|
use wasmer_core::typed_func::Wasm;
|
||||||
|
use wasmer_core::types::{LocalOrImport, WasmExternType};
|
||||||
|
use wasmer_core::types::FuncSig as WasmerFuncSig;
|
||||||
|
use wasmer_core::typed_func::WasmTypeList as WasmerWasmTypeList;
|
||||||
use wasmer_wasi::state::WasiState;
|
use wasmer_wasi::state::WasiState;
|
||||||
|
use wasmer_it::IValue;
|
||||||
|
|
||||||
mod memory_access;
|
mod memory_access;
|
||||||
mod memory;
|
mod memory;
|
||||||
|
mod type_converters;
|
||||||
|
mod wasm_type_list;
|
||||||
|
|
||||||
//use wasmer_it::interpreter::wasm::structures::{SequentialMemoryView, SequentialReader, SequentialWriter};
|
//use wasmer_it::interpreter::wasm::structures::{SequentialMemoryView, SequentialReader, SequentialWriter};
|
||||||
use crate::memory::WITMemoryView;
|
use crate::memory::WITMemoryView;
|
||||||
use crate::memory::WITMemory;
|
use crate::memory::WITMemory;
|
||||||
use crate::memory_access::{WasmerSequentialReader, WasmerSequentialWriter};
|
use crate::memory_access::{WasmerSequentialReader, WasmerSequentialWriter};
|
||||||
|
use crate::type_converters::{general_wval_to_wval, wval_to_general_wval};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WasmerBackend /*<'a>*/ {
|
pub struct WasmerBackend /*<'a>*/ {
|
||||||
@ -53,6 +65,7 @@ impl WasmBackend for WasmerBackend /*<'b>*/ {
|
|||||||
type DynamicFunc = WasmerDynamicFunc;
|
type DynamicFunc = WasmerDynamicFunc;
|
||||||
type Namespace = WasmerNamespace;
|
type Namespace = WasmerNamespace;
|
||||||
type ExportContext = WasmerExportContext<'static>;
|
type ExportContext = WasmerExportContext<'static>;
|
||||||
|
type ExportedDynFunc = WasmerExportedDynFunc<'static>;
|
||||||
|
|
||||||
fn compile(wasm: &[u8]) -> WasmBackendResult<WasmerModule> {
|
fn compile(wasm: &[u8]) -> WasmBackendResult<WasmerModule> {
|
||||||
wasmer_runtime::compile(wasm)
|
wasmer_runtime::compile(wasm)
|
||||||
@ -231,30 +244,31 @@ impl WasiImplementation<WasmerBackend> for WasmerWasiImplementation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Exports<WasmerBackend> for WasmerInstance {
|
impl Exports<WasmerBackend> for WasmerInstance {
|
||||||
fn get<'a, T: wasmer_core::export::Exportable<'a>>(
|
fn get_func_no_args_no_rets<'a>(
|
||||||
&'a self,
|
|
||||||
name: &str,
|
|
||||||
) -> wasmer_core::error::ResolveResult<T> {
|
|
||||||
self.instance.exports.get(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_func_no_args<'a, Rets: WasmTypeList + 'a>(
|
|
||||||
&'a self,
|
&'a self,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> wasmer_core::error::ResolveResult<
|
) -> wasmer_core::error::ResolveResult<
|
||||||
Box<dyn Fn() -> wasmer_core::error::RuntimeResult<Rets> + 'a>,
|
Box<dyn Fn() -> wasmer_core::error::RuntimeResult<()> + 'a>,
|
||||||
> {
|
> {
|
||||||
|
self.instance.exports.get::<Func<'a>>(name).map(|func| {
|
||||||
|
let func: Box<dyn Fn() -> wasmer_core::error::RuntimeResult<()> + 'a> =
|
||||||
|
Box::new(move || -> wasmer_core::error::RuntimeResult<()> { func.call() });
|
||||||
|
func
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_dyn_func<'a>(
|
||||||
|
&'a self,
|
||||||
|
name: &str,
|
||||||
|
) -> ResolveResult<<WasmerBackend as WasmBackend>::ExportedDynFunc> {
|
||||||
self.instance
|
self.instance
|
||||||
.exports
|
.exports
|
||||||
.get::<Func<'a, (), Rets>>(name)
|
.get::<DynFunc<'_>>(name)
|
||||||
.map(|func| {
|
.map(|func| unsafe {
|
||||||
let func: Box<dyn Fn() -> wasmer_core::error::RuntimeResult<Rets> + 'a> =
|
WasmerExportedDynFunc {
|
||||||
Box::new(
|
sig: FuncSigConverter(func.signature()).into(),
|
||||||
move || -> wasmer_core::error::RuntimeResult<Rets> {
|
func: std::mem::transmute::<DynFunc<'_>, DynFunc<'static>>(func),
|
||||||
func.call()
|
}
|
||||||
},
|
|
||||||
);
|
|
||||||
func
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,16 +334,12 @@ pub struct WasmerDynamicFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DynamicFunc<'a, WasmerBackend> for WasmerDynamicFunc {
|
impl<'a> DynamicFunc<'a, WasmerBackend> for WasmerDynamicFunc {
|
||||||
fn new<F>(sig: Arc<FuncSig>, func: F) -> Self
|
fn new<F>(sig: FuncSig, func: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(
|
F: Fn(&mut WasmerExportContext<'static>, &[WValue]) -> Vec<WValue> + 'static,
|
||||||
&mut WasmerExportContext<'static>,
|
|
||||||
&[wasmer_core::prelude::Value],
|
|
||||||
) -> Vec<wasmer_core::prelude::Value>
|
|
||||||
+ 'static,
|
|
||||||
{
|
{
|
||||||
let func = wasmer_core::typed_func::DynamicFunc::new(
|
let func = wasmer_core::typed_func::DynamicFunc::new(
|
||||||
sig,
|
std::sync::Arc::new(FuncSigConverter(&sig).into()),
|
||||||
move |ctx: &mut wasmer_core::vm::Ctx, args: &[wasmer_core::prelude::Value]| unsafe {
|
move |ctx: &mut wasmer_core::vm::Ctx, args: &[wasmer_core::prelude::Value]| unsafe {
|
||||||
let mut ctx = WasmerExportContext {
|
let mut ctx = WasmerExportContext {
|
||||||
ctx: std::mem::transmute::<
|
ctx: std::mem::transmute::<
|
||||||
@ -338,7 +348,11 @@ impl<'a> DynamicFunc<'a, WasmerBackend> for WasmerDynamicFunc {
|
|||||||
>(ctx),
|
>(ctx),
|
||||||
};
|
};
|
||||||
|
|
||||||
func(&mut ctx, args)
|
let args = args.iter().map(wval_to_general_wval).collect::<Vec<_>>();
|
||||||
|
func(&mut ctx, &args)
|
||||||
|
.iter()
|
||||||
|
.map(general_wval_to_wval)
|
||||||
|
.collect()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -374,18 +388,18 @@ pub struct WasmerExportContext<'c> {
|
|||||||
ctx: &'c mut wasmer_core::vm::Ctx,
|
ctx: &'c mut wasmer_core::vm::Ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'c> ExportContext<WasmerBackend> for WasmerExportContext<'c> {
|
impl<'c> ExportContext<'c, WasmerBackend> for WasmerExportContext<'static> {
|
||||||
fn memory(&self, memory_index: u32) -> <WasmerBackend as WasmBackend>::WITMemory {
|
fn memory(&self, memory_index: u32) -> <WasmerBackend as WasmBackend>::WITMemory {
|
||||||
WITMemory(self.ctx.memory(memory_index).clone())
|
WITMemory(self.ctx.memory(memory_index).clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_export_func_by_name<'a, Args, Rets>(
|
unsafe fn get_export_func_by_name<Args, Rets>(
|
||||||
&mut self,
|
&'c mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Result<Func<'a, Args, Rets>, ResolveError>
|
) -> Result<Box<dyn FnMut(Args) -> Result<Rets, RuntimeError> + 'c>, ResolveError>
|
||||||
where
|
where
|
||||||
Args: wasmer_core::typed_func::WasmTypeList,
|
Args: Tuple,
|
||||||
Rets: wasmer_core::typed_func::WasmTypeList,
|
Rets: Tuple,
|
||||||
{
|
{
|
||||||
let ctx = &mut self.ctx;
|
let ctx = &mut self.ctx;
|
||||||
let module_inner = &(*ctx.module);
|
let module_inner = &(*ctx.module);
|
||||||
@ -417,12 +431,14 @@ impl<'c> ExportContext<WasmerBackend> for WasmerExportContext<'c> {
|
|||||||
let export_func_signature = &module_inner.info.signatures[export_func_signature_idx];
|
let export_func_signature = &module_inner.info.signatures[export_func_signature_idx];
|
||||||
let export_func_signature_ref = SigRegistry.lookup_signature_ref(export_func_signature);
|
let export_func_signature_ref = SigRegistry.lookup_signature_ref(export_func_signature);
|
||||||
|
|
||||||
if export_func_signature_ref.params() != Args::types()
|
let arg_types = <Args::CStruct as wasmer_core::typed_func::WasmTypeList>::types();
|
||||||
|| export_func_signature_ref.returns() != Rets::types()
|
let ret_types = <Rets::CStruct as wasmer_core::typed_func::WasmTypeList>::types();
|
||||||
|
if export_func_signature_ref.params() != arg_types//Args::types()
|
||||||
|
|| export_func_signature_ref.returns() != ret_types//Rets::types()
|
||||||
{
|
{
|
||||||
return Err(ResolveError::Signature {
|
return Err(ResolveError::Signature {
|
||||||
expected: (*export_func_signature).clone(),
|
expected: (*export_func_signature).clone(),
|
||||||
found: Args::types().to_vec(),
|
found: /*Helper::<Args>::types()*/ret_types.to_vec(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,9 +459,78 @@ impl<'c> ExportContext<WasmerBackend> for WasmerExportContext<'c> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let typed_func: Func<'_, Args, Rets, wasmer_core::typed_func::Wasm> =
|
let typed_func: Func<'_, Args, Rets, wasmer_core::typed_func::Wasm> =
|
||||||
Func::from_raw_parts(func_wasm_inner, export_func_ptr, None, (*ctx) as _);
|
Func::from_raw_parts(func_wasm_inner, export_func_ptr, None, (*ctx) as _);
|
||||||
|
|
||||||
Ok(typed_func)
|
let result = Box::new(
|
||||||
|
move |args: Args| -> Result<Rets, RuntimeError> {
|
||||||
|
args.into_c_struct()
|
||||||
|
.call::<Rets::CStruct>(export_func_ptr, func_wasm_inner, *ctx)
|
||||||
|
.map(|rets: Rets| rets.0)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WasmerExportedDynFunc<'a> {
|
||||||
|
func: DynFunc<'a>,
|
||||||
|
sig: FuncSig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ExportedDynFunc<WasmerBackend> for WasmerExportedDynFunc<'a> {
|
||||||
|
fn signature(&self) -> &FuncSig {
|
||||||
|
&self.sig
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, args: &[WValue]) -> CallResult<Vec<WValue>> {
|
||||||
|
use crate::type_converters::general_wval_to_wval;
|
||||||
|
use crate::type_converters::wval_to_general_wval;
|
||||||
|
self.func
|
||||||
|
.call(
|
||||||
|
&args
|
||||||
|
.iter()
|
||||||
|
.map(general_wval_to_wval)
|
||||||
|
.collect::<Vec<wasmer_runtime::Value>>(),
|
||||||
|
)
|
||||||
|
.map(|rets| rets.iter().map(wval_to_general_wval).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FuncSigConverter<'a, T>(&'a T);
|
||||||
|
|
||||||
|
impl<'a> From<FuncSigConverter<'a, FuncSig>> for WasmerFuncSig {
|
||||||
|
fn from(sig: FuncSigConverter<'a, FuncSig>) -> Self {
|
||||||
|
let params = sig
|
||||||
|
.0
|
||||||
|
.params()
|
||||||
|
.map(type_converters::general_wtype_to_wtype)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let returns = sig
|
||||||
|
.0
|
||||||
|
.returns()
|
||||||
|
.map(type_converters::general_wtype_to_wtype)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Self::new(params, returns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<FuncSigConverter<'a, WasmerFuncSig>> for FuncSig {
|
||||||
|
fn from(sig: FuncSigConverter<'a, WasmerFuncSig>) -> Self {
|
||||||
|
let params = sig
|
||||||
|
.0
|
||||||
|
.params()
|
||||||
|
.iter()
|
||||||
|
.map(type_converters::wtype_to_general_wtype)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let returns = sig
|
||||||
|
.0
|
||||||
|
.returns()
|
||||||
|
.iter()
|
||||||
|
.map(type_converters::wtype_to_general_wtype)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Self::new(params, returns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
65
crates/wasmer-backend/src/type_converters.rs
Normal file
65
crates/wasmer-backend/src/type_converters.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Contains converters of types and values between Wasmer and wasmer_interface_types.
|
||||||
|
use wasmer_runtime::Value as WValue;
|
||||||
|
use wasmer_runtime::types::Type as WType;
|
||||||
|
|
||||||
|
use marine_wasm_backend_traits::WValue as GeneralWValue;
|
||||||
|
use marine_wasm_backend_traits::WType as GeneralWType;
|
||||||
|
|
||||||
|
pub(super) fn wtype_to_general_wtype(ty: &WType) -> GeneralWType {
|
||||||
|
match ty {
|
||||||
|
WType::I32 => GeneralWType::I32,
|
||||||
|
WType::I64 => GeneralWType::I64,
|
||||||
|
WType::F32 => GeneralWType::F32,
|
||||||
|
WType::F64 => GeneralWType::F64,
|
||||||
|
WType::V128 => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn general_wtype_to_wtype(ty: &GeneralWType) -> WType {
|
||||||
|
match ty {
|
||||||
|
GeneralWType::I32 => WType::I32,
|
||||||
|
GeneralWType::I64 => WType::I64,
|
||||||
|
GeneralWType::F32 => WType::F32,
|
||||||
|
GeneralWType::F64 => WType::F64,
|
||||||
|
ty => {
|
||||||
|
eprintln!("trying to convert {:?}", ty);
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn general_wval_to_wval(value: &GeneralWValue) -> WValue {
|
||||||
|
match value {
|
||||||
|
GeneralWValue::I32(v) => WValue::I32(*v),
|
||||||
|
GeneralWValue::I64(v) => WValue::I64(*v),
|
||||||
|
GeneralWValue::F32(v) => WValue::F32(*v),
|
||||||
|
GeneralWValue::F64(v) => WValue::F64(*v),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn wval_to_general_wval(value: &WValue) -> GeneralWValue {
|
||||||
|
match value {
|
||||||
|
WValue::I32(v) => GeneralWValue::I32(*v),
|
||||||
|
WValue::I64(v) => GeneralWValue::I64(*v),
|
||||||
|
WValue::F32(v) => GeneralWValue::F32(*v),
|
||||||
|
WValue::F64(v) => GeneralWValue::F64(*v),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
448
crates/wasmer-backend/src/wasm_type_list.rs
Normal file
448
crates/wasmer-backend/src/wasm_type_list.rs
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
use std::ptr::NonNull;
|
||||||
|
use wasmer_core::error::RuntimeError;
|
||||||
|
use wasmer_core::typed_func::{Wasm};
|
||||||
|
use wasmer_core::types::{NativeWasmType, Type};
|
||||||
|
use wasmer_core::vm::Ctx;
|
||||||
|
use marine_wasm_backend_traits::WType;
|
||||||
|
use marine_wasm_backend_traits::WValue;
|
||||||
|
use marine_wasm_backend_traits::Tuple;
|
||||||
|
use marine_wasm_backend_traits::WasmTypeList;
|
||||||
|
|
||||||
|
|
||||||
|
struct Helper<T: WasmTypeList> (T);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl<T: WasmTypeList> wasmer_core::typed_func::WasmTypeList for Helper<T> {
|
||||||
|
type CStruct = T::CStruct;
|
||||||
|
type RetArray = T::RetArray;
|
||||||
|
|
||||||
|
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||||
|
Self(T::from_ret_array(array))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty_ret_array() -> Self::RetArray {
|
||||||
|
T::empty_ret_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_c_struct(c_struct: Self::CStruct) -> Self {
|
||||||
|
Self(T::from_c_struct(c_struct))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_c_struct(self) -> Self::CStruct {
|
||||||
|
self.into_c_struct()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn types() -> &'static [Type] {
|
||||||
|
T::types() /// how to convert?
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn call<Rets>(self,
|
||||||
|
f: NonNull<wasmer_core::prelude::vm::Func>,
|
||||||
|
wasm: Wasm,
|
||||||
|
ctx: *mut Ctx
|
||||||
|
) -> Result<Rets, RuntimeError> where Rets: wasmer_core::typed_func::WasmTypeList {
|
||||||
|
// how to implement?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
const fn gen_types(wtypes: &'static [WType]) -> &'static [Type] {
|
||||||
|
let types = [Type::I32; wtypes.len()];
|
||||||
|
&types
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_traits {
|
||||||
|
( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => {
|
||||||
|
/// Struct for typed funcs.
|
||||||
|
#[repr($repr)]
|
||||||
|
pub struct $struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* )
|
||||||
|
where
|
||||||
|
$( $x: WasmExternType ),*;
|
||||||
|
|
||||||
|
#[allow(unused_parens)]
|
||||||
|
impl< $( $x ),* > wasmer_core::typed_func::WasmTypeList for Helper<( $( $x ),* )>
|
||||||
|
where
|
||||||
|
$( $x: WasmExternType ),*
|
||||||
|
{
|
||||||
|
type CStruct = $struct_name<$( $x ),*>;
|
||||||
|
|
||||||
|
type RetArray = [u64; count_idents!( $( $x ),* )];
|
||||||
|
|
||||||
|
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let [ $( $x ),* ] = array;
|
||||||
|
|
||||||
|
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty_ret_array() -> Self::RetArray {
|
||||||
|
[0; count_idents!( $( $x ),* )]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_c_struct(c_struct: Self::CStruct) -> Self {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let $struct_name ( $( $x ),* ) = c_struct;
|
||||||
|
|
||||||
|
( $( WasmExternType::from_native($x) ),* )
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_parens, non_snake_case)]
|
||||||
|
fn into_c_struct(self) -> Self::CStruct {
|
||||||
|
let ( $( $x ),* ) = self;
|
||||||
|
|
||||||
|
$struct_name ( $( WasmExternType::to_native($x) ),* )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn types() -> &'static [Type] {
|
||||||
|
&[$( $x::Native::WTYPE ),*]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[allow(unused_parens, non_snake_case)]
|
||||||
|
unsafe fn call<Rets>(
|
||||||
|
self,
|
||||||
|
f: NonNull<vm::Func>,
|
||||||
|
wasm: Wasm,
|
||||||
|
ctx: *mut vm::Ctx,
|
||||||
|
) -> Result<Rets, RuntimeError>
|
||||||
|
where
|
||||||
|
Rets: WasmTypeList
|
||||||
|
{
|
||||||
|
let ( $( $x ),* ) = self;
|
||||||
|
let args = [ $( $x.to_native().to_binary()),* ];
|
||||||
|
let mut rets = Rets::empty_ret_array();
|
||||||
|
let mut error_out = None;
|
||||||
|
|
||||||
|
if (wasm.invoke)(
|
||||||
|
wasm.trampoline,
|
||||||
|
ctx,
|
||||||
|
f,
|
||||||
|
args.as_ptr(),
|
||||||
|
rets.as_mut().as_mut_ptr(),
|
||||||
|
&mut error_out,
|
||||||
|
wasm.invoke_env
|
||||||
|
) {
|
||||||
|
Ok(Rets::from_ret_array(rets))
|
||||||
|
} else {
|
||||||
|
Err(error_out.map_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError), Into::into))
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
#[allow(unused_parens)]
|
||||||
|
impl< $( $x, )* Rets, Trap, FN > HostFunction<ExplicitVmCtx, ( $( $x ),* ), Rets> for FN
|
||||||
|
where
|
||||||
|
$( $x: WasmExternType, )*
|
||||||
|
Rets: WasmTypeList,
|
||||||
|
Trap: TrapEarly<Rets>,
|
||||||
|
FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap + 'static + Send,
|
||||||
|
{
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
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.
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Get the collection of imported functions.
|
||||||
|
let vm_imported_functions = unsafe { &(*vmctx.import_backing).vm_functions };
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
RuntimeError::User(b as Box<dyn Any + Send>)
|
||||||
|
},
|
||||||
|
// TODO(blocking): this line is wrong!
|
||||||
|
Err(err) => RuntimeError::User(err),
|
||||||
|
};
|
||||||
|
|
||||||
|
// At this point, there is an error that needs to
|
||||||
|
// be trapped.
|
||||||
|
unsafe {
|
||||||
|
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_parens)]
|
||||||
|
impl< $( $x, )* Rets, Trap, FN > HostFunction<ImplicitVmCtx, ( $( $x ),* ), Rets> for FN
|
||||||
|
where
|
||||||
|
$( $x: WasmExternType, )*
|
||||||
|
Rets: WasmTypeList,
|
||||||
|
Trap: TrapEarly<Rets>,
|
||||||
|
FN: Fn($( $x, )*) -> Trap + 'static + Send,
|
||||||
|
{
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
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.
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Get the collection of imported functions.
|
||||||
|
let vm_imported_functions = unsafe { &(*vmctx.import_backing).vm_functions };
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
RuntimeError::User(b as Box<dyn Any + Send>)
|
||||||
|
},
|
||||||
|
// TODO(blocking): this line is wrong!
|
||||||
|
Err(err) => RuntimeError::User(err),
|
||||||
|
};
|
||||||
|
|
||||||
|
// At this point, there is an error that needs to
|
||||||
|
// be trapped.
|
||||||
|
unsafe {
|
||||||
|
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_parens)]
|
||||||
|
impl<'a $( , $x )*, Rets> Func<'a, ( $( $x ),* ), Rets, Wasm>
|
||||||
|
where
|
||||||
|
$( $x: WasmExternType, )*
|
||||||
|
Rets: WasmTypeList,
|
||||||
|
{
|
||||||
|
/// Call the typed func and return results.
|
||||||
|
#[allow(non_snake_case, clippy::too_many_arguments)]
|
||||||
|
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
|
||||||
|
#[allow(unused_parens)]
|
||||||
|
unsafe {
|
||||||
|
<( $( $x ),* ) as WasmTypeList>::call(
|
||||||
|
( $( $x ),* ),
|
||||||
|
self.func,
|
||||||
|
self.inner,
|
||||||
|
self.vmctx
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! count_idents {
|
||||||
|
( $($idents:ident),* ) => {{
|
||||||
|
#[allow(dead_code, non_camel_case_types)]
|
||||||
|
enum Idents { $($idents,)* __CountIdentsLast }
|
||||||
|
const COUNT: usize = Idents::__CountIdentsLast as usize;
|
||||||
|
COUNT
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! wasm_extern_type {
|
||||||
|
($type:ty => $native_type:ty) => {
|
||||||
|
unsafe impl WasmExternType for $type {
|
||||||
|
type Native = $native_type;
|
||||||
|
|
||||||
|
fn from_native(native: Self::Native) -> Self {
|
||||||
|
native as _
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_native(self) -> Self::Native {
|
||||||
|
self as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_extern_type!(i8 => i32);
|
||||||
|
wasm_extern_type!(u8 => i32);
|
||||||
|
wasm_extern_type!(i16 => i32);
|
||||||
|
wasm_extern_type!(u16 => i32);
|
||||||
|
wasm_extern_type!(i32 => i32);
|
||||||
|
wasm_extern_type!(u32 => i32);
|
||||||
|
wasm_extern_type!(i64 => i64);
|
||||||
|
wasm_extern_type!(u64 => i64);
|
||||||
|
wasm_extern_type!(f32 => f32);
|
||||||
|
wasm_extern_type!(f64 => f64);
|
||||||
|
|
||||||
|
impl_traits!([C] S0,);
|
||||||
|
impl_traits!([transparent] S1, A);
|
||||||
|
impl_traits!([C] S2, A, B);
|
||||||
|
impl_traits!([C] S3, A, B, C);
|
||||||
|
impl_traits!([C] S4, A, B, C, D);
|
||||||
|
impl_traits!([C] S5, A, B, C, D, E);
|
||||||
|
impl_traits!([C] S6, A, B, C, D, E, F);
|
||||||
|
impl_traits!([C] S7, A, B, C, D, E, F, G);
|
||||||
|
impl_traits!([C] S8, A, B, C, D, E, F, G, H);
|
||||||
|
impl_traits!([C] S9, A, B, C, D, E, F, G, H, I);
|
||||||
|
impl_traits!([C] S10, A, B, C, D, E, F, G, H, I, J);
|
||||||
|
impl_traits!([C] S11, A, B, C, D, E, F, G, H, I, J, K);
|
||||||
|
impl_traits!([C] S12, A, B, C, D, E, F, G, H, I, J, K, L);
|
||||||
|
impl_traits!([C] S13, A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||||
|
impl_traits!([C] S14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||||
|
impl_traits!([C] S15, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||||
|
impl_traits!([C] S16, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||||
|
impl_traits!([C] S17, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
|
||||||
|
impl_traits!([C] S18, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
|
||||||
|
impl_traits!([C] S19, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
|
||||||
|
impl_traits!([C] S20, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
|
||||||
|
impl_traits!([C] S21, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
|
||||||
|
impl_traits!([C] S22, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
|
||||||
|
impl_traits!([C] S23, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);
|
||||||
|
impl_traits!([C] S24, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);
|
||||||
|
impl_traits!([C] S25, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y);
|
||||||
|
impl_traits!([C] S26, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
|
||||||
|
|
||||||
|
*/
|
@ -31,8 +31,8 @@ use crate::HostImportDescriptor;
|
|||||||
//use wasmer_core::Func;
|
//use wasmer_core::Func;
|
||||||
//use wasmer_core::vm::Ctx;
|
//use wasmer_core::vm::Ctx;
|
||||||
//use wasmer_core::typed_func::DynamicFunc;
|
//use wasmer_core::typed_func::DynamicFunc;
|
||||||
use wasmer_core::types::Value as WValue;
|
//use wasmer_core::types::Value as WValue;
|
||||||
use wasmer_core::types::FuncSig;
|
//use wasmer_core::types::FuncSig;
|
||||||
use it_lilo::lifter::ILifter;
|
use it_lilo::lifter::ILifter;
|
||||||
use it_lilo::lowerer::ILowerer;
|
use it_lilo::lowerer::ILowerer;
|
||||||
use it_memory_traits::Memory as ITMemory;
|
use it_memory_traits::Memory as ITMemory;
|
||||||
@ -40,7 +40,7 @@ use it_memory_traits::Memory as ITMemory;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use marine_wasm_backend_traits::WasmBackend;
|
use marine_wasm_backend_traits::{FuncSig, WasmBackend};
|
||||||
use marine_wasm_backend_traits::DynamicFunc;
|
use marine_wasm_backend_traits::DynamicFunc;
|
||||||
use marine_wasm_backend_traits::ExportContext;
|
use marine_wasm_backend_traits::ExportContext;
|
||||||
|
|
||||||
@ -159,10 +159,7 @@ pub(crate) fn create_host_import_func<WB: WasmBackend>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
<WB as WasmBackend>::DynamicFunc::new(
|
<WB as WasmBackend>::DynamicFunc::new(FuncSig::new(raw_args, raw_output), func)
|
||||||
std::sync::Arc::new(FuncSig::new(raw_args, raw_output)),
|
|
||||||
func,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_error_handler(err: &HostImportError) -> Option<crate::IValue> {
|
fn default_error_handler(err: &HostImportError) -> Option<crate::IValue> {
|
||||||
|
@ -26,8 +26,11 @@ use wasmer_core::Func;
|
|||||||
pub use errors::HostImportError;
|
pub use errors::HostImportError;
|
||||||
pub(crate) use imports::create_host_import_func;
|
pub(crate) use imports::create_host_import_func;
|
||||||
|
|
||||||
pub(self) use wasmer_core::types::Value as WValue;
|
//pub(self) use wasmer_core::types::Value as WValue;
|
||||||
pub(self) use wasmer_core::types::Type as WType;
|
//pub(self) use wasmer_core::types::Type as WType;
|
||||||
|
|
||||||
|
pub(self) use marine_wasm_backend_traits::WValue;
|
||||||
|
pub(self) use marine_wasm_backend_traits::WType;
|
||||||
|
|
||||||
pub(self) type HostImportResult<T> = std::result::Result<T, HostImportError>;
|
pub(self) type HostImportResult<T> = std::result::Result<T, HostImportError>;
|
||||||
pub(self) type WasmModuleFunc<Args, Rets> = Box<RefCell<Option<Func<'static, Args, Rets>>>>;
|
pub(self) type WasmModuleFunc<Args, Rets> = Box<RefCell<Option<Func<'static, Args, Rets>>>>;
|
||||||
|
@ -46,6 +46,8 @@ use std::convert::TryInto;
|
|||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
//use wasmer_core::types::FuncSig;
|
||||||
|
use marine_wasm_backend_traits::FuncSig;
|
||||||
|
|
||||||
type ITInterpreter<WB> = Interpreter<
|
type ITInterpreter<WB> = Interpreter<
|
||||||
ITInstance<WB>,
|
ITInstance<WB>,
|
||||||
@ -148,7 +150,7 @@ impl<WB: WasmBackend> MModule<WB> {
|
|||||||
|
|
||||||
// call _start to populate the WASI state of the module
|
// call _start to populate the WASI state of the module
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
if let Ok(start_func) = wasmer_instance.exports().get_func_no_args::<'_, ()>("_start") {
|
if let Ok(start_func) = wasmer_instance.exports().get_func_no_args_no_rets("_start") {
|
||||||
start_func()?;
|
start_func()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,15 +328,12 @@ impl<WB: WasmBackend> MModule<WB> {
|
|||||||
I1: Iterator<Item = &'a IType>,
|
I1: Iterator<Item = &'a IType>,
|
||||||
I2: Iterator<Item = &'b IType>,
|
I2: Iterator<Item = &'b IType>,
|
||||||
{
|
{
|
||||||
use wasmer_core::types::FuncSig;
|
//use wasmer_core::types::FuncSig;
|
||||||
use super::type_converters::itype_to_wtype;
|
use super::type_converters::itype_to_wtype;
|
||||||
|
|
||||||
let inputs = inputs.map(itype_to_wtype).collect::<Vec<_>>();
|
let inputs = inputs.map(itype_to_wtype).collect::<Vec<_>>();
|
||||||
let outputs = outputs.map(itype_to_wtype).collect::<Vec<_>>();
|
let outputs = outputs.map(itype_to_wtype).collect::<Vec<_>>();
|
||||||
<WB as WasmBackend>::DynamicFunc::new(
|
<WB as WasmBackend>::DynamicFunc::new(FuncSig::new(inputs, outputs), raw_import)
|
||||||
Arc::new(FuncSig::new(inputs, outputs)),
|
|
||||||
raw_import,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a closure that is represent a IT module import
|
// creates a closure that is represent a IT module import
|
||||||
|
@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use std::cell::Cell;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use wasmer_it::interpreter::wasm;
|
|
||||||
use wasmer_core::memory::{Memory, MemoryView};
|
|
||||||
use wasmer_core::vm::LocalMemory;
|
|
||||||
use crate::module::WasmerSequentialReader;
|
|
||||||
|
|
||||||
use crate::module::WasmerSequentialWriter;
|
|
||||||
|
|
||||||
use it_memory_traits::{MemoryAccessError};
|
|
||||||
|
|
||||||
pub(crate) struct WITMemoryView<'a>(pub(crate) MemoryView<'a, u8>);
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct WITMemory(pub(super) Memory);
|
|
||||||
impl std::ops::Deref for WITMemory {
|
|
||||||
type Target = Memory;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WITMemoryView<'_> {
|
|
||||||
fn check_bounds(
|
|
||||||
&self,
|
|
||||||
offset: usize,
|
|
||||||
size: usize,
|
|
||||||
memory_size: usize,
|
|
||||||
) -> Result<(), MemoryAccessError> {
|
|
||||||
if offset + size >= memory_size {
|
|
||||||
Err(MemoryAccessError::OutOfBounds {
|
|
||||||
offset,
|
|
||||||
size,
|
|
||||||
memory_size,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s, 'v> wasm::structures::SequentialMemoryView<'v> for WITMemoryView<'s> {
|
|
||||||
type SR = WasmerSequentialReader<'v>;
|
|
||||||
type SW = WasmerSequentialWriter<'v>;
|
|
||||||
|
|
||||||
fn sequential_writer(
|
|
||||||
&'v self,
|
|
||||||
offset: usize,
|
|
||||||
size: usize,
|
|
||||||
) -> Result<Self::SW, MemoryAccessError> {
|
|
||||||
let view = &self.0;
|
|
||||||
let slice = view.deref();
|
|
||||||
|
|
||||||
self.check_bounds(offset, size, slice.len())?;
|
|
||||||
|
|
||||||
let writer = WasmerSequentialWriter {
|
|
||||||
offset,
|
|
||||||
slice,
|
|
||||||
current_offset: Cell::new(offset),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sequential_reader(
|
|
||||||
&'v self,
|
|
||||||
offset: usize,
|
|
||||||
size: usize,
|
|
||||||
) -> Result<Self::SR, MemoryAccessError> {
|
|
||||||
let view = &self.0;
|
|
||||||
let slice: &[Cell<u8>] = view.deref();
|
|
||||||
|
|
||||||
self.check_bounds(offset, size, slice.len())?;
|
|
||||||
|
|
||||||
let reader = WasmerSequentialReader {
|
|
||||||
memory: slice,
|
|
||||||
offset: Cell::new(offset),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(reader)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> wasm::structures::Memory<WITMemoryView<'a>> for WITMemory {
|
|
||||||
fn view(&self) -> WITMemoryView<'a> {
|
|
||||||
let LocalMemory { base, .. } = unsafe { *self.0.vm_local_memory() };
|
|
||||||
let length = self.0.size().bytes().0 / std::mem::size_of::<u8>();
|
|
||||||
|
|
||||||
unsafe { WITMemoryView(MemoryView::new(base as _, length as u32)) }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
use std::cell::Cell;
|
|
||||||
use it_memory_traits::{SequentialReader, SequentialWriter};
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! value_der {
|
|
||||||
($self:expr, $offset:expr, @seq_start $($ids:tt),* @seq_end) => {
|
|
||||||
[$($self.memory[$offset + $ids].get()),+]
|
|
||||||
};
|
|
||||||
|
|
||||||
($self:expr, $offset:expr, 1) => {
|
|
||||||
crate::value_der!($self, $offset, @seq_start 0 @seq_end);
|
|
||||||
};
|
|
||||||
|
|
||||||
($self:expr, $offset:expr, 2) => {
|
|
||||||
crate::value_der!($self, $offset, @seq_start 0, 1 @seq_end);
|
|
||||||
};
|
|
||||||
|
|
||||||
($self:expr, $offset:expr, 4) => {
|
|
||||||
crate::value_der!($self, $offset, @seq_start 0, 1, 2, 3 @seq_end);
|
|
||||||
};
|
|
||||||
|
|
||||||
($self:expr, $offset:expr, 8) => {
|
|
||||||
crate::value_der!($self, $offset, @seq_start 0, 1, 2, 3, 4, 5, 6, 7 @seq_end);
|
|
||||||
};
|
|
||||||
|
|
||||||
($self:expr, $offset:expr, 16) => {
|
|
||||||
crate::value_der!($self, $offset, @seq_start 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 @seq_end);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! read_ty {
|
|
||||||
($func_name:ident, $ty:ty, 1) => {
|
|
||||||
fn $func_name(&self) -> $ty {
|
|
||||||
let offset = self.offset.get();
|
|
||||||
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 1));
|
|
||||||
|
|
||||||
self.offset.set(offset + 1);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
($func_name:ident, $ty:ty, 2) => {
|
|
||||||
fn $func_name(&self) -> $ty {
|
|
||||||
let offset = self.offset.get();
|
|
||||||
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 2));
|
|
||||||
|
|
||||||
self.offset.set(offset + 2);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
($func_name:ident, $ty:ty, 4) => {
|
|
||||||
fn $func_name(&self) -> $ty {
|
|
||||||
let offset = self.offset.get();
|
|
||||||
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 4));
|
|
||||||
|
|
||||||
self.offset.set(offset + 4);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
($func_name:ident, $ty:ty, 8) => {
|
|
||||||
fn $func_name(&self) -> $ty {
|
|
||||||
let offset = self.offset.get();
|
|
||||||
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 8));
|
|
||||||
|
|
||||||
self.offset.set(offset + 8);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
($func_name:ident, $ty:ty, 16) => {
|
|
||||||
fn $func_name(&self) -> $ty {
|
|
||||||
let offset = self.offset.get();
|
|
||||||
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 16));
|
|
||||||
|
|
||||||
self.offset.set(offset + 16);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct WasmerSequentialReader<'s> {
|
|
||||||
pub memory: &'s [Cell<u8>],
|
|
||||||
pub offset: Cell<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct WasmerSequentialWriter<'s> {
|
|
||||||
pub offset: usize,
|
|
||||||
pub slice: &'s [Cell<u8>],
|
|
||||||
pub current_offset: Cell<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SequentialReader for WasmerSequentialReader<'_> {
|
|
||||||
fn read_byte(&self) -> u8 {
|
|
||||||
let offset = self.offset.get();
|
|
||||||
let result = self.memory[offset].get();
|
|
||||||
self.offset.set(offset + 1);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
// needed because clippy suggests using an iterator which looks worse
|
|
||||||
#[allow(clippy::needless_range_loop)]
|
|
||||||
fn read_bytes<const COUNT: usize>(&self) -> [u8; COUNT] {
|
|
||||||
let offset = self.offset.get();
|
|
||||||
let mut result = [0u8; COUNT];
|
|
||||||
for index in 0..COUNT {
|
|
||||||
result[index] = self.memory[offset + index].get();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.offset.set(offset + COUNT);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SequentialWriter for WasmerSequentialWriter<'_> {
|
|
||||||
fn start_offset(&self) -> usize {
|
|
||||||
self.offset
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u8(&self, value: u8) {
|
|
||||||
let offset = self.current_offset.get();
|
|
||||||
self.slice[offset].set(value);
|
|
||||||
self.current_offset.set(offset + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u32(&self, value: u32) {
|
|
||||||
self.write_bytes(&value.to_le_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_bytes(&self, bytes: &[u8]) {
|
|
||||||
for byte in bytes {
|
|
||||||
self.write_u8(*byte)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -46,8 +46,10 @@ pub struct MFunctionSignature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use marine_module::MModule;
|
pub(crate) use marine_module::MModule;
|
||||||
pub(self) use wasmer_core::types::Type as WType;
|
//pub(self) use wasmer_core::types::Type as WType;
|
||||||
pub(self) use wasmer_core::types::Value as WValue;
|
//pub(self) use wasmer_core::types::Value as WValue;
|
||||||
|
//pub(self) use marine_wasm_backend_traits::WType;
|
||||||
|
pub(self) use marine_wasm_backend_traits::WValue;
|
||||||
|
|
||||||
// types that often used together
|
// types that often used together
|
||||||
pub(crate) mod wit_prelude {
|
pub(crate) mod wit_prelude {
|
||||||
|
@ -15,7 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/// Contains converters of types and values between Wasmer and wasmer_interface_types.
|
/// Contains converters of types and values between Wasmer and wasmer_interface_types.
|
||||||
use super::{WType, WValue, IType, IValue};
|
use super::{IType, IValue};
|
||||||
|
|
||||||
|
use marine_wasm_backend_traits::WType;
|
||||||
|
use marine_wasm_backend_traits::WValue;
|
||||||
|
|
||||||
pub(super) fn wtype_to_itype(ty: &WType) -> IType {
|
pub(super) fn wtype_to_itype(ty: &WType) -> IType {
|
||||||
match ty {
|
match ty {
|
||||||
@ -23,7 +26,7 @@ pub(super) fn wtype_to_itype(ty: &WType) -> IType {
|
|||||||
WType::I64 => IType::I64,
|
WType::I64 => IType::I64,
|
||||||
WType::F32 => IType::F32,
|
WType::F32 => IType::F32,
|
||||||
WType::F64 => IType::F64,
|
WType::F64 => IType::F64,
|
||||||
WType::V128 => unimplemented!(),
|
//WType::V128 => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +67,6 @@ pub(super) fn wval_to_ival(value: &WValue) -> IValue {
|
|||||||
WValue::I64(v) => IValue::I64(*v),
|
WValue::I64(v) => IValue::I64(*v),
|
||||||
WValue::F32(v) => IValue::F32(*v),
|
WValue::F32(v) => IValue::F32(*v),
|
||||||
WValue::F64(v) => IValue::F64(*v),
|
WValue::F64(v) => IValue::F64(*v),
|
||||||
_ => unimplemented!(),
|
//_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::marine_module::MModule;
|
use super::marine_module::MModule;
|
||||||
use super::{IType, IFunctionArg, IValue, WValue};
|
use super::{IType, IFunctionArg, IValue};
|
||||||
use super::marine_module::Callable;
|
use super::marine_module::Callable;
|
||||||
use crate::MResult;
|
use crate::MResult;
|
||||||
|
|
||||||
use marine_wasm_backend_traits::WasmBackend;
|
use marine_wasm_backend_traits::{WasmBackend, WValue};
|
||||||
|
use marine_wasm_backend_traits::ExportedDynFunc;
|
||||||
|
|
||||||
use wasmer_it::interpreter::wasm;
|
use wasmer_it::interpreter::wasm;
|
||||||
use wasmer_core::instance::DynFunc;
|
|
||||||
|
|
||||||
// use std::sync::Arc;
|
// use std::sync::Arc;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -30,7 +30,7 @@ use std::rc::Rc;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum WITFunctionInner<WB: WasmBackend> {
|
enum WITFunctionInner<WB: WasmBackend> {
|
||||||
Export {
|
Export {
|
||||||
func: Rc<DynFunc<'static>>,
|
func: Rc<<WB as WasmBackend>::ExportedDynFunc>,
|
||||||
},
|
},
|
||||||
Import {
|
Import {
|
||||||
// TODO: use dyn Callable here
|
// TODO: use dyn Callable here
|
||||||
@ -49,24 +49,22 @@ pub(super) struct WITFunction<WB: WasmBackend> {
|
|||||||
|
|
||||||
impl<WB: WasmBackend> WITFunction<WB> {
|
impl<WB: WasmBackend> WITFunction<WB> {
|
||||||
/// Creates functions from a "usual" (not IT) module export.
|
/// Creates functions from a "usual" (not IT) module export.
|
||||||
pub(super) fn from_export(dyn_func: DynFunc<'static>, name: String) -> MResult<Self> {
|
pub(super) fn from_export(
|
||||||
|
dyn_func: <WB as WasmBackend>::ExportedDynFunc,
|
||||||
|
name: String,
|
||||||
|
) -> MResult<Self> {
|
||||||
use super::type_converters::wtype_to_itype;
|
use super::type_converters::wtype_to_itype;
|
||||||
|
|
||||||
let signature = dyn_func.signature();
|
let signature = dyn_func.signature();
|
||||||
let arguments = signature
|
let arguments = signature
|
||||||
.params()
|
.params()
|
||||||
.iter()
|
|
||||||
.map(|wtype| IFunctionArg {
|
.map(|wtype| IFunctionArg {
|
||||||
// here it's considered as an anonymous arguments
|
// here it's considered as an anonymous arguments
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
ty: wtype_to_itype(wtype),
|
ty: wtype_to_itype(wtype),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let outputs = signature
|
let outputs = signature.returns().map(wtype_to_itype).collect::<Vec<_>>();
|
||||||
.returns()
|
|
||||||
.iter()
|
|
||||||
.map(wtype_to_itype)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let inner = WITFunctionInner::Export {
|
let inner = WITFunctionInner::Export {
|
||||||
func: Rc::new(dyn_func),
|
func: Rc::new(dyn_func),
|
||||||
@ -128,14 +126,20 @@ impl<WB: WasmBackend> wasm::structures::LocalImport for WITFunction<WB> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, arguments: &[IValue]) -> std::result::Result<Vec<IValue>, ()> {
|
fn call(&self, arguments: &[IValue]) -> std::result::Result<Vec<IValue>, ()> {
|
||||||
use super::type_converters::{ival_to_wval, wval_to_ival};
|
use super::type_converters::wval_to_ival;
|
||||||
|
use super::type_converters::ival_to_wval;
|
||||||
match &self.inner {
|
match &self.inner {
|
||||||
WITFunctionInner::Export { func, .. } => func
|
WITFunctionInner::Export { func, .. } => func
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.call(&arguments.iter().map(ival_to_wval).collect::<Vec<WValue>>())
|
.call(
|
||||||
.map(|result| result.iter().map(wval_to_ival).collect())
|
arguments
|
||||||
.map_err(|_| ()),
|
.iter()
|
||||||
|
.map(ival_to_wval)
|
||||||
|
.collect::<Vec<WValue>>()
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.map_err(|_| ())
|
||||||
|
.map(|results| results.iter().map(wval_to_ival).collect()),
|
||||||
WITFunctionInner::Import { callable, .. } => Rc::make_mut(&mut callable.clone())
|
WITFunctionInner::Import { callable, .. } => Rc::make_mut(&mut callable.clone())
|
||||||
.call(arguments)
|
.call(arguments)
|
||||||
.map_err(|_| ()),
|
.map_err(|_| ()),
|
||||||
|
@ -78,24 +78,16 @@ impl<WB: WasmBackend> ITInstance<WB> {
|
|||||||
wasmer_instance: &<WB as WasmBackend>::I,
|
wasmer_instance: &<WB as WasmBackend>::I,
|
||||||
it: &MITInterfaces<'_>,
|
it: &MITInterfaces<'_>,
|
||||||
) -> MResult<HashMap<usize, WITFunction<WB>>> {
|
) -> MResult<HashMap<usize, WITFunction<WB>>> {
|
||||||
use wasmer_core::DynFunc;
|
|
||||||
|
|
||||||
let module_exports = &wasmer_instance.exports();
|
let module_exports = &wasmer_instance.exports();
|
||||||
|
|
||||||
it.exports()
|
it.exports()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(export_id, export)| {
|
.map(|(export_id, export)| {
|
||||||
let export_func = module_exports.get(export.name)?;
|
let export_func = module_exports.get_dyn_func(export.name)?;
|
||||||
unsafe {
|
Ok((
|
||||||
// TODO: refactor this with new Wasmer API when it is ready
|
export_id,
|
||||||
// here it is safe because dyn func is never lives WITInstance
|
WITFunction::from_export(export_func, export.name.to_string())?,
|
||||||
let export_func =
|
))
|
||||||
std::mem::transmute::<DynFunc<'_>, DynFunc<'static>>(export_func);
|
|
||||||
Ok((
|
|
||||||
export_id,
|
|
||||||
WITFunction::from_export(export_func, export.name.to_string())?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user