feat!: decouple wasmer from Marine, replace it with generic backend interface (#219)

This commit is contained in:
Valery Antopol
2023-03-15 00:43:51 +03:00
committed by GitHub
parent b0e9b2c104
commit d3a773df4f
157 changed files with 5375 additions and 2179 deletions

View File

@ -1,4 +1,4 @@
edition = "2018"
edition = "2021"
reorder_imports = false
reorder_modules = false

2422
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,8 @@ members = [
"crates/min-it-version",
"crates/module-info-parser",
"crates/module-interface",
"crates/wasm-backend-traits",
"crates/wasmtime-backend",
"crates/utils",
"examples/call_parameters",
"examples/failing",
@ -40,6 +42,12 @@ members = [
"tools/repl",
]
[workspace.dependencies]
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.26.0" }
it-memory-traits ="0.4.0"
it-lilo = "0.5.0"
fluence-it-types = { version = "0.4.0", features = ["impls"] }
[profile.release]
opt-level = 3
debug = false

View File

@ -4,7 +4,7 @@ description = "Core of Marine, the Fluence Wasm Runtime"
version = "0.19.0"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "marine_core"
@ -17,15 +17,13 @@ marine-it-parser = { path = "../crates/it-parser", version = "0.11.1" }
marine-it-generator = { path = "../crates/it-generator", version = "0.9.5" }
marine-module-interface = { path = "../crates/module-interface", version = "0.6.1" }
marine-utils = { path = "../crates/utils", version = "0.4.0" }
marine-min-it-version = { path = "../crates/min-it-version", version = "0.2.1" }
marine-min-it-version = { path = "../crates/min-it-version", version = "0.2.1" }
marine-wasm-backend-traits = {path = "../crates/wasm-backend-traits", version = "0.1.0"}
marine-wasmtime-backend = { path = "../crates/wasmtime-backend", version = "0.1.0"}
wasmer-runtime = { package = "wasmer-runtime-fl", version = "=0.17.1" }
# dynamicfunc-fat-closures allows using state inside DynamicFunc
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1", features = ["dynamicfunc-fat-closures"] }
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1"}
wasmer-wasi = { package = "wasmer-wasi-fl", version = "0.17.1" }
it-lilo = "0.4.1"
it-memory-traits = "0.3.1"
wasmer-it = { workspace = true, default-features = false }
it-lilo = { workspace = true}
it-memory-traits = { workspace = true }
bytesize = "1.1.0"
multimap = "0.8.3"

View File

@ -18,9 +18,8 @@ use super::IValue;
use super::IType;
use crate::HostImportError;
use wasmer_wasi::WasiVersion;
use wasmer_runtime::ImportObject;
use wasmer_core::vm::Ctx;
use marine_wasm_backend_traits::WasiParameters;
use marine_wasm_backend_traits::WasmBackend;
use std::path::PathBuf;
use std::collections::HashMap;
@ -29,12 +28,21 @@ use std::collections::HashSet;
// 65536*1600 ~ 100 Mb (Wasm page size is 64 Kb)
const DEFAULT_HEAP_PAGES_COUNT: u32 = 1600;
pub type HostExportedFunc = Box<dyn Fn(&mut Ctx, Vec<IValue>) -> Option<IValue> + 'static>;
pub type ErrorHandler = Option<Box<dyn Fn(&HostImportError) -> Option<IValue> + 'static>>;
pub type ErrorHandler =
Option<Box<dyn Fn(&HostImportError) -> Option<IValue> + Sync + Send + 'static>>;
pub type HostExportedFunc<WB> = Box<
dyn for<'c> Fn(&mut <WB as WasmBackend>::Caller<'c>, Vec<IValue>) -> Option<IValue>
+ Sync
+ Send
+ 'static,
>;
pub struct HostImportDescriptor {
pub type RawImportCreator<WB> =
Box<dyn FnOnce(<WB as WasmBackend>::ContextMut<'_>) -> <WB as WasmBackend>::Function>;
pub struct HostImportDescriptor<WB: WasmBackend> {
/// This closure will be invoked for corresponding import.
pub host_exported_func: HostExportedFunc,
pub host_exported_func: HostExportedFunc<WB>,
/// Type of the closure arguments.
pub argument_types: Vec<IType>,
@ -47,41 +55,29 @@ pub struct HostImportDescriptor {
pub error_handler: ErrorHandler,
}
pub struct MModuleConfig {
pub struct MModuleConfig<WB: WasmBackend> {
/// Maximum number of Wasm memory pages that loaded module can use.
/// Each Wasm page is 65536 bytes long.
pub max_heap_pages_count: u32,
/// Import object that will be used in module instantiation process.
pub raw_imports: ImportObject,
pub raw_imports: HashMap<String, RawImportCreator<WB>>,
/// Imports from the host side that will be used in module instantiation process.
pub host_imports: HashMap<String, HostImportDescriptor>,
pub host_imports: HashMap<String, HostImportDescriptor<WB>>,
/// Desired WASI version.
pub wasi_version: WasiVersion,
/// Environment variables for loaded modules.
pub wasi_envs: HashMap<Vec<u8>, Vec<u8>>,
/// List of available directories for loaded modules.
pub wasi_preopened_files: HashSet<PathBuf>,
/// Mapping between paths.
pub wasi_mapped_dirs: HashMap<String, PathBuf>,
/// WASI parameters: env variables, mapped dirs, preopened files and args
pub wasi_parameters: WasiParameters,
}
impl Default for MModuleConfig {
impl<WB: WasmBackend> Default for MModuleConfig<WB> {
fn default() -> Self {
// some reasonable defaults
Self {
max_heap_pages_count: DEFAULT_HEAP_PAGES_COUNT,
raw_imports: ImportObject::new(),
raw_imports: HashMap::new(),
host_imports: HashMap::new(),
wasi_version: WasiVersion::Latest,
wasi_envs: HashMap::new(),
wasi_preopened_files: HashSet::new(),
wasi_mapped_dirs: HashMap::new(),
wasi_parameters: WasiParameters::default(),
}
}
}
@ -89,29 +85,24 @@ impl Default for MModuleConfig {
// TODO: implement debug for MModuleConfig
#[allow(dead_code)]
impl MModuleConfig {
impl<WB: WasmBackend> MModuleConfig<WB> {
pub fn with_mem_pages_count(mut self, mem_pages_count: u32) -> Self {
self.max_heap_pages_count = mem_pages_count;
self
}
pub fn with_wasi_version(mut self, wasi_version: WasiVersion) -> Self {
self.wasi_version = wasi_version;
self
}
pub fn with_wasi_envs(mut self, envs: HashMap<Vec<u8>, Vec<u8>>) -> Self {
self.wasi_envs = envs;
self.wasi_parameters.envs = envs;
self
}
pub fn with_wasi_preopened_files(mut self, preopened_files: HashSet<PathBuf>) -> Self {
self.wasi_preopened_files = preopened_files;
self.wasi_parameters.preopened_files = preopened_files;
self
}
pub fn with_wasi_mapped_dirs(mut self, mapped_dirs: HashMap<String, PathBuf>) -> Self {
self.wasi_mapped_dirs = mapped_dirs;
self.wasi_parameters.mapped_dirs = mapped_dirs;
self
}
}

View File

@ -20,8 +20,7 @@ use crate::misc::PrepareError;
use marine_it_interfaces::MITInterfacesError;
use marine_it_parser::ITParserError;
use marine_module_interface::it_interface::ITInterfaceError;
use wasmer_runtime::error as wasmer_error;
use marine_wasm_backend_traits::errors::*;
use thiserror::Error as ThisError;
@ -30,41 +29,13 @@ use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum MError {
/// This error type is produced by Wasmer during resolving a Wasm function.
#[error("Wasmer resolve error: {0}")]
ResolveError(#[from] wasmer_error::ResolveError),
/// Error related to calling a main Wasm module.
#[error("Wasmer invoke error: {0}")]
WasmerInvokeError(String),
/// Error that raises during compilation Wasm code by Wasmer.
#[error("Wasmer creation error: {0}")]
WasmerCreationError(#[from] wasmer_error::CreationError),
/// Error that raises during creation of some Wasm objects (like table and memory) by Wasmer.
#[error("Wasmer compile error: {0}")]
WasmerCompileError(#[from] wasmer_error::CompileError),
/// Errors arisen during execution of a Wasm module.
#[error("Wasmer runtime error: {0}")]
WasmerRuntimeError(String),
/// Errors arisen during linking Wasm modules with already loaded into Marine modules.
#[error("Wasmer link error: {0}")]
WasmerLinkError(#[from] wasmer_error::LinkError),
/// Errors from the temporary class of amalgamation errors from the Wasmer side.
#[error("Wasmer error: {0}")]
WasmerError(String),
/// Errors related to failed resolving of records.
#[error("{0}")]
RecordResolveError(String),
RecordResolveError(String), // TODO: use a proper error type
/// Errors arisen during creation of a WASI context.
#[error("{0}")]
WASIPrepareError(String),
#[error(transparent)]
WASIPrepareError(#[from] WasiError),
/// Errors occurred inside marine-module-interface crate.
#[error(transparent)]
@ -100,7 +71,10 @@ pub enum MError {
/// Incorrect IT section.
#[error("{0}")]
IncorrectWIT(String),
IncorrectWIT(String), // TODO: use a proper error type
#[error("Wasm backend error: {0}")]
WasmBackendError(#[from] WasmBackendError),
}
impl From<MITInterfacesError> for MError {
@ -109,26 +83,32 @@ impl From<MITInterfacesError> for MError {
}
}
impl From<wasmer_error::RuntimeError> for MError {
fn from(err: wasmer_error::RuntimeError) -> Self {
Self::WasmerRuntimeError(err.to_string())
impl From<ModuleCreationError> for MError {
fn from(value: ModuleCreationError) -> Self {
Into::<WasmBackendError>::into(value).into()
}
}
impl From<wasmer_error::Error> for MError {
fn from(err: wasmer_error::Error) -> Self {
Self::WasmerError(err.to_string())
impl From<ResolveError> for MError {
fn from(value: ResolveError) -> Self {
Into::<WasmBackendError>::into(value).into()
}
}
impl From<wasmer_error::InvokeError> for MError {
fn from(err: wasmer_error::InvokeError) -> Self {
Self::WasmerInvokeError(err.to_string())
impl From<ImportError> for MError {
fn from(value: ImportError) -> Self {
Into::<WasmBackendError>::into(value).into()
}
}
impl From<()> for MError {
fn from(_err: ()) -> Self {
MError::IncorrectWIT("failed to parse instructions for adapter type".to_string())
impl From<InstantiationError> for MError {
fn from(value: InstantiationError) -> Self {
Into::<WasmBackendError>::into(value).into()
}
}
impl From<RuntimeError> for MError {
fn from(value: RuntimeError) -> Self {
Into::<WasmBackendError>::into(value).into()
}
}

View File

@ -36,15 +36,15 @@ pub enum HostImportError {
#[error("Not enough WValue arguments are provided from the Wasm side")]
MismatchWValuesCount,
#[error("{0}")]
#[error(transparent)]
LifterError(#[from] LiError),
#[error("{0}")]
#[error(transparent)]
LowererError(#[from] LoError),
#[error("{0}")]
#[error(transparent)]
RecordNotFound(#[from] RecordResolvableError),
#[error("{0}")]
#[error(transparent)]
InvalidUTF8String(#[from] std::string::FromUtf8Error),
}

View File

@ -22,126 +22,189 @@ use super::lowering::LoHelper;
use super::utils::itypes_args_to_wtypes;
use super::utils::itypes_output_to_wtypes;
use crate::IType;
use crate::IValue;
use crate::MRecordTypes;
use crate::init_wasm_func_once;
use crate::init_wasm_func;
use crate::call_wasm_func;
use crate::HostImportDescriptor;
use crate::module::wit_prelude::WITMemoryView;
use crate::generic::HostImportDescriptor;
use marine_wasm_backend_traits::prelude::*;
use wasmer_core::Func;
use wasmer_core::vm::Ctx;
use wasmer_core::typed_func::DynamicFunc;
use wasmer_core::types::Value as WValue;
use wasmer_core::types::FuncSig;
use it_lilo::lifter::ILifter;
use it_lilo::lowerer::ILowerer;
use it_memory_traits::Memory as ITMemory;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
pub(crate) fn create_host_import_func(
descriptor: HostImportDescriptor,
record_types: Rc<MRecordTypes>,
) -> DynamicFunc<'static> {
let allocate_func: AllocateFunc = Box::new(RefCell::new(None));
let set_result_ptr_func: SetResultPtrFunc = Box::new(RefCell::new(None));
let set_result_size_func: SetResultSizeFunc = Box::new(RefCell::new(None));
pub(crate) fn create_host_import_func<WB: WasmBackend>(
store: &mut <WB as WasmBackend>::Store,
descriptor: HostImportDescriptor<WB>,
record_types: Arc<MRecordTypes>,
) -> <WB as WasmBackend>::Function {
let raw_args = itypes_args_to_wtypes(&descriptor.argument_types);
let raw_output =
itypes_output_to_wtypes(&output_type_to_types(descriptor.output_type.as_ref()));
let func = move |caller: <WB as WasmBackend>::Caller<'_>, inputs: &[WValue]| -> Vec<WValue> {
call_host_import(caller, inputs, &descriptor, record_types.clone())
};
<WB as WasmBackend>::Function::new_with_caller(
&mut store.as_context_mut(),
FuncSig::new(raw_args, raw_output),
func,
)
}
fn call_host_import<WB: WasmBackend>(
mut caller: <WB as WasmBackend>::Caller<'_>,
inputs: &[WValue],
descriptor: &HostImportDescriptor<WB>,
record_types: Arc<MRecordTypes>,
) -> Vec<WValue> {
let HostImportDescriptor {
host_exported_func,
argument_types,
output_type,
error_handler,
..
} = descriptor;
let output_type_to_types = |output_type| match output_type {
Some(ty) => vec![ty],
None => vec![],
};
let memory = caller
.memory(STANDARD_MEMORY_INDEX)
.unwrap_or_else(|| panic!("Host import called directly, not from wasm"));
let raw_args = itypes_args_to_wtypes(&argument_types);
let raw_output = itypes_output_to_wtypes(&output_type_to_types(output_type));
let func = move |ctx: &mut Ctx, inputs: &[WValue]| -> Vec<WValue> {
let result = {
let memory_index = 0;
let memory = ctx.memory(memory_index);
let memory_view = WITMemoryView(memory.view::<u8>());
let li_helper = LiHelper::new(record_types.clone());
let lifter = ILifter::new(memory_view, &li_helper);
match wvalues_to_ivalues(&lifter, inputs, &argument_types) {
Ok(ivalues) => host_exported_func(ctx, ivalues),
Err(e) => {
log::error!("error occurred while lifting values in host import: {}", e);
error_handler
.as_ref()
.map_or_else(|| default_error_handler(&e), |h| h(&e))
}
}
};
init_wasm_func_once!(allocate_func, ctx, (i32, i32), i32, ALLOCATE_FUNC_NAME, 2);
let memory_index = 0;
let memory = ctx.memory(memory_index);
let memory_view = WITMemoryView(memory.view::<u8>());
let lo_helper = LoHelper::new(&allocate_func, ctx);
let t = ILowerer::new(memory_view, &lo_helper)
.map_err(HostImportError::LowererError)
.and_then(|lowerer| ivalue_to_wvalues(&lowerer, result));
let wvalues = match t {
Ok(wvalues) => wvalues,
Err(e) => {
log::error!("host closure failed: {}", e);
// returns 0 to a Wasm module in case of errors
init_wasm_func_once!(set_result_ptr_func, ctx, i32, (), SET_PTR_FUNC_NAME, 4);
init_wasm_func_once!(set_result_size_func, ctx, i32, (), SET_SIZE_FUNC_NAME, 4);
call_wasm_func!(set_result_ptr_func, 0);
call_wasm_func!(set_result_size_func, 0);
return vec![WValue::I32(0)];
}
};
// TODO: refactor this when multi-value is supported
match wvalues.len() {
// strings and arrays are passed back to the Wasm module by pointer and size
2 => {
init_wasm_func_once!(set_result_ptr_func, ctx, i32, (), SET_PTR_FUNC_NAME, 4);
init_wasm_func_once!(set_result_size_func, ctx, i32, (), SET_SIZE_FUNC_NAME, 4);
call_wasm_func!(set_result_ptr_func, wvalues[0].to_u128() as _);
call_wasm_func!(set_result_size_func, wvalues[1].to_u128() as _);
vec![]
}
// records and primitive types are passed to the Wasm module by pointer
// and value on the stack
1 => {
init_wasm_func_once!(set_result_ptr_func, ctx, i32, (), SET_PTR_FUNC_NAME, 3);
call_wasm_func!(set_result_ptr_func, wvalues[0].to_u128() as _);
vec![wvalues[0].clone()]
}
// when None is passed
0 => vec![],
// at now while multi-values aren't supported ivalue_to_wvalues returns only Vec with
// 0, 1, 2 values
_ => unimplemented!(),
let inputs = lift_inputs::<WB>(
&mut caller,
memory.clone(),
record_types,
inputs,
argument_types,
);
let output = match inputs {
Ok(ivalues) => host_exported_func(&mut caller, ivalues),
Err(e) => {
log::error!("error occurred while lifting values in host import: {}", e);
error_handler
.as_ref()
.map_or_else(|| default_error_handler(&e), |h| h(&e))
}
};
DynamicFunc::new(
std::sync::Arc::new(FuncSig::new(raw_args, raw_output)),
func,
lower_outputs::<WB>(&mut caller, memory, output)
}
fn lift_inputs<WB: WasmBackend>(
caller: &mut <WB as WasmBackend>::Caller<'_>,
memory: <WB as WasmBackend>::Memory,
record_types: Arc<MRecordTypes>,
inputs: &[WValue],
argument_types: &[IType],
) -> HostImportResult<Vec<IValue>> {
let memory_view = memory.view();
let li_helper = LiHelper::new(record_types);
let lifter = ILifter::new(memory_view, &li_helper);
wvalues_to_ivalues(
&mut caller.as_context_mut(),
&lifter,
inputs,
argument_types,
)
}
fn lower_outputs<WB: WasmBackend>(
caller: &mut <WB as WasmBackend>::Caller<'_>,
memory: <WB as WasmBackend>::Memory,
output: Option<IValue>,
) -> Vec<WValue> {
init_wasm_func!(
allocate_func,
caller,
(i32, i32),
i32,
ALLOCATE_FUNC_NAME,
2
);
let is_record = matches!(&output, Some(IValue::Record(_)));
let memory_view = memory.view();
let mut lo_helper = LoHelper::new(&mut allocate_func, memory);
let lowering_result =
ILowerer::<'_, _, _, DelayedContextLifetime<WB>>::new(memory_view, &mut lo_helper)
.map_err(HostImportError::LowererError)
.and_then(|mut lowerer| {
ivalue_to_wvalues(&mut caller.as_context_mut(), &mut lowerer, output)
});
let wvalues = match lowering_result {
Ok(wvalues) => wvalues,
Err(e) => {
log::error!("host closure failed: {}", e);
// returns 0 to a Wasm module in case of errors
init_wasm_func!(set_result_ptr_func, caller, i32, (), SET_PTR_FUNC_NAME, 4);
init_wasm_func!(set_result_size_func, caller, i32, (), SET_SIZE_FUNC_NAME, 4);
call_wasm_func!(set_result_ptr_func, &mut caller.as_context_mut(), 0);
call_wasm_func!(set_result_size_func, &mut caller.as_context_mut(), 0);
return vec![WValue::I32(0)];
}
};
// TODO: refactor this when multi-value is supported
match wvalues.len() {
// strings and arrays are passed back to the Wasm module by pointer and size
// values used and consumed by set_result_ptr and set_result_size
2 => {
init_wasm_func!(set_result_ptr_func, caller, i32, (), SET_PTR_FUNC_NAME, 4);
init_wasm_func!(set_result_size_func, caller, i32, (), SET_SIZE_FUNC_NAME, 4);
call_wasm_func!(
set_result_ptr_func,
&mut caller.as_context_mut(),
wvalues[0].to_u128() as _
);
call_wasm_func!(
set_result_size_func,
&mut caller.as_context_mut(),
wvalues[1].to_u128() as _
);
vec![]
}
// records lowerer returns only pointer which has to be used and consumed via set_result_ptr
1 if is_record => {
init_wasm_func!(set_result_ptr_func, caller, i32, (), SET_PTR_FUNC_NAME, 3);
call_wasm_func!(
set_result_ptr_func,
&mut caller.as_context_mut(),
wvalues[0].to_u128() as _
);
vec![]
}
// primitive values are passed as is
1 => vec![wvalues[0].clone()],
// when None is passed
0 => vec![],
// at now while multi-values aren't supported ivalue_to_wvalues returns only Vec with
// 0, 1, 2 values
_ => unimplemented!(),
}
}
fn output_type_to_types(output_type: Option<&IType>) -> Vec<IType> {
match output_type {
Some(ty) => vec![ty.clone()],
None => vec![],
}
}
fn default_error_handler(err: &HostImportError) -> Option<crate::IValue> {
panic!(
"an error is occurred while lifting values to interface values: {}",

View File

@ -19,14 +19,14 @@ use crate::IRecordType;
use it_lilo::traits::RecordResolvable;
use it_lilo::traits::RecordResolvableError;
use std::rc::Rc;
use std::sync::Arc;
pub(crate) struct LiHelper {
record_types: Rc<MRecordTypes>,
record_types: Arc<MRecordTypes>,
}
impl LiHelper {
pub(crate) fn new(record_types: Rc<MRecordTypes>) -> Self {
pub(crate) fn new(record_types: Arc<MRecordTypes>) -> Self {
Self { record_types }
}
}

View File

@ -43,8 +43,13 @@ macro_rules! simple_wvalue_to_ivalue {
}};
}
pub(crate) fn wvalues_to_ivalues<R: RecordResolvable, MV: MemoryView>(
lifter: &ILifter<'_, R, MV>,
pub(crate) fn wvalues_to_ivalues<
R: RecordResolvable,
MV: MemoryView<S>,
S: it_memory_traits::Store,
>(
store: &mut <S as it_memory_traits::Store>::ActualStore<'_>,
lifter: &ILifter<'_, R, MV, S>,
wvalues: &[WValue],
itypes: &[IType],
) -> HostImportResult<Vec<IValue>> {
@ -73,7 +78,9 @@ pub(crate) fn wvalues_to_ivalues<R: RecordResolvable, MV: MemoryView>(
let offset = next_wvalue!(wvalue, I32);
let size = next_wvalue!(wvalue, I32);
let raw_str = lifter.reader.read_raw_u8_array(offset as _, size as _)?;
let raw_str = lifter
.reader
.read_raw_u8_array(store, offset as _, size as _)?;
let str = String::from_utf8(raw_str)?;
result.push(IValue::String(str));
}
@ -81,21 +88,23 @@ pub(crate) fn wvalues_to_ivalues<R: RecordResolvable, MV: MemoryView>(
let offset = next_wvalue!(wvalue, I32);
let size = next_wvalue!(wvalue, I32);
let array = lifter.reader.read_raw_u8_array(offset as _, size as _)?;
let array = lifter
.reader
.read_raw_u8_array(store, offset as _, size as _)?;
result.push(IValue::ByteArray(array));
}
IType::Array(ty) => {
let offset = next_wvalue!(wvalue, I32);
let size = next_wvalue!(wvalue, I32);
let array = array_lift_memory(lifter, ty, offset as _, size as _)?;
let array = array_lift_memory(store, lifter, ty, offset as _, size as _)?;
result.push(array);
}
IType::Record(record_type_id) => {
let record_type = lifter.resolver.resolve_record(*record_type_id)?;
let offset = next_wvalue!(wvalue, I32);
let record = record_lift_memory(lifter, record_type, offset as _)?;
let record = record_lift_memory(store, lifter, record_type, offset as _)?;
result.push(record);
}
}

View File

@ -15,34 +15,59 @@
*/
use super::AllocateFunc;
use crate::module::wit_prelude::WITMemoryView;
use crate::call_wasm_func;
use marine_wasm_backend_traits::DelayedContextLifetime;
use marine_wasm_backend_traits::WasmBackend;
use it_lilo::traits::Allocatable;
use it_lilo::traits::AllocatableError;
use it_memory_traits::MemoryView;
use it_memory_traits::Memory;
use it_lilo::traits::DEFAULT_MEMORY_INDEX;
use wasmer_core::vm::Ctx;
use std::marker::PhantomData;
pub(crate) struct LoHelper<'c> {
allocate_func: &'c AllocateFunc,
ctx: &'c Ctx,
pub(crate) struct LoHelper<
'c,
WB: WasmBackend,
MV: MemoryView<DelayedContextLifetime<WB>>,
M: Memory<MV, DelayedContextLifetime<WB>>,
> {
allocate_func: &'c mut AllocateFunc<WB>,
memory: M,
_memory_view_phantom: PhantomData<MV>,
}
impl<'c> LoHelper<'c> {
pub(crate) fn new(allocate_func: &'c AllocateFunc, ctx: &'c Ctx) -> Self {
Self { allocate_func, ctx }
impl<
'c,
WB: WasmBackend,
MV: MemoryView<DelayedContextLifetime<WB>>,
M: Memory<MV, DelayedContextLifetime<WB>>,
> LoHelper<'c, WB, MV, M>
{
pub(crate) fn new(allocate_func: &'c mut AllocateFunc<WB>, memory: M) -> Self {
Self {
allocate_func,
memory,
_memory_view_phantom: <_>::default(),
}
}
}
impl<'s> Allocatable<WITMemoryView<'s>> for LoHelper<'s> {
impl<
's,
WB: WasmBackend,
MV: MemoryView<DelayedContextLifetime<WB>>,
M: Memory<MV, DelayedContextLifetime<WB>>,
> Allocatable<MV, DelayedContextLifetime<WB>> for LoHelper<'s, WB, MV, M>
{
fn allocate(
&self,
&mut self,
store: &mut <WB as WasmBackend>::ContextMut<'_>,
size: u32,
type_tag: u32,
) -> Result<(u32, WITMemoryView<'s>), AllocatableError> {
let offset = call_wasm_func!(self.allocate_func, size as _, type_tag as _);
let view = WITMemoryView(self.ctx.memory(DEFAULT_MEMORY_INDEX as u32).view());
Ok((offset as u32, view))
) -> Result<(u32, MV), AllocatableError> {
let offset = call_wasm_func!(self.allocate_func, store, size as _, type_tag as _);
Ok((offset as u32, self.memory.view()))
}
}

View File

@ -22,8 +22,13 @@ use it_lilo::lowerer::*;
use it_lilo::traits::Allocatable;
use it_memory_traits::MemoryView;
pub(crate) fn ivalue_to_wvalues<A: Allocatable<MV>, MV: MemoryView>(
lowerer: &ILowerer<'_, A, MV>,
pub(crate) fn ivalue_to_wvalues<
A: Allocatable<MV, Store>,
MV: MemoryView<Store>,
Store: it_memory_traits::Store,
>(
store: &mut <Store as it_memory_traits::Store>::ActualStore<'_>,
lowerer: &mut ILowerer<'_, A, MV, Store>,
ivalue: Option<IValue>,
) -> HostImportResult<Vec<WValue>> {
let result = match ivalue {
@ -41,21 +46,21 @@ pub(crate) fn ivalue_to_wvalues<A: Allocatable<MV>, MV: MemoryView>(
Some(IValue::F32(v)) => vec![WValue::F32(v)],
Some(IValue::F64(v)) => vec![WValue::F64(v)],
Some(IValue::String(str)) => {
let offset = lowerer.writer.write_bytes(str.as_bytes())?;
let offset = lowerer.writer.write_bytes(store, str.as_bytes())?;
vec![WValue::I32(offset as _), WValue::I32(str.len() as _)]
}
Some(IValue::ByteArray(array)) => {
let offset = lowerer.writer.write_bytes(&array)?;
let offset = lowerer.writer.write_bytes(store, &array)?;
vec![WValue::I32(offset as _), WValue::I32(array.len() as _)]
}
Some(IValue::Array(values)) => {
let LoweredArray { offset, size } = array_lower_memory(lowerer, values)?;
let LoweredArray { offset, size } = array_lower_memory(store, lowerer, values)?;
vec![WValue::I32(offset as _), WValue::I32(size as _)]
}
Some(IValue::Record(values)) => {
let offset = record_lower_memory(lowerer, values)?;
let offset = record_lower_memory(store, lowerer, values)?;
vec![WValue::I32(offset as i32)]
}
None => vec![],

View File

@ -20,20 +20,20 @@ mod lowering;
mod imports;
mod utils;
use std::cell::RefCell;
use wasmer_core::Func;
use marine_wasm_backend_traits::RuntimeResult;
use marine_wasm_backend_traits::WasmBackend;
pub use errors::HostImportError;
pub(crate) use imports::create_host_import_func;
pub(self) use wasmer_core::types::Value as WValue;
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 WasmModuleFunc<Args, Rets> = Box<RefCell<Option<Func<'static, Args, Rets>>>>;
pub(self) type AllocateFunc = WasmModuleFunc<(i32, i32), i32>;
pub(self) type SetResultPtrFunc = WasmModuleFunc<i32, ()>;
pub(self) type SetResultSizeFunc = WasmModuleFunc<i32, ()>;
pub(self) type WasmModuleFunc<WB, Args, Rets> = Box<
dyn FnMut(&mut <WB as WasmBackend>::ContextMut<'_>, Args) -> RuntimeResult<Rets> + Sync + Send,
>;
pub(self) type AllocateFunc<WB> = WasmModuleFunc<WB, (i32, i32), i32>;
pub(self) const ALLOCATE_FUNC_NAME: &str = "allocate";
pub(self) const SET_PTR_FUNC_NAME: &str = "set_result_ptr";

View File

@ -17,85 +17,6 @@
use super::WType;
use crate::IType;
use wasmer_core::backend::SigRegistry;
use wasmer_core::module::ExportIndex;
use wasmer_core::vm::Ctx;
use wasmer_core::typed_func::WasmTypeList;
use wasmer_runtime::Func;
use wasmer_runtime::error::ResolveError;
use wasmer_runtime::types::LocalOrImport;
// based on Wasmer: https://github.com/wasmerio/wasmer/blob/081f6250e69b98b9f95a8f62ad6d8386534f3279/lib/runtime-core/src/instance.rs#L863
/// Extract export function from Wasmer instance by name.
pub(super) unsafe fn get_export_func_by_name<'a, Args, Rets>(
ctx: &'a mut Ctx,
name: &str,
) -> Result<Func<'a, Args, Rets>, ResolveError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
let module_inner = &(*ctx.module);
let export_index =
module_inner
.info
.exports
.get(name)
.ok_or_else(|| ResolveError::ExportNotFound {
name: name.to_string(),
})?;
let export_func_index = match export_index {
ExportIndex::Func(func_index) => func_index,
_ => {
return Err(ResolveError::ExportWrongType {
name: name.to_string(),
})
}
};
let export_func_signature_idx = *module_inner
.info
.func_assoc
.get(*export_func_index)
.expect("broken invariant, incorrect func index");
let export_func_signature = &module_inner.info.signatures[export_func_signature_idx];
let export_func_signature_ref = SigRegistry.lookup_signature_ref(export_func_signature);
if export_func_signature_ref.params() != Args::types()
|| export_func_signature_ref.returns() != Rets::types()
{
return Err(ResolveError::Signature {
expected: (*export_func_signature).clone(),
found: Args::types().to_vec(),
});
}
let func_wasm_inner = module_inner
.runnable_module
.get_trampoline(&module_inner.info, export_func_signature_idx)
.unwrap();
let export_func_ptr = match export_func_index.local_or_import(&module_inner.info) {
LocalOrImport::Local(local_func_index) => module_inner
.runnable_module
.get_func(&module_inner.info, local_func_index)
.unwrap(),
_ => {
return Err(ResolveError::ExportNotFound {
name: name.to_string(),
})
}
};
let typed_func: Func<'_, Args, Rets, wasmer_core::typed_func::Wasm> =
Func::from_raw_parts(func_wasm_inner, export_func_ptr, None, ctx as _);
Ok(typed_func)
}
pub(super) fn itypes_args_to_wtypes(itypes: &[IType]) -> Vec<WType> {
itypes
.iter()
@ -122,35 +43,26 @@ pub(super) fn itypes_output_to_wtypes(itypes: &[IType]) -> Vec<WType> {
.collect()
}
#[macro_export] // https://github.com/rust-lang/rust/issues/57966#issuecomment-461077932
/// Initialize Wasm function in form of Box<RefCell<Option<Func<'static, args, rets>>>> only once.
macro_rules! init_wasm_func_once {
#[macro_export]
/// Initialize Wasm function in form of Box<RefCell<Option<Func<'static, args, rets>>>>.
/// This macro does not cache result.
macro_rules! init_wasm_func {
($func:ident, $ctx:ident, $args:ty, $rets:ty, $func_name:ident, $ret_error_code: expr) => {
if $func.borrow().is_none() {
let raw_func = match unsafe {
super::utils::get_export_func_by_name::<$args, $rets>($ctx, $func_name)
} {
Ok(func) => func,
Err(_) => return vec![WValue::I32($ret_error_code)],
};
unsafe {
// assumed that this function will be used only in the context of closure
// linked to a corresponding Wasm import, so it is safe to make is static
// because all Wasm imports live in the Wasmer instances, which
// is itself static (i.e., lives until the end of the program)
let raw_func = std::mem::transmute::<Func<'_, _, _>, Func<'static, _, _>>(raw_func);
*$func.borrow_mut() = Some(raw_func);
}
}
let mut $func: Box<
dyn FnMut(&mut <WB as WasmBackend>::ContextMut<'_>, $args) -> RuntimeResult<$rets>
+ Send
+ Sync,
> = match { $ctx.get_func($func_name) } {
Ok(func) => func,
Err(_) => return vec![WValue::I32($ret_error_code)],
};
};
}
#[macro_export]
/// Call Wasm function that have Box<RefCell<Option<Func<'static, args, rets>>>> type.
macro_rules! call_wasm_func {
($func:expr, $($arg:expr),*) => {
$func.borrow().as_ref().unwrap().call($($arg),*).unwrap()
($func:expr, $store:expr, $($arg:expr),*) => {
$func.as_mut()($store, ($($arg),*)).unwrap()
};
}

View File

@ -35,10 +35,6 @@ mod misc;
mod module;
mod memory_statistic;
pub use config::MModuleConfig;
pub use config::HostExportedFunc;
pub use config::HostImportDescriptor;
pub use crate::marine_core::MarineCore;
pub use crate::marine_core::MModuleInterface;
pub use errors::MError;
pub use host_imports::HostImportError;
@ -59,3 +55,21 @@ pub mod ne_vec {
}
pub(crate) type MResult<T> = std::result::Result<T, MError>;
pub mod generic {
pub use crate::config::MModuleConfig;
pub use crate::config::HostExportedFunc;
pub use crate::config::HostImportDescriptor;
pub use crate::marine_core::MarineCore;
}
pub mod wasmtime {
pub type WasmBackend = marine_wasmtime_backend::WasmtimeWasmBackend;
pub type MModuleConfig = crate::config::MModuleConfig<WasmBackend>;
pub type HostExportedFunc = crate::config::HostExportedFunc<WasmBackend>;
pub type HostImportDescriptor = crate::config::HostImportDescriptor<WasmBackend>;
pub type MarineCore = crate::marine_core::MarineCore<WasmBackend>;
}
pub use crate::wasmtime::*;

View File

@ -14,15 +14,22 @@
* limitations under the License.
*/
use super::*;
use super::generic::*;
use crate::module::MModule;
use crate::module::MRecordTypes;
use crate::{IRecordType, IValue, MemoryStats, MError, MFunctionSignature, ModuleMemoryStat, MResult};
use marine_wasm_backend_traits::AsContextMut;
use marine_wasm_backend_traits::Store;
use marine_wasm_backend_traits::WasiState;
use marine_wasm_backend_traits::WasmBackend;
use serde::Serialize;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
use std::cell::RefCell;
/// Represent Marine module interface.
#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
@ -31,17 +38,36 @@ pub struct MModuleInterface<'a> {
pub function_signatures: Vec<MFunctionSignature>,
}
/// # Description
///
/// The base struct of Marine, the Fluence compute runtime.
pub struct MarineCore {
/// Allows dynamic loading and unloading modules, but never frees resources used for instantiation.
/// A new module can import functions from previously loaded modules.
///
/// # Recommendations
///
/// Its not recommended to use this struct to load/unload unlimited number of modules.
/// Better alternative is to use multiple instances of this struct for independent groups of modules
/// and drop them when the group is no longer needed.
pub struct MarineCore<WB: WasmBackend> {
// set of modules registered inside Marine
modules: HashMap<String, MModule>,
modules: HashMap<String, MModule<WB>>,
// Wasm backend may have state in the future
#[allow(unused)]
wasm_backend: WB,
/// Container for all objects created by a Wasm backend.
store: RefCell<<WB as WasmBackend>::Store>,
}
impl MarineCore {
pub fn new() -> Self {
Self {
impl<WB: WasmBackend> MarineCore<WB> {
pub fn new() -> MResult<Self> {
let wasm_backend = WB::new()?;
let store = <WB as WasmBackend>::Store::new(&wasm_backend);
Ok(Self {
modules: HashMap::new(),
}
wasm_backend,
store: RefCell::new(store),
})
}
/// Invoke a function of a module inside Marine by given function name with given arguments.
@ -52,10 +78,17 @@ impl MarineCore {
arguments: &[IValue],
) -> MResult<Vec<IValue>> {
let module_name = module_name.as_ref();
let store = &mut self.store;
self.modules.get_mut(module_name).map_or_else(
|| Err(MError::NoSuchModule(module_name.to_string())),
|module| module.call(module_name, func_name.as_ref(), arguments),
|module| {
module.call(
&mut store.get_mut().as_context_mut(),
module_name,
func_name.as_ref(),
arguments,
)
},
)
}
@ -64,7 +97,7 @@ impl MarineCore {
&mut self,
name: impl Into<String>,
wasm_bytes: &[u8],
config: MModuleConfig,
config: MModuleConfig<WB>,
) -> MResult<()> {
self.load_module_(name.into(), wasm_bytes, config)
}
@ -73,11 +106,17 @@ impl MarineCore {
&mut self,
name: String,
wasm_bytes: &[u8],
config: MModuleConfig,
config: MModuleConfig<WB>,
) -> MResult<()> {
let _prepared_wasm_bytes =
crate::misc::prepare_module(wasm_bytes, config.max_heap_pages_count)?;
let module = MModule::new(&name, wasm_bytes, config, &self.modules)?;
let module = MModule::new(
&name,
self.store.get_mut(),
wasm_bytes,
config,
&self.modules,
)?;
match self.modules.entry(name) {
Entry::Vacant(entry) => {
@ -97,10 +136,10 @@ impl MarineCore {
.ok_or_else(|| MError::NoSuchModule(name.as_ref().to_string()))
}
pub fn module_wasi_state(
&mut self,
pub fn module_wasi_state<'s>(
&'s mut self,
module_name: impl AsRef<str>,
) -> Option<&wasmer_wasi::state::WasiState> {
) -> Option<Box<dyn WasiState + 's>> {
self.modules
.get_mut(module_name.as_ref())
.map(|module| module.get_wasi_state())
@ -132,7 +171,7 @@ impl MarineCore {
&self,
module_name: impl AsRef<str>,
record_id: u64,
) -> Option<&Rc<IRecordType>> {
) -> Option<&Arc<IRecordType>> {
self.modules
.get(module_name.as_ref())
.and_then(|module| module.export_record_type_by_id(record_id))
@ -144,14 +183,18 @@ impl MarineCore {
.modules
.iter()
.map(|(module_name, module)| {
ModuleMemoryStat::new(module_name, module.memory_size(), module.max_memory_size())
ModuleMemoryStat::new(
module_name,
module.memory_size(&mut self.store.borrow_mut().as_context_mut()),
module.max_memory_size(),
)
})
.collect::<Vec<_>>();
records.into()
}
fn get_module_interface(module: &MModule) -> MModuleInterface<'_> {
fn get_module_interface(module: &MModule<WB>) -> MModuleInterface<'_> {
let record_types = module.export_record_types();
let function_signatures = module.get_exports_signatures().collect::<Vec<_>>();
@ -162,9 +205,3 @@ impl MarineCore {
}
}
}
impl Default for MarineCore {
fn default() -> Self {
Self::new()
}
}

View File

@ -44,7 +44,8 @@ impl ModuleBootstrapper {
}
fn set_max_heap_pages_count(self, max_heap_pages_count: u32) -> PrepareResult<Self> {
use elements::{MemoryType, MemorySection};
use elements::MemoryType;
use elements::MemorySection;
let Self { mut module } = self;
let globals_pages_count = get_heap_base(&module)

View File

@ -57,7 +57,8 @@ fn find_global_by_index(
}
fn u32_from_global_entry(global_entry: &elements::GlobalEntry) -> HResult<u32> {
use elements::{Instruction, ValueType};
use elements::Instruction;
use elements::ValueType;
let entry_type = global_entry.global_type().content_type();
if !matches!(entry_type, ValueType::I32) {

View File

@ -20,23 +20,18 @@ use super::PrepareError;
use marine_module_info_parser::sdk_version;
use marine_min_it_version::min_sdk_version;
use marine_min_it_version::min_it_version;
use marine_wasm_backend_traits::WasmBackend;
use wasmer_core::Module;
pub(crate) fn check_sdk_version(
name: impl Into<String>,
wasmer_module: &Module,
pub(crate) fn check_sdk_version<WB: WasmBackend>(
name: String,
wasmer_module: &<WB as WasmBackend>::Module,
) -> PrepareResult<()> {
let module_version = sdk_version::extract_from_wasmer_module(wasmer_module)?;
let module_version = match module_version {
Some(module_version) => module_version,
None => return Err(PrepareError::ModuleWithoutVersion(name.into())),
};
let module_version = sdk_version::extract_from_compiled_module::<WB>(wasmer_module)?;
let required_version = min_sdk_version();
if module_version < *required_version {
return Err(PrepareError::IncompatibleSDKVersions {
module_name: name.into(),
module_name: name,
required: required_version.clone(),
provided: module_version,
});

View File

@ -17,140 +17,140 @@
use super::wit_prelude::*;
use super::MFunctionSignature;
use super::MRecordTypes;
use super::{IType, IRecordType, IFunctionArg, IValue, WValue};
use super::IType;
use super::IRecordType;
use super::IFunctionArg;
use super::IValue;
use super::WValue;
use crate::generic::HostImportDescriptor;
use crate::MResult;
use crate::MModuleConfig;
use crate::generic::MModuleConfig;
use crate::config::RawImportCreator;
use marine_wasm_backend_traits::prelude::*;
use marine_it_interfaces::MITInterfaces;
use marine_it_parser::extract_it_from_module;
use marine_utils::SharedString;
use wasmer_core::Instance as WasmerInstance;
use wasmer_core::import::Namespace;
use wasmer_runtime::compile;
use wasmer_runtime::ImportObject;
use wasmer_it::interpreter::Interpreter;
use std::collections::HashMap;
use std::convert::TryInto;
use std::mem::MaybeUninit;
use std::sync::Arc;
use std::rc::Rc;
use std::borrow::BorrowMut;
const MEMORY_INDEX: u32 = 0;
const START_FUNC: &str = "_start";
const INITIALIZE_FUNC: &str = "_initialize";
type ITInterpreter =
Interpreter<ITInstance, ITExport, WITFunction, WITMemory, WITMemoryView<'static>>;
type ITInterpreter<WB> = Interpreter<
ITInstance<WB>,
ITExport,
WITFunction<WB>,
<WB as WasmBackend>::Memory,
<WB as WasmBackend>::MemoryView,
DelayedContextLifetime<WB>,
>;
#[derive(Clone)]
pub(super) struct ITModuleFunc {
interpreter: Arc<ITInterpreter>,
pub(super) arguments: Rc<Vec<IFunctionArg>>,
pub(super) output_types: Rc<Vec<IType>>,
pub(super) struct ITModuleFunc<WB: WasmBackend> {
interpreter: Arc<ITInterpreter<WB>>,
pub(super) arguments: Arc<Vec<IFunctionArg>>,
pub(super) output_types: Arc<Vec<IType>>,
}
#[derive(Clone)]
pub(super) struct Callable {
pub(super) it_instance: Arc<ITInstance>,
pub(super) it_module_func: ITModuleFunc,
pub(super) struct Callable<WB: WasmBackend> {
pub(super) it_instance: Arc<ITInstance<WB>>,
pub(super) it_module_func: ITModuleFunc<WB>,
}
impl Callable {
pub fn call(&mut self, args: &[IValue]) -> MResult<Vec<IValue>> {
impl<WB: WasmBackend> Callable<WB> {
pub fn call(
&mut self,
store: &mut <WB as WasmBackend>::ContextMut<'_>,
args: &[IValue],
) -> MResult<Vec<IValue>> {
use wasmer_it::interpreter::stack::Stackable;
let result = self
.it_module_func
.interpreter
.run(args, Arc::make_mut(&mut self.it_instance))?
.run(args, Arc::make_mut(&mut self.it_instance), store)?
.as_slice()
.to_owned();
Ok(result)
}
}
type ExportFunctions = HashMap<SharedString, Rc<Callable>>;
type ExportFunctions<WB> = HashMap<SharedString, Arc<Callable<WB>>>;
pub(crate) struct MModule {
// wasmer_instance is needed because WITInstance contains dynamic functions
// that internally keep pointer to it.
#[allow(unused)]
wasmer_instance: Box<WasmerInstance>,
pub(crate) struct MModule<WB: WasmBackend> {
wasm_instance: Box<<WB as WasmBackend>::Instance>,
// import_object is needed because ImportObject::extend doesn't really deep copy
// imports, so we need to store imports of this module to prevent their removing.
#[allow(unused)]
it_import_object: ImportObject,
// host_import_object is needed because ImportObject::extend doesn't really deep copy
// imports, so we need to store imports of this module to prevent their removing.
#[allow(unused)]
host_import_object: ImportObject,
// host_closures_import_object is needed because ImportObject::extend doesn't really deep copy
// imports, so we need to store imports of this module to prevent their removing.
#[allow(unused)]
host_closures_import_object: ImportObject,
// TODO: replace with dyn Trait
export_funcs: ExportFunctions,
export_funcs: ExportFunctions<WB>,
// TODO: save refs instead copying of a record types HashMap.
/// Record types used in exported functions as arguments or return values.
export_record_types: MRecordTypes,
}
impl MModule {
impl<WB: WasmBackend> MModule<WB> {
pub(crate) fn new(
name: &str,
store: &mut <WB as WasmBackend>::Store,
wasm_bytes: &[u8],
config: MModuleConfig,
modules: &HashMap<String, MModule>,
config: MModuleConfig<WB>,
modules: &HashMap<String, MModule<WB>>,
) -> MResult<Self> {
let wasmer_module = compile(wasm_bytes)?;
crate::misc::check_sdk_version(name, &wasmer_module)?;
let wasm_module = <WB as WasmBackend>::Module::new(store, wasm_bytes)?;
crate::misc::check_sdk_version::<WB>(name.to_string(), &wasm_module)?;
let it = extract_it_from_module(&wasmer_module)?;
let it = extract_it_from_module::<WB>(&wasm_module)?;
crate::misc::check_it_version(name, &it.version)?;
let mit = MITInterfaces::new(it);
let mut wit_instance = Arc::new_uninit();
let wit_import_object = Self::adjust_wit_imports(&mit, wit_instance.clone())?;
let raw_imports = config.raw_imports.clone();
let (wasi_import_object, host_closures_import_object) =
Self::create_import_objects(config, &mit, wit_import_object.clone())?;
let mut linker = <WB as WasmBackend>::Imports::new(store);
let wasmer_instance = wasmer_module.instantiate(&wasi_import_object)?;
let MModuleConfig {
raw_imports,
host_imports,
wasi_parameters,
..
} = config;
Self::add_wit_imports(store, &mut linker, &mit, wit_instance.clone())?;
Self::add_wasi_imports(store, &mut linker, wasi_parameters)?;
Self::add_host_imports(store, &mut linker, raw_imports, host_imports, &mit)?;
let wasm_instance = wasm_module.instantiate(store, &linker)?;
let it_instance = unsafe {
// TODO: check if this MaybeUninit/Arc tricks are still needed
// get_mut_unchecked here is safe because currently only this modules have reference to
// it and the environment is single-threaded
*Arc::get_mut_unchecked(&mut wit_instance) =
MaybeUninit::new(ITInstance::new(&wasmer_instance, name, &mit, modules)?);
std::mem::transmute::<_, Arc<ITInstance>>(wit_instance)
MaybeUninit::new(ITInstance::new(&wasm_instance, store, name, &mit, modules)?);
std::mem::transmute::<_, Arc<ITInstance<WB>>>(wit_instance)
};
let (export_funcs, export_record_types) = Self::instantiate_exports(&it_instance, &mit)?;
// backend is not expected to call _start or _initialize
// call _initialize to populate the WASI state of the module
#[rustfmt::skip]
if let Ok(initialize_func) = wasmer_instance.exports.get::<wasmer_runtime::Func<'_, (), ()>>(INITIALIZE_FUNC) {
initialize_func.call()?;
if let Ok(initialize_func) = wasm_instance.get_function(store, INITIALIZE_FUNC) {
initialize_func.call(store, &[])?;
}
// call _start to call module's main function
#[rustfmt::skip]
if let Ok(start_func) = wasmer_instance.exports.get::<wasmer_runtime::Func<'_, (), ()>>(START_FUNC) {
start_func.call()?;
if let Ok(start_func) = wasm_instance.get_function(store, START_FUNC) {
start_func.call(store, &[])?;
}
Ok(Self {
wasmer_instance: Box::new(wasmer_instance),
it_import_object: wit_import_object,
host_import_object: raw_imports,
host_closures_import_object,
wasm_instance: Box::new(wasm_instance),
export_funcs,
export_record_types,
})
@ -158,19 +158,33 @@ impl MModule {
pub(crate) fn call(
&mut self,
store: &mut <WB as WasmBackend>::ContextMut<'_>,
module_name: &str,
function_name: &str,
args: &[IValue],
) -> MResult<Vec<IValue>> {
self.export_funcs.get_mut(function_name).map_or_else(
log::debug!(
"calling {}::{} with args: {:?}",
module_name,
function_name,
args
);
let res = self.export_funcs.get_mut(function_name).map_or_else(
|| {
Err(MError::NoSuchFunction(
module_name.to_string(),
function_name.to_string(),
))
},
|func| Rc::make_mut(func).call(args),
)
|func| Arc::make_mut(func).call(store, args),
);
log::debug!(
"calling {}::{} with result: {:?}",
module_name,
function_name,
res
);
res
}
pub(crate) fn get_exports_signatures(&self) -> impl Iterator<Item = MFunctionSignature> + '_ {
@ -187,30 +201,28 @@ impl MModule {
&self.export_record_types
}
pub(crate) fn export_record_type_by_id(&self, record_type: u64) -> Option<&Rc<IRecordType>> {
pub(crate) fn export_record_type_by_id(&self, record_type: u64) -> Option<&Arc<IRecordType>> {
self.export_record_types.get(&record_type)
}
pub(crate) fn get_wasi_state(&mut self) -> &wasmer_wasi::state::WasiState {
unsafe { wasmer_wasi::state::get_wasi_state(self.wasmer_instance.context_mut()) }
pub(crate) fn get_wasi_state<'s>(&'s mut self) -> Box<dyn WasiState + 's> {
<WB as WasmBackend>::Wasi::get_wasi_state(self.wasm_instance.borrow_mut())
}
/// Returns Wasm linear memory size that this module consumes in bytes.
pub(crate) fn memory_size(&self) -> usize {
let pages = self.wasmer_instance.context().memory(MEMORY_INDEX).size();
pages.bytes().0
pub(crate) fn memory_size(&self, store: &mut <WB as WasmBackend>::ContextMut<'_>) -> usize {
let memory = self
.wasm_instance
.get_nth_memory(store, STANDARD_MEMORY_INDEX)
.expect("It is expected that the existence of at least one memory is checked in the MModule::new function");
memory.size(store)
}
/// Returns max Wasm linear memory size that this module could consume in bytes.
pub(crate) fn max_memory_size(&self) -> Option<usize> {
let maybe_pages = self
.wasmer_instance
.context()
.memory(MEMORY_INDEX)
.descriptor()
.maximum;
maybe_pages.map(|pages| pages.bytes().0)
// TODO: provide limits API to marine wasm backend traits
None
}
// TODO: change the cloning Callable behaviour after changes of Wasmer API
@ -218,7 +230,7 @@ impl MModule {
&self,
module_name: &str,
function_name: &str,
) -> MResult<Rc<Callable>> {
) -> MResult<Arc<Callable<WB>>> {
match self.export_funcs.get(function_name) {
Some(func) => Ok(func.clone()),
None => Err(MError::NoSuchFunction(
@ -228,121 +240,101 @@ impl MModule {
}
}
fn create_import_objects(
config: MModuleConfig,
fn add_wasi_imports(
store: &mut <WB as WasmBackend>::Store,
linker: &mut <WB as WasmBackend>::Imports,
parameters: WasiParameters,
) -> MResult<()> {
<WB as WasmBackend>::Wasi::register_in_linker(
&mut store.as_context_mut(),
linker,
parameters,
)?;
Ok(())
}
fn add_host_imports(
store: &mut <WB as WasmBackend>::Store,
linker: &mut <WB as WasmBackend>::Imports,
raw_imports: HashMap<String, RawImportCreator<WB>>,
host_imports: HashMap<String, HostImportDescriptor<WB>>,
mit: &MITInterfaces<'_>,
wit_import_object: ImportObject,
) -> MResult<(ImportObject, ImportObject)> {
) -> MResult<()> {
use crate::host_imports::create_host_import_func;
let wasi_envs = config
.wasi_envs
.into_iter()
.map(|(mut left, right)| {
left.push(61); // 61 is ASCII code of '='
left.extend(right);
left
})
.collect::<Vec<_>>();
let wasi_preopened_files = config.wasi_preopened_files.into_iter().collect::<Vec<_>>();
let wasi_mapped_dirs = config.wasi_mapped_dirs.into_iter().collect::<Vec<_>>();
let mut wasi_import_object = wasmer_wasi::generate_import_object_for_version(
config.wasi_version,
vec![],
wasi_envs,
wasi_preopened_files,
wasi_mapped_dirs,
)
.map_err(MError::WASIPrepareError)?;
let mut host_closures_namespace = Namespace::new();
let record_types = mit
.record_types()
.map(|(id, r)| (id, r.clone()))
.collect::<HashMap<_, _>>();
let record_types = Rc::new(record_types);
let record_types = Arc::new(record_types);
for (import_name, descriptor) in config.host_imports {
let host_import = create_host_import_func(descriptor, record_types.clone());
host_closures_namespace.insert(import_name, host_import);
}
let mut host_closures_import_object = ImportObject::new();
host_closures_import_object.register("host", host_closures_namespace);
wasi_import_object.extend(wit_import_object);
wasi_import_object.extend(config.raw_imports);
wasi_import_object.extend(host_closures_import_object.clone());
Ok((wasi_import_object, host_closures_import_object))
}
fn instantiate_exports(
it_instance: &Arc<ITInstance>,
mit: &MITInterfaces<'_>,
) -> MResult<(ExportFunctions, MRecordTypes)> {
let module_interface = marine_module_interface::it_interface::get_interface(mit)?;
let export_funcs = module_interface
.function_signatures
let host_imports = host_imports
.into_iter()
.map(|sign| {
let adapter_instructions = mit.adapter_by_type_r(sign.adapter_function_type)?;
let interpreter: ITInterpreter = adapter_instructions.clone().try_into()?;
let it_module_func = ITModuleFunc {
interpreter: Arc::new(interpreter),
arguments: sign.arguments.clone(),
output_types: sign.outputs.clone(),
};
let shared_string = SharedString(sign.name);
let callable = Rc::new(Callable {
it_instance: it_instance.clone(),
it_module_func,
});
Ok((shared_string, callable))
.map(|(import_name, descriptor)| {
let func = create_host_import_func::<WB>(store, descriptor, record_types.clone());
(import_name, func)
})
.collect::<MResult<ExportFunctions>>()?;
.collect::<Vec<_>>();
Ok((export_funcs, module_interface.export_record_types))
let all_imports = raw_imports
.into_iter()
.map(|(name, creator)| (name, creator(store.as_context_mut())))
.chain(host_imports.into_iter())
.collect::<Vec<_>>();
linker.register(store, "host", all_imports.into_iter())?;
Ok(())
}
// this function deals only with import functions that have an adaptor implementation
fn adjust_wit_imports(
fn add_wit_imports(
store: &mut <WB as WasmBackend>::Store,
linker: &mut <WB as WasmBackend>::Imports,
wit: &MITInterfaces<'_>,
wit_instance: Arc<MaybeUninit<ITInstance>>,
) -> MResult<ImportObject> {
wit_instance: Arc<MaybeUninit<ITInstance<WB>>>,
) -> MResult<()> {
use marine_it_interfaces::ITAstType;
use wasmer_core::typed_func::DynamicFunc;
use wasmer_core::vm::Ctx;
// returns function that will be called from imports of Wasmer module
fn dyn_func_from_raw_import<'a, 'b, F>(
inputs: impl Iterator<Item = &'a IType>,
outputs: impl Iterator<Item = &'b IType>,
fn func_from_raw_import<'a, 'b, F, WB, I1, I2>(
store: &mut <WB as WasmBackend>::Store,
inputs: I1,
outputs: I2,
raw_import: F,
) -> DynamicFunc<'static>
) -> <WB as WasmBackend>::Function
where
F: Fn(&mut Ctx, &[WValue]) -> Vec<WValue> + 'static,
F: for<'c> Fn(<WB as WasmBackend>::Caller<'c>, &[WValue]) -> Vec<WValue>
+ Sync
+ Send
+ 'static,
WB: WasmBackend,
I1: Iterator<Item = &'a IType>,
I2: Iterator<Item = &'b IType>,
{
use wasmer_core::types::FuncSig;
use super::type_converters::itype_to_wtype;
let inputs = inputs.map(itype_to_wtype).collect::<Vec<_>>();
let outputs = outputs.map(itype_to_wtype).collect::<Vec<_>>();
DynamicFunc::new(Arc::new(FuncSig::new(inputs, outputs)), raw_import)
<WB as WasmBackend>::Function::new_with_caller(
&mut store.as_context_mut(),
FuncSig::new(inputs, outputs),
raw_import,
)
}
// creates a closure that is represent a IT module import
fn create_raw_import(
wit_instance: Arc<MaybeUninit<ITInstance>>,
interpreter: ITInterpreter,
fn create_raw_import<WB: WasmBackend>(
wit_instance: Arc<MaybeUninit<ITInstance<WB>>>,
interpreter: ITInterpreter<WB>,
import_namespace: String,
import_name: String,
) -> impl Fn(&mut Ctx, &[WValue]) -> Vec<WValue> + 'static {
move |_: &mut Ctx, inputs: &[WValue]| -> Vec<WValue> {
) -> impl for<'c> Fn(<WB as WasmBackend>::Caller<'c>, &[WValue]) -> Vec<WValue>
+ Sync
+ Send
+ 'static {
move |mut ctx: <WB as WasmBackend>::Caller<'_>, inputs: &[WValue]| -> Vec<WValue> {
use wasmer_it::interpreter::stack::Stackable;
use super::type_converters::wval_to_ival;
@ -363,6 +355,7 @@ impl MModule {
interpreter.run(
&wit_inputs,
Arc::make_mut(&mut wit_instance_callable.assume_init()),
&mut ctx.as_context_mut(),
)
};
@ -374,6 +367,7 @@ impl MModule {
// TODO: optimize by prevent copying stack values
outputs
.map_err(|e| log::error!("interpreter got error {e}"))
.unwrap_or_default()
.as_slice()
.iter()
@ -402,7 +396,12 @@ impl MModule {
arguments,
output_types,
} => {
let interpreter: ITInterpreter = adapter_instructions.clone().try_into()?;
let interpreter: ITInterpreter<WB> =
adapter_instructions.clone().try_into().map_err(|_| {
MError::IncorrectWIT(
"failed to parse instructions for adapter type".to_string(),
)
})?;
let raw_import = create_raw_import(
wit_instance.clone(),
@ -411,7 +410,8 @@ impl MModule {
import_name.to_string(),
);
let wit_import = dyn_func_from_raw_import(
let wit_import = func_from_raw_import::<_, WB, _, _>(
store,
arguments.iter().map(|IFunctionArg { ty, .. }| ty),
output_types.iter(),
raw_import,
@ -427,17 +427,49 @@ impl MModule {
})
.collect::<MResult<multimap::MultiMap<_, _>>>()?;
let mut import_object = ImportObject::new();
// TODO: refactor this
for (namespace_name, funcs) in wit_import_funcs.into_iter() {
let mut namespace = Namespace::new();
for (import_name, import_func) in funcs.into_iter() {
namespace.insert(import_name.to_string(), import_func);
}
import_object.register(namespace_name, namespace);
let funcs = funcs.into_iter().map(|(name, f)| (name.to_string(), f));
linker.register(store, namespace_name, funcs)?;
}
Ok(import_object)
Ok(())
}
fn instantiate_exports(
it_instance: &Arc<ITInstance<WB>>,
mit: &MITInterfaces<'_>,
) -> MResult<(ExportFunctions<WB>, MRecordTypes)> {
let module_interface = marine_module_interface::it_interface::get_interface(mit)?;
let export_funcs = module_interface
.function_signatures
.into_iter()
.map(|sign| {
let adapter_instructions = mit.adapter_by_type_r(sign.adapter_function_type)?;
let interpreter: ITInterpreter<WB> =
adapter_instructions.clone().try_into().map_err(|_| {
MError::IncorrectWIT(
"failed to parse instructions for adapter type".to_string(),
)
})?;
let it_module_func = ITModuleFunc {
interpreter: Arc::new(interpreter),
arguments: sign.arguments.clone(),
output_types: sign.outputs.clone(),
};
let shared_string = SharedString(sign.name);
let callable = Arc::new(Callable {
it_instance: it_instance.clone(),
it_module_func,
});
Ok((shared_string, callable))
})
.collect::<MResult<ExportFunctions<WB>>>()?;
Ok((export_funcs, module_interface.export_record_types))
}
}

View File

@ -1,100 +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 it_memory_traits::{MemoryAccessError, MemoryView, MemoryReadable, MemoryWritable};
use wasmer_it::interpreter::wasm;
use wasmer_core::memory::Memory;
use wasmer_core::memory::MemoryView as WasmerMemoryView;
use wasmer_core::vm::LocalMemory;
use std::iter::zip;
pub(crate) struct WITMemoryView<'a>(pub(crate) WasmerMemoryView<'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<'s> MemoryReadable for WITMemoryView<'s> {
fn read_byte(&self, offset: u32) -> u8 {
self.0[offset as usize].get()
}
// needed because clippy suggests using an iterator which looks worse
#[allow(clippy::needless_range_loop)]
fn read_array<const COUNT: usize>(&self, offset: u32) -> [u8; COUNT] {
let mut result = [0u8; COUNT];
for index in 0..COUNT {
result[index] = self.0[offset as usize + index].get();
}
result
}
// needed because clippy suggests using an iterator which looks worse
#[allow(clippy::needless_range_loop)]
fn read_vec(&self, offset: u32, size: u32) -> Vec<u8> {
let end = (offset + size) as usize;
let start = offset as usize;
self.0[start..end].iter().map(|v| v.get()).collect()
}
}
impl<'s> MemoryWritable for WITMemoryView<'s> {
fn write_byte(&self, offset: u32, value: u8) {
self.0[offset as usize].set(value);
}
fn write_bytes(&self, offset: u32, bytes: &[u8]) {
let offset = offset as usize;
let pairs = zip(bytes.iter(), self.0[offset..offset + bytes.len()].iter());
for (src, dst) in pairs {
dst.set(*src)
}
}
}
impl<'s> MemoryView for WITMemoryView<'s> {
fn check_bounds(&self, offset: u32, size: u32) -> Result<(), MemoryAccessError> {
let memory_size = self.0.len() as u32;
if offset + size >= memory_size {
Err(MemoryAccessError::OutOfBounds {
offset,
size,
memory_size,
})
} else {
Ok(())
}
}
}
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(WasmerMemoryView::new(base as _, length as u32)) }
}
}

View File

@ -16,12 +16,12 @@
mod exports;
mod marine_module;
mod memory;
mod wit_function;
mod wit_instance;
mod type_converters;
pub use wit_instance::MRecordTypes;
pub use wasmer_it::IType;
pub use wasmer_it::IRecordType;
pub use wasmer_it::ast::FunctionArg as IFunctionArg;
@ -31,19 +31,18 @@ pub use wasmer_it::to_interface_value;
use serde::Serialize;
use serde::Deserialize;
use std::rc::Rc;
use std::sync::Arc;
/// Represent a function type inside Marine module.
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub struct MFunctionSignature {
pub name: Rc<String>,
pub arguments: Rc<Vec<IFunctionArg>>,
pub outputs: Rc<Vec<IType>>,
pub name: Arc<String>,
pub arguments: Arc<Vec<IFunctionArg>>,
pub outputs: Arc<Vec<IType>>,
}
pub(crate) use marine_module::MModule;
pub(self) use wasmer_core::types::Type as WType;
pub(self) use wasmer_core::types::Value as WValue;
pub(self) use marine_wasm_backend_traits::WValue;
// types that often used together
pub(crate) mod wit_prelude {
@ -51,7 +50,4 @@ pub(crate) mod wit_prelude {
pub(super) use super::exports::ITExport;
pub(super) use crate::MError;
pub(super) use super::wit_function::WITFunction;
pub(crate) use super::memory::WITMemoryView;
pub(crate) use super::memory::WITMemory;
}

View File

@ -14,8 +14,11 @@
* limitations under the License.
*/
/// Contains converters of types and values between Wasmer and wasmer_interface_types.
use super::{WType, WValue, IType, IValue};
use super::IType;
use super::IValue;
use marine_wasm_backend_traits::WType;
use marine_wasm_backend_traits::WValue;
pub(super) fn wtype_to_itype(ty: &WType) -> IType {
match ty {
@ -23,7 +26,10 @@ pub(super) fn wtype_to_itype(ty: &WType) -> IType {
WType::I64 => IType::I64,
WType::F32 => IType::F32,
WType::F64 => IType::F64,
WType::V128 => unimplemented!(),
ty => {
eprintln!("trying to convert {:?}", ty);
unimplemented!()
}
}
}
@ -64,6 +70,5 @@ pub(super) fn wval_to_ival(value: &WValue) -> IValue {
WValue::I64(v) => IValue::I64(*v),
WValue::F32(v) => IValue::F32(*v),
WValue::F64(v) => IValue::F64(*v),
_ => unimplemented!(),
}
}

View File

@ -15,42 +15,50 @@
*/
use super::marine_module::MModule;
use super::{IType, IFunctionArg, IValue, WValue};
use super::IType;
use super::IFunctionArg;
use super::IValue;
use super::marine_module::Callable;
use crate::MResult;
use wasmer_it::interpreter::wasm;
use wasmer_core::instance::DynFunc;
use marine_wasm_backend_traits::DelayedContextLifetime;
use marine_wasm_backend_traits::WasmBackend;
use marine_wasm_backend_traits::WValue;
use marine_wasm_backend_traits::Function;
// use std::sync::Arc;
use std::rc::Rc;
use wasmer_it::interpreter::wasm;
use std::sync::Arc;
#[derive(Clone)]
enum WITFunctionInner {
enum WITFunctionInner<WB: WasmBackend> {
Export {
func: Rc<DynFunc<'static>>,
func: Arc<<WB as WasmBackend>::Function>,
},
Import {
// TODO: use dyn Callable here
callable: Rc<Callable>,
callable: Arc<Callable<WB>>,
},
}
/// Represents all import and export functions that could be called from IT context by call-core.
#[derive(Clone)]
pub(super) struct WITFunction {
pub(super) struct WITFunction<WB: WasmBackend> {
name: String,
arguments: Rc<Vec<IFunctionArg>>,
outputs: Rc<Vec<IType>>,
inner: WITFunctionInner,
arguments: Arc<Vec<IFunctionArg>>,
outputs: Arc<Vec<IType>>,
inner: WITFunctionInner<WB>,
}
impl WITFunction {
impl<WB: WasmBackend> WITFunction<WB> {
/// 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(
store: &mut <WB as WasmBackend>::Store,
dyn_func: <WB as WasmBackend>::Function,
name: String,
) -> MResult<Self> {
use super::type_converters::wtype_to_itype;
let signature = dyn_func.signature();
let signature = dyn_func.signature(store);
let arguments = signature
.params()
.iter()
@ -67,11 +75,11 @@ impl WITFunction {
.collect::<Vec<_>>();
let inner = WITFunctionInner::Export {
func: Rc::new(dyn_func),
func: Arc::new(dyn_func),
};
let arguments = Rc::new(arguments);
let outputs = Rc::new(outputs);
let arguments = Arc::new(arguments);
let outputs = Arc::new(outputs);
Ok(Self {
name,
@ -83,11 +91,11 @@ impl WITFunction {
/// Creates function from a module import.
pub(super) fn from_import(
wit_module: &MModule,
wit_module: &MModule<WB>,
module_name: &str,
function_name: &str,
arguments: Rc<Vec<IFunctionArg>>,
outputs: Rc<Vec<IType>>,
arguments: Arc<Vec<IFunctionArg>>,
outputs: Arc<Vec<IType>>,
) -> MResult<Self> {
let callable = wit_module.get_callable(module_name, function_name)?;
@ -104,7 +112,9 @@ impl WITFunction {
}
}
impl wasm::structures::LocalImport for WITFunction {
impl<WB: WasmBackend> wasm::structures::LocalImport<DelayedContextLifetime<WB>>
for WITFunction<WB>
{
fn name(&self) -> &str {
self.name.as_str()
}
@ -125,17 +135,28 @@ impl wasm::structures::LocalImport for WITFunction {
&self.outputs
}
fn call(&self, arguments: &[IValue]) -> std::result::Result<Vec<IValue>, ()> {
use super::type_converters::{ival_to_wval, wval_to_ival};
fn call(
&self,
store: &mut <WB as WasmBackend>::ContextMut<'_>,
arguments: &[IValue],
) -> std::result::Result<Vec<IValue>, ()> {
use super::type_converters::wval_to_ival;
use super::type_converters::ival_to_wval;
match &self.inner {
WITFunctionInner::Export { func, .. } => func
.as_ref()
.call(&arguments.iter().map(ival_to_wval).collect::<Vec<WValue>>())
.map(|result| result.iter().map(wval_to_ival).collect())
.map_err(|_| ()),
WITFunctionInner::Import { callable, .. } => Rc::make_mut(&mut callable.clone())
.call(arguments)
.call(
store,
arguments
.iter()
.map(ival_to_wval)
.collect::<Vec<WValue>>()
.as_slice(),
)
.map_err(|_| ())
.map(|results| results.iter().map(wval_to_ival).collect()),
WITFunctionInner::Import { callable, .. } => Arc::make_mut(&mut callable.clone())
.call(store, arguments)
.map_err(|_| ()),
}
}

View File

@ -19,40 +19,49 @@ use super::marine_module::MModule;
use super::IRecordType;
use crate::MResult;
use marine_wasm_backend_traits::AsContextMut;
use marine_wasm_backend_traits::STANDARD_MEMORY_EXPORT_NAME;
use marine_wasm_backend_traits::DelayedContextLifetime;
use marine_wasm_backend_traits::WasmBackend;
use marine_wasm_backend_traits::Instance;
use marine_it_interfaces::MITInterfaces;
use marine_it_interfaces::ITAstType;
use wasmer_it::interpreter::wasm;
use wasmer_it::interpreter::wasm::structures::{LocalImportIndex, Memory, TypedIndex};
use wasmer_core::Instance as WasmerInstance;
use wasmer_it::interpreter::wasm::structures::LocalImportIndex;
use wasmer_it::interpreter::wasm::structures::Memory;
use wasmer_it::interpreter::wasm::structures::TypedIndex;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
pub type MRecordTypes = HashMap<u64, Rc<IRecordType>>;
pub type MRecordTypes = HashMap<u64, Arc<IRecordType>>;
/// Contains all import and export functions that could be called from IT context by call-core.
#[derive(Clone)]
pub(super) struct ITInstance {
pub(super) struct ITInstance<WB: WasmBackend> {
/// IT functions indexed by id.
funcs: HashMap<usize, WITFunction>,
funcs: HashMap<usize, WITFunction<WB>>,
/// IT memories.
memories: Vec<WITMemory>,
memories: Vec<<WB as WasmBackend>::Memory>,
/// All record types that instance contains.
record_types_by_id: MRecordTypes,
}
impl ITInstance {
impl<WB: WasmBackend> ITInstance<WB> {
pub(super) fn new(
wasmer_instance: &WasmerInstance,
wasm_instance: &<WB as WasmBackend>::Instance,
store: &mut <WB as WasmBackend>::Store,
module_name: &str,
wit: &MITInterfaces<'_>,
modules: &HashMap<String, MModule>,
modules: &HashMap<String, MModule<WB>>,
) -> MResult<Self> {
let mut exports = Self::extract_raw_exports(wasmer_instance, wit)?;
let mut exports = Self::extract_raw_exports(wasm_instance, store, wit)?;
let imports = Self::extract_imports(module_name, modules, wit, exports.len())?;
let memories = Self::extract_memories(wasmer_instance);
let memories = Self::extract_memories(wasm_instance, store);
exports.extend(imports);
let funcs = exports;
@ -67,27 +76,18 @@ impl ITInstance {
}
fn extract_raw_exports(
wasmer_instance: &WasmerInstance,
wasm_instance: &<WB as WasmBackend>::Instance,
store: &mut <WB as WasmBackend>::Store,
it: &MITInterfaces<'_>,
) -> MResult<HashMap<usize, WITFunction>> {
use wasmer_core::DynFunc;
let module_exports = &wasmer_instance.exports;
) -> MResult<HashMap<usize, WITFunction<WB>>> {
it.exports()
.enumerate()
.map(|(export_id, export)| {
let export_func = module_exports.get(export.name)?;
unsafe {
// TODO: refactor this with new Wasmer API when it is ready
// here it is safe because dyn func is never lives WITInstance
let export_func =
std::mem::transmute::<DynFunc<'_>, DynFunc<'static>>(export_func);
Ok((
export_id,
WITFunction::from_export(export_func, export.name.to_string())?,
))
}
let export_func = wasm_instance.get_function(store, export.name)?;
Ok((
export_id,
WITFunction::from_export(store, export_func, export.name.to_string())?,
))
})
.collect()
}
@ -95,10 +95,10 @@ impl ITInstance {
/// Extracts only those imports that don't have implementations.
fn extract_imports(
module_name: &str,
modules: &HashMap<String, MModule>,
modules: &HashMap<String, MModule<WB>>,
wit: &MITInterfaces<'_>,
start_index: usize,
) -> MResult<HashMap<usize, WITFunction>> {
) -> MResult<HashMap<usize, WITFunction<WB>>> {
wit.imports()
.filter(|import|
// filter out imports that have implementations
@ -129,29 +129,29 @@ impl ITInstance {
output_types,
)?;
Ok((start_index + idx as usize, func))
Ok((start_index + idx, func))
}
None => Err(MError::NoSuchModule(import.namespace.to_string())),
})
.collect::<MResult<HashMap<_, _>>>()
}
fn extract_memories(wasmer_instance: &WasmerInstance) -> Vec<WITMemory> {
use wasmer_core::export::Export::Memory;
fn extract_memories(
wasm_instance: &<WB as WasmBackend>::Instance,
store: &mut <WB as WasmBackend>::Store,
) -> Vec<<WB as WasmBackend>::Memory> {
use marine_wasm_backend_traits::Export::Memory;
let mut memories = wasmer_instance
.exports()
let mut memories = wasm_instance
.export_iter(store.as_context_mut())
.filter_map(|(_, export)| match export {
Memory(memory) => Some(WITMemory(memory)),
Memory(memory) => Some(memory),
_ => None,
})
.collect::<Vec<_>>();
if let Some(Memory(memory)) = wasmer_instance
.import_object
.maybe_with_namespace("env", |env| env.get_export("memory"))
{
memories.push(WITMemory(memory));
if let Ok(memory) = wasm_instance.get_memory(store, STANDARD_MEMORY_EXPORT_NAME) {
memories.push(memory);
}
memories
@ -175,19 +175,28 @@ impl ITInstance {
}
}
impl<'v> wasm::structures::Instance<ITExport, WITFunction, WITMemory, WITMemoryView<'v>>
for ITInstance
impl<WB: WasmBackend>
wasm::structures::Instance<
ITExport,
WITFunction<WB>,
<WB as WasmBackend>::Memory,
<WB as WasmBackend>::MemoryView,
DelayedContextLifetime<WB>,
> for ITInstance<WB>
{
fn export(&self, _export_name: &str) -> Option<&ITExport> {
// exports aren't used in this version of IT
None
}
fn local_or_import<I: TypedIndex + LocalImportIndex>(&self, index: I) -> Option<&WITFunction> {
fn local_or_import<I: TypedIndex + LocalImportIndex>(
&self,
index: I,
) -> Option<&WITFunction<WB>> {
self.funcs.get(&index.index())
}
fn memory(&self, index: usize) -> Option<&WITMemory> {
fn memory(&self, index: usize) -> Option<&<WB as WasmBackend>::Memory> {
if index >= self.memories.len() {
None
} else {
@ -195,17 +204,17 @@ impl<'v> wasm::structures::Instance<ITExport, WITFunction, WITMemory, WITMemoryV
}
}
fn memory_view(&self, index: usize) -> Option<WITMemoryView<'static>> {
fn memory_view(&self, index: usize) -> Option<<WB as WasmBackend>::MemoryView> {
if index >= self.memories.len() {
return None;
}
let memory = &self.memories[index];
let view: WITMemoryView<'static> = memory.view();
let view: <WB as WasmBackend>::MemoryView = memory.view();
Some(view)
}
fn wit_record_by_id(&self, index: u64) -> Option<&Rc<IRecordType>> {
fn wit_record_by_id(&self, index: u64) -> Option<&Arc<IRecordType>> {
self.record_types_by_id.get(&index)
}
}

View File

@ -26,9 +26,9 @@ static GREETING_WASM_BYTES: Lazy<Vec<u8>> = Lazy::new(|| {
#[test]
pub fn greeting_basic() {
let mut marine_core = MarineCore::new();
let mut marine_core = MarineCore::new().unwrap();
marine_core
.load_module("greeting", &*GREETING_WASM_BYTES, <_>::default())
.load_module("greeting", &GREETING_WASM_BYTES, <_>::default())
.unwrap_or_else(|e| panic!("can't load a module into Marine: {:?}", e));
let result1 = marine_core
@ -50,13 +50,13 @@ pub fn greeting_basic() {
#[test]
// test loading module with the same name twice
pub fn non_unique_module_name() {
let mut marine_core = MarineCore::new();
let mut marine_core = MarineCore::new().unwrap();
let module_name = String::from("greeting");
marine_core
.load_module(&module_name, &*GREETING_WASM_BYTES, <_>::default())
.load_module(&module_name, &GREETING_WASM_BYTES, <_>::default())
.unwrap_or_else(|e| panic!("can't load a module into Marine: {:?}", e));
let load_result = marine_core.load_module(&module_name, &*GREETING_WASM_BYTES, <_>::default());
let load_result = marine_core.load_module(&module_name, &GREETING_WASM_BYTES, <_>::default());
assert!(load_result.is_err());
assert!(std::matches!(
load_result.err().unwrap(),
@ -68,9 +68,9 @@ pub fn non_unique_module_name() {
#[allow(unused_variables)]
// test calling Marine with non-exist module and function names
pub fn non_exist_module_func() {
let mut marine_core = MarineCore::new();
let mut marine_core = MarineCore::new().unwrap();
marine_core
.load_module("greeting", &*GREETING_WASM_BYTES, <_>::default())
.load_module("greeting", &GREETING_WASM_BYTES, <_>::default())
.unwrap_or_else(|e| panic!("can't load a module into Marine: {:?}", e));
let module_name = "greeting";

View File

@ -25,7 +25,7 @@ pub fn records() {
let pure_wasm_bytes = std::fs::read("../examples/records/artifacts/records_pure.wasm")
.expect("../examples/records/artifacts/records_pure.wasm should presence");
let mut marine_core = MarineCore::new();
let mut marine_core = MarineCore::new().unwrap();
let load_result = marine_core.load_module("pure", &pure_wasm_bytes, <_>::default());
assert!(load_result.is_err());

View File

@ -35,7 +35,7 @@ pub async fn download(url: &str) -> bytes::Bytes {
async fn redis() {
let wasm_bytes = download(REDIS_DOWNLOAD_URL).await;
let mut marine_core = MarineCore::new();
let mut marine_core = MarineCore::new().unwrap();
let module_name = "redis";
let config = <_>::default();
@ -95,7 +95,7 @@ async fn redis() {
async fn sqlite() {
let wasm_bytes = download(SQLITE_DOWNLOAD_URL).await;
let mut marine_core = MarineCore::new();
let mut marine_core = MarineCore::new().unwrap();
let module_name = "sqlite";
let config = <_>::default();

View File

@ -2,7 +2,7 @@
name = "lilo-after-2gb-test"
version = "0.1.0"
authors = ["Fluence Labs"]
edition = "2018"
edition = "2021"
publish = false
[[bin]]

View File

@ -4,11 +4,13 @@ description = "Fluence Application Service"
version = "0.24.0"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[dependencies]
marine-runtime = { path = "../../marine", version = "0.25.0" }
marine-min-it-version = { path = "../../crates/min-it-version", version = "0.2.1" }
marine-wasm-backend-traits = {path = "../wasm-backend-traits", version = "0.1.0"}
marine-wasmtime-backend = { path = "../wasmtime-backend", version = "0.1.0"}
maplit = "1.0.2"
log = "0.4.17"
@ -16,7 +18,6 @@ serde = "1.0.147"
serde_derive = "1.0.147"
serde_json = "1.0.89"
toml = "0.5.9"
wasmer-wasi = { package = "wasmer-wasi-fl", version = "0.17.1" }
[features]
raw-module-api = ["marine-runtime/raw-module-api"]

View File

@ -15,6 +15,7 @@
*/
use marine::MarineConfig;
use std::path::PathBuf;
/// Describes behaviour of the Fluence AppService.

View File

@ -19,6 +19,7 @@ use crate::AppServiceError;
use crate::config::AppServiceConfig;
use marine::TomlMarineConfig;
use serde_derive::Serialize;
use serde_derive::Deserialize;

View File

@ -20,9 +20,12 @@ use crate::MemoryStats;
use crate::service_interface::ServiceInterface;
use super::AppServiceError;
#[cfg(feature = "raw-module-api")]
use marine_wasm_backend_traits::WasiState;
use marine::Marine;
use marine::MarineModuleConfig;
use marine::IValue;
use serde_json::Value as JValue;
use maplit::hashmap;
@ -44,8 +47,8 @@ impl AppService {
/// Create Service with given modules and service id.
pub fn new<C, S>(config: C, service_id: S, envs: HashMap<Vec<u8>, Vec<u8>>) -> Result<Self>
where
S: Into<String>,
C: TryInto<AppServiceConfig>,
S: Into<String>,
AppServiceError: From<C::Error>,
{
let mut config: AppServiceConfig = config.try_into()?;
@ -222,7 +225,7 @@ impl AppService {
pub fn load_module<C, S>(&mut self, name: S, wasm_bytes: &[u8], config: Option<C>) -> Result<()>
where
S: Into<String>,
C: TryInto<crate::MarineModuleConfig>,
C: TryInto<marine::MarineModuleConfig>,
marine::MarineError: From<C::Error>,
{
self.marine
@ -243,7 +246,7 @@ impl AppService {
pub fn get_wasi_state(
&mut self,
module_name: impl AsRef<str>,
) -> Result<&wasmer_wasi::state::WasiState> {
) -> Result<Box<dyn WasiState + '_>> {
self.marine
.module_wasi_state(module_name)
.map_err(Into::into)

View File

@ -22,7 +22,7 @@ use marine::itype_text_view;
use serde::Serialize;
use std::rc::Rc;
use std::sync::Arc;
#[derive(Serialize)]
pub struct FunctionSignature {
@ -91,7 +91,7 @@ fn serialize_function_signature(
fn serialize_record_type(
id: u64,
record: Rc<IRecordType>,
record: Arc<IRecordType>,
record_types: &MRecordTypes,
) -> RecordType {
let fields = record

View File

@ -4,7 +4,7 @@ description = "Fluence Marine interface types generator"
version = "0.9.5"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "marine_it_generator"
@ -14,8 +14,8 @@ path = "src/lib.rs"
marine-it-parser = { path = "../it-parser", version = "0.11.1" }
marine-macro-impl = "0.7.1"
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1"}
it-lilo = "0.4.1"
wasmer-it = { workspace = true }
it-lilo = { workspace = true}
thiserror = "1.0.37"
walrus = "0.19.0"

View File

@ -19,7 +19,7 @@ use wasmer_it::IType;
use wasmer_it::ast::FunctionArg as IFunctionArg;
use once_cell::sync::Lazy;
use std::rc::Rc;
use std::sync::Arc;
pub(crate) struct ApiExportFuncDescriptor {
pub(crate) name: &'static str,
@ -31,8 +31,8 @@ pub(crate) struct ApiExportFuncDescriptor {
impl ApiExportFuncDescriptor {
pub fn update_interfaces(&self, interfaces: &mut Interfaces<'_>) {
let func_type = wasmer_it::ast::Type::Function {
arguments: Rc::new(self.arguments.clone()),
output_types: Rc::new(self.output_types.clone()),
arguments: Arc::new(self.arguments.clone()),
output_types: Arc::new(self.output_types.clone()),
};
interfaces.types.push(func_type);

View File

@ -27,7 +27,7 @@ use wasmer_it::IType;
use wasmer_it::ast::Interfaces;
use wasmer_it::IRecordType;
use std::rc::Rc;
use std::sync::Arc;
#[derive(PartialEq, Eq, Debug, Default)]
pub(crate) struct ITResolver<'a> {
@ -58,7 +58,7 @@ impl<'a> ITResolver<'a> {
self.types.insert(record_name.to_string(), new_type_id);
self.interfaces
.types
.push(Type::Record(Rc::new(IRecordType::default())));
.push(Type::Record(Arc::new(IRecordType::default())));
self.unresolved_types_count += 1;
new_type_id
@ -87,14 +87,14 @@ impl<'a> ITResolver<'a> {
match self.types.get(&record.name) {
Some(pos) => {
self.interfaces.types[*pos] = Type::Record(Rc::new(record));
self.interfaces.types[*pos] = Type::Record(Arc::new(record));
self.unresolved_types_count -= 1;
}
None => {
self.types
.insert(record.name.clone(), self.interfaces.types.len());
self.interfaces.types.push(Type::Record(Rc::new(record)));
self.interfaces.types.push(Type::Record(Arc::new(record)));
}
}
}
@ -152,8 +152,8 @@ impl<'a> ITResolver<'a> {
pub(crate) fn add_fn_type(
&mut self,
arguments: Rc<Vec<wasmer_it::ast::FunctionArg>>,
output_types: Rc<Vec<IType>>,
arguments: Arc<Vec<wasmer_it::ast::FunctionArg>>,
output_types: Arc<Vec<IType>>,
) {
let fn_type = wasmer_it::ast::Type::Function {
arguments,

View File

@ -25,16 +25,16 @@ use wasmer_it::IType;
/// Generates IT instructions for a argument of an export function.
pub(super) trait ArgumentITGenerator {
fn generate_instructions_for_arg<'a>(
fn generate_instructions_for_arg(
&self,
arg_id: u32,
it_resolver: &mut ITResolver<'a>,
it_resolver: &mut ITResolver<'_>,
) -> Result<Vec<Instruction>>;
}
impl ArgumentITGenerator for ParsedType {
#[rustfmt::skip]
fn generate_instructions_for_arg<'a>(&self, index: u32, it_resolver: &mut ITResolver<'a>) -> Result<Vec<Instruction>> {
fn generate_instructions_for_arg(&self, index: u32, it_resolver: &mut ITResolver<'_>) -> Result<Vec<Instruction>> {
let instructions = match self {
ParsedType::Boolean(_) => vec![Instruction::ArgumentGet { index }, Instruction::I32FromBool],
ParsedType::I8(_) => vec![Instruction::ArgumentGet { index }, Instruction::I32FromS8],

View File

@ -25,15 +25,15 @@ use wasmer_it::IType;
/// Generates IT instructions for a output type of an export function.
pub(super) trait OutputITGenerator {
fn generate_instructions_for_output_type<'a>(
fn generate_instructions_for_output_type(
&self,
it_resolver: &mut ITResolver<'a>,
it_resolver: &mut ITResolver<'_>,
) -> Result<Vec<Instruction>>;
}
impl OutputITGenerator for ParsedType {
#[rustfmt::skip]
fn generate_instructions_for_output_type<'a>(&self, it_resolver: &mut ITResolver<'a>) -> Result<Vec<Instruction>> {
fn generate_instructions_for_output_type(&self, it_resolver: &mut ITResolver<'_>) -> Result<Vec<Instruction>> {
let instructions = match self {
ParsedType::Boolean(_) => vec![Instruction::BoolFromI32],
ParsedType::I8(_) => vec![Instruction::S8FromI32],

View File

@ -33,7 +33,7 @@ use wasmer_it::ast::FunctionArg as IFunctionArg;
use wasmer_it::interpreter::Instruction;
use wasmer_it::IType;
use std::rc::Rc;
use std::sync::Arc;
const HOST_NAMESPACE_NAME: &str = "host";
@ -148,17 +148,17 @@ fn generate_it_instructions<'f>(
Ok(())
}
pub(crate) fn generate_raw_args(signature: &FnSignature) -> Rc<Vec<IFunctionArg>> {
pub(crate) fn generate_raw_args(signature: &FnSignature) -> Arc<Vec<IFunctionArg>> {
let raw_inputs = signature
.arguments
.iter()
.flat_map(to_raw_input_types)
.collect::<Vec<_>>();
Rc::new(raw_inputs)
Arc::new(raw_inputs)
}
pub(crate) fn generate_raw_output_type(signature: &FnSignature) -> Rc<Vec<IType>> {
pub(crate) fn generate_raw_output_type(signature: &FnSignature) -> Arc<Vec<IType>> {
let raw_outputs = signature
.output_types
.iter()
@ -170,7 +170,7 @@ pub(crate) fn generate_raw_output_type(signature: &FnSignature) -> Rc<Vec<IType>
})
.collect::<Vec<_>>();
Rc::new(raw_outputs)
Arc::new(raw_outputs)
}
use marine_macro_impl::RustType;

View File

@ -24,19 +24,19 @@ use wasmer_it::IType;
/// Generate IT instructions for a foreign mod.
pub(super) trait ArgumentITGenerator {
fn generate_instructions_for_arg<'a>(
fn generate_instructions_for_arg(
&self,
arg_id: u32,
it_resolver: &mut ITResolver<'a>,
it_resolver: &mut ITResolver<'_>,
) -> Result<(Vec<Instruction>, u32)>;
}
#[rustfmt::skip]
impl ArgumentITGenerator for ParsedType {
fn generate_instructions_for_arg<'a>(
fn generate_instructions_for_arg(
&self,
index: u32,
it_resolver: &mut ITResolver<'a>,
it_resolver: &mut ITResolver<'_>,
) -> Result<(Vec<Instruction>, u32)> {
let instructions = match self {
ParsedType::Boolean(_) => (vec![Instruction::ArgumentGet { index }, Instruction::BoolFromI32], 1),

View File

@ -25,16 +25,16 @@ use wasmer_it::IType;
/// Generate IT instructions for a foreign mod.
pub(super) trait OutputITGenerator {
fn generate_instructions_for_output_type<'a>(
fn generate_instructions_for_output_type(
&self,
it_resolver: &mut ITResolver<'a>,
it_resolver: &mut ITResolver<'_>,
) -> Result<Vec<Instruction>>;
}
#[rustfmt::skip]
impl OutputITGenerator for ParsedType {
#[rustfmt::skip]
fn generate_instructions_for_output_type<'a>(&self, it_resolver: &mut ITResolver<'a>) -> Result<Vec<Instruction>> {
fn generate_instructions_for_output_type(&self, it_resolver: &mut ITResolver<'_>) -> Result<Vec<Instruction>> {
let instructions = match self {
ParsedType::Boolean(_) => vec![Instruction::I32FromBool],
ParsedType::I8(_) => vec![Instruction::I32FromS8],

View File

@ -23,7 +23,7 @@ use marine_macro_impl::ParsedType;
use marine_macro_impl::RustType;
use wasmer_it::ast::FunctionArg as IFunctionArg;
use std::rc::Rc;
use std::sync::Arc;
// return error if there is no record with such name
pub(crate) fn ptype_to_itype_checked(
@ -107,10 +107,10 @@ pub(crate) fn wtype_to_itype(pty: &RustType) -> IType {
}
}
pub(crate) fn generate_it_args<'f>(
pub(crate) fn generate_it_args(
signature: &FnSignature,
it_resolver: &mut ITResolver<'f>,
) -> Result<Rc<Vec<IFunctionArg>>> {
it_resolver: &mut ITResolver<'_>,
) -> Result<Arc<Vec<IFunctionArg>>> {
let arguments = signature
.arguments
.iter()
@ -122,21 +122,21 @@ pub(crate) fn generate_it_args<'f>(
})
.collect::<Result<Vec<_>>>()?;
let arguments = Rc::new(arguments);
let arguments = Arc::new(arguments);
Ok(arguments)
}
pub(crate) fn generate_it_output_type<'f>(
pub(crate) fn generate_it_output_type(
signature: &FnSignature,
it_resolver: &mut ITResolver<'f>,
) -> Result<Rc<Vec<IType>>> {
it_resolver: &mut ITResolver<'_>,
) -> Result<Arc<Vec<IType>>> {
let output_types = signature
.output_types
.iter()
.map(|ty| ptype_to_itype_checked(ty, it_resolver))
.collect::<Result<Vec<_>>>()?;
let output_types = Rc::new(output_types);
let output_types = Arc::new(output_types);
Ok(output_types)
}

View File

@ -4,12 +4,12 @@ description = "Fluence Marine interface types helper crate"
version = "0.7.3"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "marine_it_interfaces"
path = "src/lib.rs"
[dependencies]
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1"}
wasmer-it = { workspace = true}
multimap = "0.8.3"

View File

@ -23,7 +23,7 @@ use multimap::MultiMap;
use std::iter::Iterator;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
pub type CoreFunctionType = u32;
pub type AdapterFunctionType = u32;
@ -111,7 +111,7 @@ impl<'a> MITInterfaces<'a> {
self.types.iter()
}
pub fn record_types(&self) -> impl Iterator<Item = (u64, &Rc<IRecordType>)> {
pub fn record_types(&self) -> impl Iterator<Item = (u64, &Arc<IRecordType>)> {
self.types.iter().enumerate().filter_map(|(id, t)| match t {
ITAstType::Record(r) => Some((id as u64, r)),
_ => None,

View File

@ -4,14 +4,14 @@ description = "Fluence Marine interface-types serde tools"
version = "0.3.5"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "it_json_serde"
path = "src/lib.rs"
[dependencies]
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1"}
wasmer-it = { workspace = true}
serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.89"

View File

@ -33,9 +33,9 @@ pub use ivalues_to_json::ivalues_to_json;
pub use json_to_ivalues::json_to_ivalues;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
pub(crate) use wasmer_it::IValue;
pub(crate) use wasmer_it::IType;
pub(crate) use wasmer_it::IRecordType;
pub(crate) type MRecordTypes = HashMap<u64, Rc<IRecordType>>;
pub(crate) type MRecordTypes = HashMap<u64, Arc<IRecordType>>;

View File

@ -4,7 +4,7 @@ description = "Fluence Marine interface types parser"
version = "0.11.1"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "marine_it_parser"
@ -13,12 +13,12 @@ path = "src/lib.rs"
[dependencies]
marine-it-interfaces = { path = "../it-interfaces", version = "0.7.3" }
marine-module-interface = { path = "../module-interface", version = "0.6.1" }
marine-wasm-backend-traits = { path = "../wasm-backend-traits", version = "0.1.0"}
anyhow = "1.0.66"
walrus = "0.19.0"
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1"}
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1" }
nom = "5.1"
wasmer-it = { workspace = true }
nom = "7.1"
itertools = "0.10.5"
semver = "1.0.14"

View File

@ -27,7 +27,7 @@ pub fn delete_it_section_from_file(
out_wasm_path: PathBuf,
) -> Result<(), ITParserError> {
let module = ModuleConfig::new()
.parse_file(&in_wasm_path)
.parse_file(in_wasm_path)
.map_err(ITParserError::CorruptedWasmFile)?;
let mut module = delete_it_section(module);

View File

@ -19,10 +19,9 @@ use super::errors::ITParserError;
use crate::ParserResult;
use walrus::ModuleConfig;
use wasmer_it::{
ast::Interfaces,
decoders::wat::{parse, Buffer},
};
use wasmer_it::ast::Interfaces;
use wasmer_it::decoders::wat::parse;
use wasmer_it::decoders::wat::Buffer;
use wasmer_it::ToBytes;
use std::path::Path;

View File

@ -45,15 +45,15 @@ pub enum ITParserError {
CorruptedITSection(nom::Err<(Vec<u8>, nom::error::ErrorKind)>),
/// An error related to incorrect data in IT section.
#[error("{0}")]
IncorrectITFormat(String),
#[error("0")]
IncorrectITFormat(String), // TODO: use a proper error type
/// An error occurred while processing module interface.
#[error("{0}")]
#[error(transparent)]
ModuleInterfaceError(#[from] InterfaceError),
/// An error occurred while processing module IT interface.
#[error("{0}")]
#[error(transparent)]
ModuleITInterfaceError(#[from] ITInterfaceError),
/// An error occurred while parsing file in Wat format.

View File

@ -20,7 +20,8 @@ use crate::ParserResult;
use walrus::IdsToIndices;
use wasmer_it::ast::Interfaces;
use wasmer_core::Module as WasmerModule;
use marine_wasm_backend_traits::WasmBackend;
use marine_wasm_backend_traits::Module as WasmModule;
use std::borrow::Cow;
use std::path::Path;
@ -42,16 +43,18 @@ where
}
/// Extracts IT section of provided Wasm binary and converts it to a MITInterfaces.
pub fn extract_it_from_module(wasmer_module: &WasmerModule) -> ParserResult<Interfaces<'_>> {
let wit_sections = wasmer_module
.custom_sections(IT_SECTION_NAME)
.ok_or(ITParserError::NoITSection)?;
pub fn extract_it_from_module<WB: WasmBackend>(
wasm_module: &<WB as WasmBackend>::Module,
) -> ParserResult<Interfaces<'_>> {
let wit_sections = wasm_module.custom_sections(IT_SECTION_NAME);
if wit_sections.len() > 1 {
return Err(ITParserError::MultipleITSections);
}
let it_section = match wit_sections.len() {
0 => Err(ITParserError::NoITSection),
1 => Ok(&wit_sections[0]),
_ => Err(ITParserError::MultipleITSections),
}?;
extract_it_from_bytes(&wit_sections[0])
extract_it_from_bytes(it_section)
}
pub fn extract_version_from_module(module: &walrus::Module) -> ParserResult<semver::Version> {

View File

@ -4,7 +4,7 @@ version = "0.2.1"
description = "Fluence Marine interface types minimum supported version checker"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "marine_min_it_version"

View File

@ -4,7 +4,7 @@ description = "Fluence Marine Wasm module info (manifest and version) parser"
version = "0.5.1"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "marine_module_info_parser"
@ -13,7 +13,7 @@ path = "src/lib.rs"
[dependencies]
marine-rs-sdk-main = "0.7.1"
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1" }
marine-wasm-backend-traits = { path = "../wasm-backend-traits", version = "0.1.0"}
anyhow = "1.0.66"
chrono = "0.4.23"

View File

@ -30,11 +30,11 @@ pub enum ModuleInfoError {
MultipleCustomSections(&'static str, usize),
/// Errors related to corrupted version.
#[error("{0}")]
#[error(transparent)]
VersionError(#[from] SDKVersionError),
/// Errors related to corrupted manifest.
#[error("{0}")]
#[error(transparent)]
ManifestError(#[from] ManifestError),
/// An error occurred while parsing Wasm file.

View File

@ -20,7 +20,9 @@ use crate::ModuleInfoError;
use crate::extract_custom_sections_by_name;
use crate::try_as_one_section;
use wasmer_core::Module as WasmerModule;
use marine_wasm_backend_traits::Module as ModuleTrait;
use marine_wasm_backend_traits::WasmBackend;
use marine_rs_sdk_main::MANIFEST_SECTION_NAME;
use walrus::ModuleConfig;
use walrus::Module;
@ -29,7 +31,7 @@ use std::borrow::Cow;
use std::path::Path;
use std::convert::TryInto;
pub fn extract_from_path<P>(wasm_module_path: P) -> ModuleInfoResult<Option<ModuleManifest>>
pub fn extract_from_path<P>(wasm_module_path: P) -> ModuleInfoResult<ModuleManifest>
where
P: AsRef<Path>,
{
@ -40,12 +42,8 @@ where
extract_from_module(&module)
}
pub fn extract_from_module(wasm_module: &Module) -> ModuleInfoResult<Option<ModuleManifest>> {
pub fn extract_from_module(wasm_module: &Module) -> ModuleInfoResult<ModuleManifest> {
let sections = extract_custom_sections_by_name(wasm_module, MANIFEST_SECTION_NAME)?;
if sections.is_empty() {
return Ok(None);
}
let section = try_as_one_section(&sections, MANIFEST_SECTION_NAME)?;
let manifest = match section {
@ -53,20 +51,15 @@ pub fn extract_from_module(wasm_module: &Module) -> ModuleInfoResult<Option<Modu
Cow::Owned(vec) => vec.as_slice().try_into(),
}?;
Ok(Some(manifest))
Ok(manifest)
}
pub fn extract_from_wasmer_module(
wasmer_module: &WasmerModule,
) -> ModuleInfoResult<Option<ModuleManifest>> {
let sections = wasmer_module.custom_sections(MANIFEST_SECTION_NAME);
let sections = match sections {
Some(sections) => sections,
None => return Ok(None),
};
pub fn extract_from_compiled_module<WB: WasmBackend>(
module: &<WB as WasmBackend>::Module,
) -> ModuleInfoResult<ModuleManifest> {
let sections = module.custom_sections(MANIFEST_SECTION_NAME);
let section = try_as_one_section(sections, MANIFEST_SECTION_NAME)?;
let manifest = section.as_slice().try_into()?;
Ok(Some(manifest))
Ok(manifest)
}

View File

@ -23,5 +23,5 @@ mod tests;
pub use errors::ManifestError;
pub use manifest_extractor::extract_from_path;
pub use manifest_extractor::extract_from_module;
pub use manifest_extractor::extract_from_wasmer_module;
pub use manifest_extractor::extract_from_compiled_module;
pub use module_manifest::ModuleManifest;

View File

@ -109,7 +109,7 @@ fn test_too_big_field_len() {
array.add_utf8_field("repository");
let actual: Result<ModuleManifest, _> = array.as_bytes().try_into();
let expected = Err(ManifestError::TooBigFieldSize("version", incorrect_size));
let expected: Result<_, _> = Err(ManifestError::TooBigFieldSize("version", incorrect_size));
assert_eq!(actual, expected);
}
@ -123,7 +123,7 @@ fn test_without_one_field() {
array.add_utf8_field("description");
let actual: Result<ModuleManifest, _> = array.as_bytes().try_into();
let expected = Err(ManifestError::NotEnoughBytesForPrefix("repository"));
let expected: Result<_, _> = Err(ManifestError::NotEnoughBytesForPrefix("repository"));
assert_eq!(actual, expected);
}
@ -131,7 +131,7 @@ fn test_without_one_field() {
#[test]
fn test_with_empty_slice() {
let actual: Result<ModuleManifest, _> = vec![].as_slice().try_into();
let expected = Err(ManifestError::NotEnoughBytesForPrefix("authors"));
let expected: Result<_, _> = Err(ManifestError::NotEnoughBytesForPrefix("authors"));
assert_eq!(actual, expected);
}
@ -148,7 +148,7 @@ fn test_not_enough_data_for_field() {
array.add_utf8_string("repository");
let actual: Result<ModuleManifest, _> = array.as_bytes().try_into();
let expected = Err(ManifestError::NotEnoughBytesForField(
let expected: Result<_, _> = Err(ManifestError::NotEnoughBytesForField(
"repository",
too_big_size as usize,
));

View File

@ -21,6 +21,6 @@ mod version_extractor;
pub use errors::SDKVersionError;
pub use version_extractor::extract_from_path;
pub use version_extractor::extract_from_module;
pub use version_extractor::extract_from_wasmer_module;
pub use version_extractor::extract_from_compiled_module;
pub use version_embedder::embed_from_path;
pub use version_embedder::embed_from_module;

View File

@ -20,7 +20,9 @@ use super::SDKVersionError;
use crate::extract_custom_sections_by_name;
use crate::try_as_one_section;
use wasmer_core::Module as WasmerModule;
use marine_wasm_backend_traits::WasmBackend;
use marine_wasm_backend_traits::Module as WasmModule;
use marine_rs_sdk_main::VERSION_SECTION_NAME;
use walrus::ModuleConfig;
use walrus::Module;
@ -29,7 +31,7 @@ use std::borrow::Cow;
use std::str::FromStr;
use std::path::Path;
pub fn extract_from_path<P>(wasm_module_path: P) -> ModuleInfoResult<Option<semver::Version>>
pub fn extract_from_path<P>(wasm_module_path: P) -> ModuleInfoResult<semver::Version>
where
P: AsRef<Path>,
{
@ -40,12 +42,8 @@ where
extract_from_module(&module)
}
pub fn extract_from_module(wasm_module: &Module) -> ModuleInfoResult<Option<semver::Version>> {
pub fn extract_from_module(wasm_module: &Module) -> ModuleInfoResult<semver::Version> {
let sections = extract_custom_sections_by_name(wasm_module, VERSION_SECTION_NAME)?;
if sections.is_empty() {
return Ok(None);
}
let section = try_as_one_section(&sections, VERSION_SECTION_NAME)?;
let version = match section {
@ -53,23 +51,17 @@ pub fn extract_from_module(wasm_module: &Module) -> ModuleInfoResult<Option<semv
Cow::Owned(vec) => as_semver(vec),
}?;
Ok(Some(version))
Ok(version)
}
pub fn extract_from_wasmer_module(
wasmer_module: &WasmerModule,
) -> ModuleInfoResult<Option<semver::Version>> {
let sections = wasmer_module.custom_sections(VERSION_SECTION_NAME);
let sections = match sections {
Some(sections) => sections,
None => return Ok(None),
};
pub fn extract_from_compiled_module<WB: WasmBackend>(
wasm_module: &<WB as WasmBackend>::Module,
) -> ModuleInfoResult<semver::Version> {
let sections = wasm_module.custom_sections(VERSION_SECTION_NAME);
let section = try_as_one_section(sections, VERSION_SECTION_NAME)?;
let version = as_semver(section)?;
Ok(Some(version))
Ok(version)
}
fn as_semver(version_as_bytes: &[u8]) -> Result<semver::Version, super::SDKVersionError> {

View File

@ -4,7 +4,7 @@ description = "Fluence Marine module interface"
version = "0.6.1"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "marine_module_interface"
@ -15,8 +15,8 @@ marine-it-interfaces = { path = "../it-interfaces", version = "0.7.3" }
anyhow = "1.0.66"
walrus = "0.19.0"
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1" }
nom = "5.1"
wasmer-it = { workspace = true }
nom = "7.1.3"
itertools = "0.10.5"
semver = "1.0.14"

View File

@ -22,6 +22,6 @@ pub enum InterfaceError {
#[error("record type with type id {0} not found")]
NotFoundRecordTypeId(u64),
#[error("{0}")]
#[error(transparent)]
ITInterfaceError(#[from] ITInterfaceError),
}

View File

@ -24,7 +24,7 @@ use wasmer_it::IRecordType;
use wasmer_it::IType;
use std::collections::HashSet;
use std::rc::Rc;
use std::sync::Arc;
use itertools::Itertools;
pub(crate) struct RecordsTransformer {
@ -60,7 +60,7 @@ impl RecordsTransformer {
fn dfs(
&mut self,
record_id: u64,
record: &Rc<IRecordType>,
record: &Arc<IRecordType>,
exported_records: &IRecordTypes,
) -> InterfaceResult<()> {
if !self.used.insert(record_id) {
@ -107,7 +107,7 @@ impl RecordsTransformer {
fn convert_record(
id: u64,
record: &Rc<IRecordType>,
record: &Arc<IRecordType>,
record_types: &IRecordTypes,
) -> RecordType {
use super::itype_text_view;

View File

@ -28,6 +28,6 @@ pub enum ITInterfaceError {
#[error("mailformed module: a record contains more recursion level then allowed")]
TooManyRecursionLevels,
#[error("{0}")]
#[error(transparent)]
MITInterfacesError(#[from] MITInterfacesError),
}

View File

@ -20,7 +20,7 @@ use super::RIResult;
use marine_it_interfaces::MITInterfaces;
use std::rc::Rc;
use std::sync::Arc;
pub struct ITExportFuncDescriptor<'n> {
pub adapter_function_type: u32,
@ -69,7 +69,7 @@ pub fn get_export_funcs(mit: &MITInterfaces<'_>) -> RIResult<Vec<IFunctionSignat
output_types,
} => {
let signature = IFunctionSignature {
name: Rc::new(descriptor.name.to_string()),
name: Arc::new(descriptor.name.to_string()),
arguments: arguments.clone(),
outputs: output_types.clone(),
adapter_function_type: descriptor.adapter_function_type,

View File

@ -22,16 +22,16 @@ use serde::Serialize;
use serde::Deserialize;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
pub type IRecordTypes = HashMap<u64, Rc<IRecordType>>;
pub type IRecordTypes = HashMap<u64, Arc<IRecordType>>;
/// Represent a function type inside Marine module.
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub struct IFunctionSignature {
pub name: Rc<String>,
pub arguments: Rc<Vec<IFunctionArg>>,
pub outputs: Rc<Vec<IType>>,
pub name: Arc<String>,
pub arguments: Arc<Vec<IFunctionArg>>,
pub outputs: Arc<Vec<IType>>,
pub adapter_function_type: u32,
}

View File

@ -4,7 +4,7 @@ description = "Fluence Marine utils crate"
version = "0.4.0"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "marine_utils"

View File

@ -29,10 +29,10 @@ mod wasm_mem_pages_conversion;
pub use wasm_mem_pages_conversion::*;
use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
pub struct SharedString(pub Rc<String>);
pub struct SharedString(pub Arc<String>);
impl std::borrow::Borrow<str> for SharedString {
fn borrow(&self) -> &str {

View File

@ -0,0 +1,16 @@
[package]
name = "marine-wasm-backend-traits"
version = "0.1.0"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2021"
[dependencies]
wasmer-it = { workspace = true }
it-memory-traits = { workspace = true }
thiserror = "1.0.24"
anyhow = "1.0.68"
wasmparser = "0.101.1"
paste = "1.0.9"
multimap = "0.8.3"

View File

@ -0,0 +1,36 @@
/*
* Copyright 2023 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 crate::AsContextMut;
use crate::FuncGetter;
use crate::WasmBackend;
/// `Caller` is a structure used to pass context to imports.
/// It serves as a handle to `Store`, and also provides access to `Memory` and export functions
/// from the caller instance, if there is one.
pub trait Caller<WB: WasmBackend>:
FuncGetter<WB, (i32, i32), i32>
+ FuncGetter<WB, (i32, i32), ()>
+ FuncGetter<WB, i32, i32>
+ FuncGetter<WB, i32, ()>
+ FuncGetter<WB, (), i32>
+ FuncGetter<WB, (), ()>
+ AsContextMut<WB>
{
/// Gets the `Memory` from the caller instance.
/// Returns `None` if function was called directly from host.
fn memory(&mut self, memory_index: u32) -> Option<<WB as WasmBackend>::Memory>;
}

View File

@ -0,0 +1,145 @@
/*
* Copyright 2023 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 crate::WType;
use thiserror::Error;
pub type WasmBackendResult<T> = Result<T, WasmBackendError>;
pub type ResolveResult<T> = Result<T, ResolveError>;
pub type RuntimeResult<T> = Result<T, RuntimeError>;
pub type ModuleCreationResult<T> = Result<T, ModuleCreationError>;
pub type InstantiationResult<T> = Result<T, InstantiationError>;
pub type WasiResult<T> = Result<T, WasiError>;
/*
General error design goals:
* expose as much detail as possible
* make as much domain-specific errors as possible implementation-independent
So, Error enums should follow this principle:
* errors fully expressible without implementation info should have implementation-independent view
* errors not fully expressible without implementation info should have some common view and a way to get implmententation-specific details
* "Other" type for all errors not suited for listed options
*/
#[derive(Debug, Error)]
pub enum WasmBackendError {
#[error(transparent)]
ResolveError(#[from] ResolveError),
#[error(transparent)]
RuntimeError(#[from] RuntimeError),
#[error(transparent)]
ModuleCreationError(#[from] ModuleCreationError),
#[error(transparent)]
ImportError(#[from] ImportError),
#[error(transparent)]
InstantiationError(#[from] InstantiationError),
#[error(transparent)]
InitializationError(anyhow::Error),
}
#[derive(Debug, Error)]
pub enum ResolveError {
#[error("export not found: {0}")]
ExportNotFound(String),
#[error("export type mismatch: expected {expected}, found {actual}")]
ExportTypeMismatch {
expected: &'static str,
actual: &'static str,
},
#[error(transparent)]
Other(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum RuntimeError {
#[error("Unsupported type encountered: {0}")]
UnsupportedType(WType),
#[error(transparent)]
Trap(anyhow::Error),
#[error(transparent)]
UserError(#[from] UserError),
#[error("A function returned invalid number of results: expected {expected}, got {actual}")]
IncorrectResultsNumber { expected: usize, actual: usize },
#[error(transparent)]
Other(anyhow::Error),
}
#[derive(Debug, Error)]
pub enum ModuleCreationError {
#[error(transparent)]
FailedToCompileWasm(anyhow::Error),
#[error("{0}")]
FailedToExtractCustomSections(String), // TODO: use a proper error type
#[error(transparent)]
Other(anyhow::Error),
}
#[derive(Debug, Error)]
pub enum ImportError {
#[error("Duplicate import")]
DuplicateImport(String, String),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum InstantiationError {
#[error(transparent)]
RuntimeError(RuntimeError),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum WasiError {
#[error(transparent)]
IOError(#[from] std::io::Error),
#[error(transparent)]
EngineWasiError(#[from] anyhow::Error),
#[error("Cumulative size of args array exceeds 2^32")]
TooLargeArgsArray,
#[error("Cumulative size of envs array exceeds 2^32")]
TooLargeEnvsArray,
}
#[derive(Debug, Error)]
pub enum UserError {
#[error(transparent)]
Recoverable(anyhow::Error),
#[error(transparent)]
Unrecoverable(anyhow::Error),
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2023 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.
*/
pub static STANDARD_MEMORY_EXPORT_NAME: &str = "memory";
pub static STANDARD_MEMORY_INDEX: u32 = 0;
use crate::DelayedContextLifetime;
use crate::WasmBackend;
/// Contains Wasm exports necessary for internal usage.
pub enum Export<WB: WasmBackend> {
Memory(<WB as WasmBackend>::Memory),
Function(<WB as WasmBackend>::Function),
Other,
}
// TODO: add read/write/etc methods to the `Memory` trait,
// and then make a generic implementation of interface-types traits
/// A wasm memory handle.
/// As it is only a handle to an object in `Store`, cloning is cheap.
pub trait Memory<WB: WasmBackend>:
it_memory_traits::Memory<<WB as WasmBackend>::MemoryView, DelayedContextLifetime<WB>>
+ Clone
+ Send
+ Sync
+ 'static
{
/// Get the size of the allocated memory in bytes.
fn size(&self, store: &mut <WB as WasmBackend>::ContextMut<'_>) -> usize;
}

View File

@ -0,0 +1,169 @@
/*
* Copyright 2023 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 crate::AsContextMut;
use crate::FuncSig;
use crate::impl_for_each_function_signature;
use crate::RuntimeResult;
use crate::WasmBackend;
use crate::WValue;
/// A Wasm function handle, it can be either a function from a host or an export from an `Instance`.
/// As it is only a handle to an object in `Store`, cloning is cheap
pub trait Function<WB: WasmBackend>: Send + Sync {
/// Creates a new function with dynamic signature.
/// The signature check is performed at runtime.
fn new<F>(store: &mut impl AsContextMut<WB>, sig: FuncSig, func: F) -> Self
where
F: for<'c> Fn(&[WValue]) -> Vec<WValue> + Sync + Send + 'static;
/// Creates a new function with dynamic signature that needs a context.
fn new_with_caller<F>(store: &mut impl AsContextMut<WB>, sig: FuncSig, func: F) -> Self
where
F: for<'c> Fn(<WB as WasmBackend>::Caller<'c>, &[WValue]) -> Vec<WValue>
+ Sync
+ Send
+ 'static;
/// Creates a new function with static signature.
/// Requires less runtime checks when called.
fn new_typed<Params, Results, Env>(
store: &mut impl AsContextMut<WB>,
func: impl IntoFunc<WB, Params, Results, Env>,
) -> Self;
/// Returns the signature of the function.
/// The signature is constructed each time this function is called, so
/// it is not recommended to use this function extensively.
fn signature(&self, store: &mut impl AsContextMut<WB>) -> FuncSig;
/// Calls the wasm function.
/// # Panics:
/// If given a store different from the one that stores the function.
/// # Errors:
/// See `RuntimeError` documentation.
fn call(
&self,
store: &mut impl AsContextMut<WB>,
args: &[WValue],
) -> RuntimeResult<Vec<WValue>>;
}
/// A helper trait for creating a function with a static signature.
/// Should not be implemented by users.
/// Implemented for all functions that meet the following criteria:
/// * implement Send + Sync + 'static
/// * take or not take Caller as first parameter
/// * take from 0 to 16 i32 parameters
/// * return () or i32
pub trait IntoFunc<WB: WasmBackend, Params, Results, Env> {
fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::Function;
}
/// An indicator of using Caller argument.
pub struct WithEnv {}
/// An indicator of using Caller argument.
pub struct WithoutEnv {}
#[macro_export]
macro_rules! replace_with {
($from:ident -> $to:ident) => {
$to
};
}
macro_rules! impl_into_func {
($num:tt $($args:ident)*) => (paste::paste!{
#[allow(non_snake_case)]
impl<WB, F> IntoFunc<WB, ($(replace_with!($args -> i32),)*), (), WithoutEnv> for F
where
WB: WasmBackend,
F: Fn($(replace_with!($args -> i32),)*) + Send + Sync + 'static,
{
fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::Function {
<WB as WasmBackend>::Function:: [< new_typed_ $num >] (ctx.as_context_mut(), self)
}
}
#[allow(non_snake_case)]
impl<WB, F> IntoFunc<WB, ($(replace_with!($args -> i32),)*), (), WithEnv> for F
where
WB: WasmBackend,
F: Fn(<WB as WasmBackend>::Caller<'_>, $(replace_with!($args -> i32),)*) + Send + Sync + 'static,
{
fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::Function {
<WB as WasmBackend>::Function:: [< new_typed_with_env_ $num >] (ctx.as_context_mut(), self)
}
}
#[allow(non_snake_case)]
impl<WB, F> IntoFunc<WB, ($(replace_with!($args -> i32),)*), i32, WithoutEnv> for F
where
WB: WasmBackend,
F: Fn($(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static,
{
fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::Function {
<WB as WasmBackend>::Function:: [< new_typed_ $num _r >] (ctx.as_context_mut(), self)
}
}
#[allow(non_snake_case)]
impl<WB, F> IntoFunc<WB, ($(replace_with!($args -> i32),)*), i32, WithEnv> for F
where
WB: WasmBackend,
F: Fn(<WB as WasmBackend>::Caller<'_>, $(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static,
{
fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::Function {
<WB as WasmBackend>::Function:: [< new_typed_with_env_ $num _r >] (ctx.as_context_mut(), self)
}
}
});
}
impl_for_each_function_signature!(impl_into_func);
macro_rules! declare_func_construction {
($num:tt $($args:ident)*) => (paste::paste!{
#[allow(non_snake_case)]
fn [< new_typed_ $num >]<F>(ctx: <WB as WasmBackend>::ContextMut<'_>, func: F) -> <WB as WasmBackend>::Function
where F: Fn($(replace_with!($args -> i32),)*) + Send + Sync + 'static
{
let func = move |_: <WB as WasmBackend>::Caller<'_>, $($args,)*| { func($($args,)*)};
Self:: [< new_typed_with_env_ $num >] (ctx, func)
}
#[allow(non_snake_case)]
fn [< new_typed_with_env_ $num >]<F>(ctx: <WB as WasmBackend>::ContextMut<'_>, func: F) -> <WB as WasmBackend>::Function
where F: Fn(<WB as WasmBackend>::Caller<'_>, $(replace_with!($args -> i32),)*) + Send + Sync + 'static;
#[allow(non_snake_case)]
fn [< new_typed_ $num _r>]<F>(ctx: <WB as WasmBackend>::ContextMut<'_>, func: F) -> <WB as WasmBackend>::Function
where F: Fn($(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static
{
let func = move |_: <WB as WasmBackend>::Caller<'_>, $($args,)*| -> i32 { func($($args,)*)};
Self:: [< new_typed_with_env_ $num _r >] (ctx, func)
}
#[allow(non_snake_case)]
fn [< new_typed_with_env_ $num _r>]<F>(ctx: <WB as WasmBackend>::ContextMut<'_>, func: F) -> <WB as WasmBackend>::Function
where F: Fn(<WB as WasmBackend>::Caller<'_>, $(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static;
});
}
pub trait FuncConstructor<WB: WasmBackend> {
impl_for_each_function_signature!(declare_func_construction);
}

View File

@ -0,0 +1,24 @@
pub use multimap::MultiMap;
pub fn custom_sections(bytes: &[u8]) -> Result<MultiMap<String, Vec<u8>>, String> {
use wasmparser::Parser;
use wasmparser::Payload;
Parser::new(0)
.parse_all(bytes)
.filter_map(|payload| {
let payload = match payload {
Ok(payload) => payload,
Err(e) => return Some(Err(e.to_string())),
};
match payload {
Payload::CustomSection(reader) => {
let name = reader.name().to_string();
let data = reader.data().to_vec();
Some(Ok((name, data)))
}
_ => None,
}
})
.collect()
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2023 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 crate::errors::*;
use crate::AsContext;
use crate::WasmBackend;
use crate::WType;
use std::borrow::Cow;
/// A "Linker" object, that is used to match functions with module imports during instantiation.
/// Cloning is a cheap operation for this object. All clones refer to the same data in store.
pub trait Imports<WB: WasmBackend>: Clone {
/// Creates a new empty object.
fn new(store: &mut <WB as WasmBackend>::Store) -> Self;
/// Inserts a function with name `name` to the namespace `module`.
/// # Errors:
/// An error returned if such combination of `module` and `name` already has an associated function.
fn insert(
&mut self,
store: &impl AsContext<WB>,
module: impl Into<String>,
name: impl Into<String>,
func: <WB as WasmBackend>::Function,
) -> Result<(), ImportError>;
/// Inserts several named functions to the same namespace `module` at once, an equivalent to multiple calls of `insert`.
/// # Errors:
/// An error returned if such combination of `module` and `name` already has an associated function.
///
fn register<S, I>(
&mut self,
store: &impl AsContext<WB>,
name: S,
namespace: I,
) -> Result<(), ImportError>
where
S: Into<String>,
I: IntoIterator<Item = (String, <WB as WasmBackend>::Function)>;
}
/// A type representing function signature.
#[derive(Clone)]
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) -> &[WType] {
&self.params
}
pub fn returns(&self) -> &[WType] {
&self.returns
}
}
pub type FuncFromCaller<WB, Args, Rets> = Box<
dyn FnMut(&mut <WB as WasmBackend>::ContextMut<'_>, Args) -> RuntimeResult<Rets>
+ Sync
+ Send
+ 'static,
>;
pub trait FuncGetter<WB: WasmBackend, Args, Rets> {
/// Gets an export function from the calling instance.
fn get_func(&mut self, name: &str) -> ResolveResult<FuncFromCaller<WB, Args, Rets>>;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2023 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 crate::AsContextMut;
use crate::Export;
use crate::ResolveResult;
use crate::WasmBackend;
/// A handle to an instantiated Wasm module. Cloning is cheap.
pub trait Instance<WB: WasmBackend>: Clone {
/// Returns an `Iterator` to all exports of this instance.
fn export_iter<'a>(
&'a self,
store: <WB as WasmBackend>::ContextMut<'a>,
) -> Box<dyn Iterator<Item = (&'a str, Export<WB>)> + 'a>;
/// Returns nth exported memory, None if there is no nth memory.
/// No guaranties is known for memory order, but almost always a module has only one memory,
/// hence the only valid value for `memory_index` is 0.
fn get_nth_memory(
&self,
store: &mut impl AsContextMut<WB>,
memory_index: u32, // TODO: refactor memory indexing with enums
) -> Option<<WB as WasmBackend>::Memory>;
/// Returns a memory export with given name.
/// # Errors:
/// Returns an error if there is no export with such name, or it is not a memory.
fn get_memory(
&self,
store: &mut impl AsContextMut<WB>,
memory_name: &str,
) -> ResolveResult<<WB as WasmBackend>::Memory>;
/// Returns an exported function with the given name.
/// # Errors:
/// Returns an error if there is no export with such name, or it is not a function.
fn get_function(
&self,
store: &mut impl AsContextMut<WB>,
name: &str,
) -> ResolveResult<<WB as WasmBackend>::Function>;
}

View File

@ -0,0 +1,96 @@
/*
* Copyright 2023 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.
*/
pub mod errors;
pub mod exports;
pub mod imports;
pub mod store;
pub mod wasi;
pub mod wtype;
pub mod module;
pub mod instance;
pub mod caller;
pub mod function;
pub mod macros;
/// Helper functions for backend implementations.
pub mod impl_utils;
pub mod prelude {
pub use crate::errors::*;
pub use crate::exports::*;
pub use crate::imports::*;
pub use crate::store::*;
pub use crate::wasi::*;
pub use crate::wtype::*;
pub use crate::module::*;
pub use crate::instance::*;
pub use crate::caller::*;
pub use crate::function::*;
pub use crate::WasmBackend;
pub use crate::DelayedContextLifetime;
}
pub use prelude::*;
pub use macros::*;
use it_memory_traits::MemoryView;
use std::marker::PhantomData;
/// A core trait for any backend. It serves two purposes:
/// * handles initialization of the library if needed
/// * provides access to all public types -- like `mod` but for trait impls.
pub trait WasmBackend: Clone + Default + 'static {
/// A type that stores all the data, while most of the types are handles to data from `Store`.
type Store: Store<Self>;
/// A compiled, but not instantiated module.
type Module: Module<Self>;
/// An object that holds all the functions that are given to `Module` as imports.
type Imports: Imports<Self>; // maybe rename to Linker?
/// An instantiated module ready to be executed.
type Instance: Instance<Self>;
/// A temporary immutable handle to `Store`.
type Context<'c>: Context<Self>;
/// A temporary mutable handle to `Store`
type ContextMut<'c>: ContextMut<Self>;
/// A type that is used to pass context to imports.
type Caller<'c>: Caller<Self>;
/// A function contained in `Store`, either host one or from wasm.
type Function: Function<Self> + FuncConstructor<Self>;
/// A wasm memory.
type Memory: Memory<Self>;
/// A view to the wasm memory.
type MemoryView: MemoryView<DelayedContextLifetime<Self>>;
/// Type that provides all WASI-related APIs.
type Wasi: WasiImplementation<Self>;
/// Creates a new wasm backend with default configuration. In future, a configuration
/// may be passed as argument.
fn new() -> WasmBackendResult<Self>;
}
/// This struct is a helper, that allows passing `<WB as WasmBackend>::ContextMut` as template parameter,
/// but not specify a lifetime. Any local lifetime can be used instead.
pub struct DelayedContextLifetime<WB: WasmBackend> {
_data: PhantomData<WB>,
}
impl<WB: WasmBackend> it_memory_traits::Store for DelayedContextLifetime<WB> {
type ActualStore<'c> = <WB as WasmBackend>::ContextMut<'c>;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2023 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.
*/
/// Copypasted from Wasmtime.
/// A helper macros for implementing anything for 0-16 generic parameters.
/// The expected argument signature is this:
/// ```
/// macro_rules! example_arg {
/// ($num:tt $($args:ident)*) => ();
/// }
/// ```
/// https://github.com/bytecodealliance/wasmtime/blob/812b4b5229eac29b18b5c70a48536a514d73a8a6/crates/wasmtime/src/func.rs#LL242C14-L242C41
#[macro_export]
macro_rules! impl_for_each_function_signature {
($mac:ident) => {
$mac!(0);
$mac!(1 A1);
$mac!(2 A1 A2);
$mac!(3 A1 A2 A3);
$mac!(4 A1 A2 A3 A4);
$mac!(5 A1 A2 A3 A4 A5);
$mac!(6 A1 A2 A3 A4 A5 A6);
$mac!(7 A1 A2 A3 A4 A5 A6 A7);
$mac!(8 A1 A2 A3 A4 A5 A6 A7 A8);
$mac!(9 A1 A2 A3 A4 A5 A6 A7 A8 A9);
$mac!(10 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10);
$mac!(11 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11);
$mac!(12 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12);
$mac!(13 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13);
$mac!(14 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14);
$mac!(15 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15);
$mac!(16 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 A16);
};
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2023 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 crate::ModuleCreationResult;
use crate::InstantiationResult;
use crate::WasmBackend;
/// A handle to compiled wasm module.
pub trait Module<WB: WasmBackend>: Sized {
/// Compiles a wasm bytes into a module and extracts custom sections.
fn new(store: &mut <WB as WasmBackend>::Store, wasm: &[u8]) -> ModuleCreationResult<Self>;
/// Returns custom sections corresponding to `name`, empty slice if there is no sections.
fn custom_sections(&self, name: &str) -> &[Vec<u8>];
/// Instantiates module by allocating memory, VM state and linking imports with ones from `import` argument.
/// Does not call `_start` or `_initialize` functions.
///
/// # Panics:
///
/// If the `Store` given is not the same with `Store` used to create `Imports` and this object.
fn instantiate(
&self,
store: &mut <WB as WasmBackend>::Store,
imports: &<WB as WasmBackend>::Imports,
) -> InstantiationResult<<WB as WasmBackend>::Instance>;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2023 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 crate::WasmBackend;
/// `Store` is an object that stores modules, instances, functions memories and so on.
/// `Store` is grow-only: once something added, it will not be removed until Store is destroyed.
/// Some of the implementations can limit allocated resources.
/// For example, Wasmtime cannot have more than 10000 instances in one `Store`.
///
/// Most of the functions in this crate require a handle to `Store` to work.
pub trait Store<WB: WasmBackend>: AsContextMut<WB> {
fn new(backend: &WB) -> Self;
}
/// A temporary immutable handle to store
pub trait Context<WB: WasmBackend>: AsContext<WB> {}
/// A temporary mutable handle to store
pub trait ContextMut<WB: WasmBackend>: AsContextMut<WB> {}
pub trait AsContext<WB: WasmBackend> {
fn as_context(&self) -> <WB as WasmBackend>::Context<'_>;
}
pub trait AsContextMut<WB: WasmBackend>: AsContext<WB> {
fn as_context_mut(&mut self) -> <WB as WasmBackend>::ContextMut<'_>;
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2023 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 crate::WasiError;
use crate::WasmBackend;
use std::path::PathBuf;
use std::collections::HashMap;
use std::collections::HashSet;
/// A type that provides WASI functionality to the given Wasm backend.
pub trait WasiImplementation<WB: WasmBackend> {
/// Configures WASI state and adds WASI functions to the `imports` object.
/// # Errors:
/// Returns an error if failed to open a preopen directory/file.
fn register_in_linker(
store: &mut <WB as WasmBackend>::ContextMut<'_>,
linker: &mut <WB as WasmBackend>::Imports,
config: WasiParameters,
) -> Result<(), WasiError>;
/// Optional API for getting current WASI state.
/// Returns None if not supported by current backend.
fn get_wasi_state<'s>(
instance: &'s mut <WB as WasmBackend>::Instance,
) -> Box<dyn WasiState + 's>;
}
#[derive(Default)]
pub struct WasiParameters {
pub args: Vec<Vec<u8>>,
pub envs: HashMap<Vec<u8>, Vec<u8>>,
pub preopened_files: HashSet<PathBuf>,
pub mapped_dirs: HashMap<String, PathBuf>,
}
pub trait WasiState {
fn envs(&self) -> &[Vec<u8>];
}

View File

@ -0,0 +1,92 @@
/*
* Copyright 2023 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.
*/
#[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),
}
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, unsupported.
V128,
/// ExternRef type, unsupported.
ExternRef,
/// FuncRef type, unsupported.
FuncRef,
}
impl WType {
pub fn is_supported(&self) -> bool {
!matches!(self, Self::ExternRef | Self::FuncRef | Self::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,
}
}
}
impl std::fmt::Display for WType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}

View File

@ -0,0 +1,19 @@
[package]
name = "marine-wasmtime-backend"
version = "0.1.0"
edition = "2021"
authors = ["Fluence Labs"]
license = "Apache-2.0"
[dependencies]
marine-wasm-backend-traits = {path = "../wasm-backend-traits", version = "0.1.0"}
wasmer-it = { workspace = true }
it-memory-traits = { workspace = true }
# all default features except async
wasmtime = {version = "6.0.1", default-features = false, features = ["cache", "wat", "jitdump", "parallel-compilation", "cranelift", "pooling-allocator", "vtune"]}
wasmtime-wasi = "6.0.1"
multimap = "0.8.3"
paste = "1.0.9"
anyhow = "1.0.68"
log = "0.4.1"

View File

@ -0,0 +1,119 @@
/*
* Copyright 2023 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 crate::StoreState;
use crate::WasmtimeContext;
use crate::WasmtimeContextMut;
use crate::WasmtimeWasmBackend;
use crate::WasmtimeMemory;
use marine_wasm_backend_traits::prelude::*;
use wasmtime::AsContext as WasmtimeAsContext;
use wasmtime::AsContextMut as WasmtimeAsContextMut;
pub struct WasmtimeCaller<'c> {
pub(crate) inner: wasmtime::Caller<'c, StoreState>,
}
impl<'c> Caller<WasmtimeWasmBackend> for WasmtimeCaller<'c> {
fn memory(&mut self, _memory_index: u32) -> Option<WasmtimeMemory> {
let memory = self
.inner
.get_export(STANDARD_MEMORY_EXPORT_NAME)?
.into_memory()?;
Some(WasmtimeMemory::new(memory))
}
}
impl<'c> AsContext<WasmtimeWasmBackend> for WasmtimeCaller<'c> {
fn as_context(&self) -> WasmtimeContext<'_> {
WasmtimeContext {
inner: self.inner.as_context(),
}
}
}
impl<'c> AsContextMut<WasmtimeWasmBackend> for WasmtimeCaller<'c> {
fn as_context_mut(&mut self) -> <WasmtimeWasmBackend as WasmBackend>::ContextMut<'_> {
WasmtimeContextMut {
inner: self.inner.as_context_mut(),
}
}
}
/// Implements func_getter for given function signature.
/// Later `get_func` variant will be statically chosen based on types.
macro_rules! impl_func_getter {
($args:ty, $rets:ty) => {
impl<'c> FuncGetter<WasmtimeWasmBackend, $args, $rets> for WasmtimeCaller<'c> {
fn get_func(
&mut self,
name: &str,
) -> Result<
Box<
dyn FnMut(&mut WasmtimeContextMut<'_>, $args) -> Result<$rets, RuntimeError>
+ Sync
+ Send
+ 'static,
>,
ResolveError,
> {
let export = self
.inner
.get_export(name)
.ok_or(ResolveError::ExportNotFound(name.to_string()))?;
match export {
wasmtime::Extern::Func(f) => {
let f = f
.typed(&mut self.inner)
.map_err(|e| ResolveError::Other(e))?;
let closure = move |store: &mut WasmtimeContextMut<'_>, args| {
f.call(&mut store.inner, args).map_err(|e| {
if let Some(_) = e.downcast_ref::<wasmtime::Trap>() {
RuntimeError::Trap(e)
} else {
RuntimeError::Other(e)
}
})
};
Ok(Box::new(closure))
}
wasmtime::Extern::Memory(_) => Err(ResolveError::ExportTypeMismatch {
expected: "function",
actual: "memory",
}),
_ => Err(ResolveError::ExportTypeMismatch {
expected: "function",
actual: "neither memory nor function",
}),
}
}
}
};
}
// These signatures are sufficient for marine to work.
impl_func_getter!((i32, i32), i32);
impl_func_getter!((i32, i32), ());
impl_func_getter!(i32, i32);
impl_func_getter!(i32, ());
impl_func_getter!((), i32);
impl_func_getter!((), ());

View File

@ -0,0 +1,178 @@
/*
* Copyright 2023 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 crate::WasmtimeContextMut;
use crate::WasmtimeWasmBackend;
use crate::WasmtimeCaller;
use crate::val_to_wvalue;
use crate::StoreState;
use crate::sig_to_fn_ty;
use crate::wvalue_to_val;
use crate::utils::fn_ty_to_sig;
use crate::utils::inspect_call_error;
use marine_wasm_backend_traits::prelude::*;
use marine_wasm_backend_traits::impl_for_each_function_signature;
use marine_wasm_backend_traits::replace_with;
use anyhow::anyhow;
pub struct WasmtimeFunction {
pub(crate) inner: wasmtime::Func,
}
impl Function<WasmtimeWasmBackend> for WasmtimeFunction {
fn new<F>(store: &mut impl AsContextMut<WasmtimeWasmBackend>, sig: FuncSig, func: F) -> Self
where
F: for<'c> Fn(&[WValue]) -> Vec<WValue> + Sync + Send + 'static,
{
let ty = sig_to_fn_ty(&sig);
let func = move |_: wasmtime::Caller<'_, StoreState>,
args: &[wasmtime::Val],
results_out: &mut [wasmtime::Val]|
-> Result<(), anyhow::Error> {
let args = process_func_args(args).map_err(|e| anyhow!(e))?; // TODO move earlier
let results = func(&args);
process_func_results(&results, results_out).map_err(|e| anyhow!(e))
};
let func = wasmtime::Func::new(store.as_context_mut().inner, ty, func);
WasmtimeFunction { inner: func }
}
fn new_with_caller<F>(
store: &mut impl AsContextMut<WasmtimeWasmBackend>,
sig: FuncSig,
func: F,
) -> Self
where
F: for<'c> Fn(<WasmtimeWasmBackend as WasmBackend>::Caller<'c>, &[WValue]) -> Vec<WValue>
+ Sync
+ Send
+ 'static,
{
let ty = sig_to_fn_ty(&sig);
let func = move |caller: wasmtime::Caller<'_, StoreState>,
args: &[wasmtime::Val],
results_out: &mut [wasmtime::Val]|
-> Result<(), anyhow::Error> {
let caller = WasmtimeCaller { inner: caller };
let args = process_func_args(args).map_err(|e| anyhow!(e))?;
let results = func(caller, &args);
process_func_results(&results, results_out).map_err(|e| anyhow!(e))
};
let func = wasmtime::Func::new(store.as_context_mut().inner, ty, func);
WasmtimeFunction { inner: func }
}
fn new_typed<Params, Results, Env>(
store: &mut impl marine_wasm_backend_traits::AsContextMut<WasmtimeWasmBackend>,
func: impl IntoFunc<WasmtimeWasmBackend, Params, Results, Env>,
) -> Self {
func.into_func(store)
}
fn signature<'c>(&self, store: &mut impl AsContextMut<WasmtimeWasmBackend>) -> FuncSig {
let ty = self.inner.ty(store.as_context_mut());
fn_ty_to_sig(&ty)
}
fn call<'c>(
&self,
store: &mut impl AsContextMut<WasmtimeWasmBackend>,
args: &[WValue],
) -> RuntimeResult<Vec<WValue>> {
let args = args.iter().map(wvalue_to_val).collect::<Vec<_>>();
let results_count = self.inner.ty(store.as_context_mut()).results().len();
let mut results = vec![wasmtime::Val::null(); results_count];
self.inner
.call(store.as_context_mut().inner, &args, &mut results)
.map_err(inspect_call_error)?;
results
.iter()
.map(val_to_wvalue)
.collect::<Result<Vec<_>, _>>()
}
}
/// Generates a function that accepts a Fn with $num template parameters and turns it into WasmtimeFunction.
/// Needed to allow users to pass almost any function to `Function::new_typed` without worrying about signature.
macro_rules! impl_func_construction {
($num:tt $($args:ident)*) => (paste::paste!{
fn [< new_typed_with_env_ $num >] <F>(mut ctx: WasmtimeContextMut<'_>, func: F) -> WasmtimeFunction
where F: Fn(WasmtimeCaller<'_>, $(replace_with!($args -> i32),)*) + Send + Sync + 'static {
let func = move |caller: wasmtime::Caller<'_, StoreState>, $($args,)*| {
let caller = WasmtimeCaller {inner: caller};
func(caller, $($args,)*)
};
let func = wasmtime::Func::wrap(&mut ctx.inner, func);
WasmtimeFunction {
inner: func
}
}
fn [< new_typed_with_env_ $num _r>] <F>(mut ctx: WasmtimeContextMut<'_>, func: F) -> WasmtimeFunction
where F: Fn(WasmtimeCaller<'_>, $(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static {
let func = move |caller: wasmtime::Caller<'_, StoreState>, $($args,)*| -> i32{
let caller = WasmtimeCaller {inner: caller};
func(caller, $($args,)*)
};
let func = wasmtime::Func::wrap(&mut ctx.inner, func);
WasmtimeFunction {
inner: func
}
}
});
}
impl FuncConstructor<WasmtimeWasmBackend> for WasmtimeFunction {
impl_for_each_function_signature!(impl_func_construction);
}
fn process_func_args(args: &[wasmtime::Val]) -> RuntimeResult<Vec<WValue>> {
args.iter()
.map(val_to_wvalue)
.collect::<RuntimeResult<Vec<_>>>()
}
fn process_func_results(
results_in: &[WValue],
results_out: &mut [wasmtime::Val],
) -> RuntimeResult<()> {
if results_in.len() != results_out.len() {
return Err(RuntimeError::IncorrectResultsNumber {
expected: results_out.len(),
actual: results_in.len(),
});
}
for id in 0..results_in.len() {
results_out[id] = wvalue_to_val(&results_in[id]);
}
Ok(())
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2023 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 crate::StoreState;
use crate::WasmtimeFunction;
use crate::WasmtimeStore;
use crate::WasmtimeWasmBackend;
use marine_wasm_backend_traits::prelude::*;
#[derive(Clone)]
pub struct WasmtimeImports {
pub(crate) linker: wasmtime::Linker<StoreState>,
}
impl Imports<WasmtimeWasmBackend> for WasmtimeImports {
fn new(store: &mut WasmtimeStore) -> Self {
Self {
linker: wasmtime::Linker::new(store.inner.engine()),
}
}
fn insert(
&mut self,
store: &impl AsContext<WasmtimeWasmBackend>,
module: impl Into<String>,
name: impl Into<String>,
func: <WasmtimeWasmBackend as WasmBackend>::Function,
) -> Result<(), ImportError> {
let module = module.into();
let name = name.into();
self.linker
.define(store.as_context(), &module, &name, func.inner)
.map_err(|_| ImportError::DuplicateImport(module, name))
.map(|_| ())
}
fn register<S, I>(
&mut self,
store: &impl AsContext<WasmtimeWasmBackend>,
name: S,
namespace: I,
) -> Result<(), ImportError>
where
S: Into<String>,
I: IntoIterator<Item = (String, WasmtimeFunction)>,
{
let module: String = name.into();
for (name, func) in namespace {
self.insert(store, &module, name, func)?;
}
Ok(())
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2023 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 crate::WasmtimeContextMut;
use crate::WasmtimeFunction;
use crate::WasmtimeMemory;
use crate::WasmtimeWasmBackend;
use marine_wasm_backend_traits::prelude::*;
#[derive(Clone)]
pub struct WasmtimeInstance {
pub(crate) inner: wasmtime::Instance,
}
impl Instance<WasmtimeWasmBackend> for WasmtimeInstance {
fn export_iter<'a>(
&'a self,
store: WasmtimeContextMut<'a>,
) -> Box<dyn Iterator<Item = (&'a str, Export<WasmtimeWasmBackend>)> + 'a> {
let exports = self.inner.exports(store.inner).map(|export| {
let name = export.name();
let export = match export.into_extern() {
wasmtime::Extern::Memory(memory) => Export::Memory(WasmtimeMemory::new(memory)),
wasmtime::Extern::Func(func) => Export::Function(WasmtimeFunction { inner: func }),
_ => Export::Other,
};
(name, export)
});
Box::new(exports)
}
fn get_nth_memory(
&self,
store: &mut impl AsContextMut<WasmtimeWasmBackend>,
memory_index: u32,
) -> Option<<WasmtimeWasmBackend as WasmBackend>::Memory> {
self.inner
.exports(&mut store.as_context_mut().inner)
.filter_map(wasmtime::Export::into_memory)
.nth(memory_index as usize)
.map(WasmtimeMemory::new)
}
fn get_memory(
&self,
store: &mut impl AsContextMut<WasmtimeWasmBackend>,
memory_name: &str,
) -> ResolveResult<<WasmtimeWasmBackend as WasmBackend>::Memory> {
self.inner
.get_export(&mut store.as_context_mut().inner, memory_name)
.ok_or_else(|| ResolveError::ExportNotFound(memory_name.to_string()))
.and_then(|e| {
e.into_memory().ok_or(ResolveError::ExportTypeMismatch {
expected: "memory",
actual: "other",
})
})
.map(WasmtimeMemory::new)
}
fn get_function(
&self,
store: &mut impl AsContextMut<WasmtimeWasmBackend>,
name: &str,
) -> ResolveResult<<WasmtimeWasmBackend as WasmBackend>::Function> {
let func = self
.inner
.get_export(&mut store.as_context_mut().inner, name)
.ok_or_else(|| ResolveError::ExportNotFound(name.to_owned()))
.and_then(|e| {
e.into_func().ok_or(ResolveError::ExportTypeMismatch {
expected: "function",
actual: "other",
})
})?;
Ok(WasmtimeFunction { inner: func })
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2023 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.
*/
mod caller;
mod store;
mod utils;
mod module;
mod instance;
mod wasi;
mod function;
mod imports;
mod memory;
use store::*;
use caller::*;
use module::*;
use instance::*;
use wasi::*;
use function::*;
use memory::*;
use imports::*;
use utils::*;
use marine_wasm_backend_traits::prelude::*;
use wasmtime_wasi::WasiCtx;
#[derive(Clone, Default)]
pub struct WasmtimeWasmBackend {
engine: wasmtime::Engine,
}
impl WasmBackend for WasmtimeWasmBackend {
type Store = WasmtimeStore;
type Module = WasmtimeModule;
type Imports = WasmtimeImports;
type Instance = WasmtimeInstance;
type Context<'c> = WasmtimeContext<'c>;
type ContextMut<'c> = WasmtimeContextMut<'c>;
type Caller<'c> = WasmtimeCaller<'c>;
type Function = WasmtimeFunction;
type Memory = WasmtimeMemory;
type MemoryView = WasmtimeMemory;
type Wasi = WasmtimeWasi;
fn new() -> WasmBackendResult<Self> {
let mut config = wasmtime::Config::new();
config
.debug_info(false)
.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
let engine =
wasmtime::Engine::new(&config).map_err(WasmBackendError::InitializationError)?;
Ok(Self { engine })
}
}
#[derive(Default)]
pub struct StoreState {
wasi: Vec<WasiCtx>, // wasmtime store does not release memory until drop, so do we
}

View File

@ -0,0 +1,131 @@
/*
* Copyright 2023 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 crate::WasmtimeContextMut;
use crate::WasmtimeWasmBackend;
use marine_wasm_backend_traits::DelayedContextLifetime;
use marine_wasm_backend_traits::Memory;
use it_memory_traits::MemoryAccessError;
static MEMORY_ACCESS_CONTRACT: &str = "api requires checking memory bounds before accessing memory";
#[derive(Clone)]
pub struct WasmtimeMemory {
memory: wasmtime::Memory,
}
impl WasmtimeMemory {
pub(crate) fn new(memory: wasmtime::Memory) -> Self {
Self { memory }
}
}
impl it_memory_traits::Memory<WasmtimeMemory, DelayedContextLifetime<WasmtimeWasmBackend>>
for WasmtimeMemory
{
// Wasmtime does not have the idea of MemoryView, while Wasmer has.
// And our interface-types implementation has MemoryView concept
// So, MemoryView in Wasmtime is just the memory.
fn view(&self) -> WasmtimeMemory {
self.clone()
}
}
impl Memory<WasmtimeWasmBackend> for WasmtimeMemory {
fn size(&self, store: &mut WasmtimeContextMut<'_>) -> usize {
self.memory.data_size(store)
}
}
impl it_memory_traits::MemoryReadable<DelayedContextLifetime<WasmtimeWasmBackend>>
for WasmtimeMemory
{
fn read_byte(&self, store: &mut WasmtimeContextMut<'_>, offset: u32) -> u8 {
let mut value = [0u8];
self.memory
.read(&mut store.inner, offset as usize, &mut value)
.expect(MEMORY_ACCESS_CONTRACT);
value[0]
}
fn read_array<const COUNT: usize>(
&self,
store: &mut WasmtimeContextMut<'_>,
offset: u32,
) -> [u8; COUNT] {
let mut value = [0u8; COUNT];
self.memory
.read(&mut store.inner, offset as usize, &mut value)
.expect(MEMORY_ACCESS_CONTRACT);
value
}
fn read_vec(&self, store: &mut WasmtimeContextMut<'_>, offset: u32, size: u32) -> Vec<u8> {
let mut value = vec![0u8; size as usize];
self.memory
.read(&mut store.inner, offset as usize, &mut value)
.expect(MEMORY_ACCESS_CONTRACT);
value
}
}
impl it_memory_traits::MemoryWritable<DelayedContextLifetime<WasmtimeWasmBackend>>
for WasmtimeMemory
{
fn write_byte(&self, store: &mut WasmtimeContextMut<'_>, offset: u32, value: u8) {
let buffer = [value];
self.memory
.write(&mut store.inner, offset as usize, &buffer)
.expect(MEMORY_ACCESS_CONTRACT);
}
fn write_bytes(&self, store: &mut WasmtimeContextMut<'_>, offset: u32, bytes: &[u8]) {
self.memory
.write(&mut store.inner, offset as usize, bytes)
.expect(MEMORY_ACCESS_CONTRACT);
}
}
impl it_memory_traits::MemoryView<DelayedContextLifetime<WasmtimeWasmBackend>> for WasmtimeMemory {
fn check_bounds(
&self,
store: &mut WasmtimeContextMut<'_>,
offset: u32,
size: u32,
) -> Result<(), MemoryAccessError> {
let memory_size = self.memory.data_size(&mut store.inner);
let final_size = offset
.checked_add(size)
.ok_or(MemoryAccessError::OutOfBounds {
offset,
size,
memory_size: memory_size as u32,
})? as usize;
if memory_size <= final_size {
Err(MemoryAccessError::OutOfBounds {
offset,
size,
memory_size: memory_size as u32, // TODO rewrite api when memory64 arrives
})
} else {
Ok(())
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2023 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 crate::WasmtimeImports;
use crate::WasmtimeInstance;
use crate::WasmtimeStore;
use crate::WasmtimeWasmBackend;
use crate::utils::inspect_instantiation_error;
use marine_wasm_backend_traits::prelude::*;
use marine_wasm_backend_traits::impl_utils::custom_sections;
use multimap::MultiMap;
pub struct WasmtimeModule {
pub(crate) custom_sections: MultiMap<String, Vec<u8>>,
pub(crate) inner: wasmtime::Module,
}
impl Module<WasmtimeWasmBackend> for WasmtimeModule {
fn new(store: &mut WasmtimeStore, wasm: &[u8]) -> ModuleCreationResult<Self> {
let module = wasmtime::Module::new(store.inner.engine(), wasm)
.map_err(ModuleCreationError::FailedToCompileWasm)?;
let custom_sections =
custom_sections(wasm) // TODO: avoid double module parsing
.map_err(ModuleCreationError::FailedToExtractCustomSections)?;
Ok(WasmtimeModule {
custom_sections,
inner: module,
})
}
fn custom_sections(&self, name: &str) -> &[Vec<u8>] {
self.custom_sections
.get_vec(name)
.map(|value| value.as_slice())
.unwrap_or_default()
}
fn instantiate(
&self,
store: &mut WasmtimeStore,
imports: &WasmtimeImports,
) -> InstantiationResult<<WasmtimeWasmBackend as WasmBackend>::Instance> {
// linker will not call _start, or _initialize unless Linker::module or Linker::module_async is used
let instance = imports
.linker
.instantiate(&mut store.inner, &self.inner)
.map_err(inspect_instantiation_error)?; // TODO add detail
Ok(WasmtimeInstance { inner: instance })
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2023 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 crate::StoreState;
use crate::WasmtimeWasmBackend;
use marine_wasm_backend_traits::prelude::*;
use wasmtime::StoreContext;
use wasmtime::StoreContextMut;
use wasmtime::AsContext as WasmtimeAsContext;
use wasmtime::AsContextMut as WasmtimeAsContextMut;
use std::default::Default;
/// A type that is used to store resources allocated by runtime. It includes memories, functions,
/// tables, globals and so on. More information here: https://webassembly.github.io/spec/core/exec/runtime.html#store.
/// Because of that, most of the methods in API require a handle to store to function.
pub struct WasmtimeStore {
pub(crate) inner: wasmtime::Store<StoreState>,
}
/// Temporary immutable handle to `Store`, used to interact with stored data.
pub struct WasmtimeContext<'s> {
pub(crate) inner: wasmtime::StoreContext<'s, StoreState>,
}
/// Temporary mutable handle to `Store`, used to interact with stored data.
pub struct WasmtimeContextMut<'s> {
pub(crate) inner: wasmtime::StoreContextMut<'s, StoreState>,
}
impl Store<WasmtimeWasmBackend> for WasmtimeStore {
fn new(backend: &WasmtimeWasmBackend) -> Self {
Self {
inner: wasmtime::Store::new(&backend.engine, <_>::default()),
}
}
}
impl<'c> Context<WasmtimeWasmBackend> for WasmtimeContext<'c> {}
impl<'c> ContextMut<WasmtimeWasmBackend> for WasmtimeContextMut<'c> {}
impl AsContext<WasmtimeWasmBackend> for WasmtimeStore {
fn as_context(&self) -> WasmtimeContext<'_> {
WasmtimeContext {
inner: self.inner.as_context(),
}
}
}
impl AsContextMut<WasmtimeWasmBackend> for WasmtimeStore {
fn as_context_mut(&mut self) -> WasmtimeContextMut<'_> {
WasmtimeContextMut {
inner: self.inner.as_context_mut(),
}
}
}
impl<'ctx> AsContext<WasmtimeWasmBackend> for WasmtimeContext<'ctx> {
fn as_context(&self) -> WasmtimeContext<'_> {
WasmtimeContext {
inner: self.inner.as_context(),
}
}
}
impl<'ctx> AsContext<WasmtimeWasmBackend> for WasmtimeContextMut<'ctx> {
fn as_context(&self) -> WasmtimeContext<'_> {
WasmtimeContext {
inner: self.inner.as_context(),
}
}
}
impl<'ctx> AsContextMut<WasmtimeWasmBackend> for WasmtimeContextMut<'ctx> {
fn as_context_mut(&mut self) -> WasmtimeContextMut<'_> {
WasmtimeContextMut {
inner: self.inner.as_context_mut(),
}
}
}
impl wasmtime::AsContext for WasmtimeStore {
type Data = StoreState;
fn as_context(&self) -> StoreContext<'_, Self::Data> {
self.inner.as_context()
}
}
impl wasmtime::AsContextMut for WasmtimeStore {
fn as_context_mut(&mut self) -> StoreContextMut<'_, Self::Data> {
self.inner.as_context_mut()
}
}
impl wasmtime::AsContext for WasmtimeContext<'_> {
type Data = StoreState;
fn as_context(&self) -> StoreContext<'_, Self::Data> {
self.inner.as_context()
}
}
impl wasmtime::AsContext for WasmtimeContextMut<'_> {
type Data = StoreState;
fn as_context(&self) -> StoreContext<'_, Self::Data> {
self.inner.as_context()
}
}
impl wasmtime::AsContextMut for WasmtimeContextMut<'_> {
fn as_context_mut(&mut self) -> StoreContextMut<'_, Self::Data> {
self.inner.as_context_mut()
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright 2023 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 marine_wasm_backend_traits::prelude::*;
use wasmtime::Val;
use wasmtime::ValType;
pub(crate) fn val_type_to_wtype(ty: &ValType) -> WType {
match ty {
ValType::I32 => WType::I32,
ValType::I64 => WType::I64,
ValType::F32 => WType::F32,
ValType::F64 => WType::F64,
ValType::V128 => WType::V128,
ValType::FuncRef => WType::FuncRef,
ValType::ExternRef => WType::ExternRef,
}
}
pub(crate) fn wtype_to_val_type(ty: &WType) -> ValType {
match ty {
WType::I32 => ValType::I32,
WType::I64 => ValType::I64,
WType::F32 => ValType::F32,
WType::F64 => ValType::F64,
WType::V128 => ValType::V128,
WType::FuncRef => ValType::FuncRef,
WType::ExternRef => ValType::ExternRef,
}
}
pub(crate) fn wvalue_to_val(value: &WValue) -> Val {
match value {
WValue::I32(value) => Val::I32(*value),
WValue::I64(value) => Val::I64(*value),
WValue::F32(value) => Val::F32(value.to_bits()),
WValue::F64(value) => Val::F64(value.to_bits()),
}
}
pub(crate) fn val_to_wvalue(value: &Val) -> RuntimeResult<WValue> {
match value {
Val::I32(value) => Ok(WValue::I32(*value)),
Val::I64(value) => Ok(WValue::I64(*value)),
Val::F32(value) => Ok(WValue::F32(f32::from_bits(*value))),
Val::F64(value) => Ok(WValue::F64(f64::from_bits(*value))),
Val::V128(_) => Err(RuntimeError::UnsupportedType(WType::V128)),
Val::FuncRef(_) => Err(RuntimeError::UnsupportedType(WType::V128)),
Val::ExternRef(_) => Err(RuntimeError::UnsupportedType(WType::V128)),
}
}
pub(crate) fn sig_to_fn_ty(sig: &FuncSig) -> wasmtime::FuncType {
let params = sig.params().iter().map(wtype_to_val_type);
let rets = sig.returns().iter().map(wtype_to_val_type);
wasmtime::FuncType::new(params, rets)
}
pub(crate) fn fn_ty_to_sig(ty: &wasmtime::FuncType) -> FuncSig {
let params = ty
.params()
.map(|ty| val_type_to_wtype(&ty))
.collect::<Vec<_>>();
let rets = ty
.results()
.map(|ty| val_type_to_wtype(&ty))
.collect::<Vec<_>>();
FuncSig::new(params, rets)
}
pub(crate) fn inspect_call_error(e: anyhow::Error) -> RuntimeError {
if e.downcast_ref::<wasmtime::Trap>().is_some() {
RuntimeError::Trap(e)
} else {
match e.downcast::<UserError>() {
Ok(e) => RuntimeError::UserError(e),
Err(e) => RuntimeError::Other(e),
}
}
}
pub(crate) fn inspect_instantiation_error(e: anyhow::Error) -> InstantiationError {
if e.downcast_ref::<wasmtime::Trap>().is_some() {
InstantiationError::RuntimeError(RuntimeError::Trap(e))
} else {
match e.downcast::<UserError>() {
Ok(e) => InstantiationError::RuntimeError(RuntimeError::UserError(e)),
Err(e) => InstantiationError::Other(e),
}
}
}

View File

@ -0,0 +1,159 @@
/*
* Copyright 2023 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 crate::StoreState;
use crate::WasmtimeContextMut;
use crate::WasmtimeImports;
use crate::WasmtimeWasmBackend;
use marine_wasm_backend_traits::prelude::*;
use wasmtime_wasi::ambient_authority;
use wasmtime_wasi::WasiCtxBuilder;
use anyhow::anyhow;
use std::path::Path;
use std::path::PathBuf;
use std::collections::HashMap;
use std::collections::HashSet;
pub struct WasmtimeWasi {}
impl WasiImplementation<WasmtimeWasmBackend> for WasmtimeWasi {
fn register_in_linker(
store: &mut WasmtimeContextMut<'_>,
linker: &mut WasmtimeImports,
parameters: WasiParameters,
) -> Result<(), WasiError> {
let WasiParameters {
args,
envs,
preopened_files,
mapped_dirs,
} = parameters;
let wasi_ctx_builder = WasiCtxBuilder::new();
// process and add CLI arguments to wasi context
let wasi_ctx_builder = populate_args(wasi_ctx_builder, args)?;
// process and add environment variables to wasi context
let wasi_ctx_builder = populate_envs(wasi_ctx_builder, envs)?;
// add preopened files to wasi context, do not create dirs
let wasi_ctx_builder = populate_preopens(wasi_ctx_builder, preopened_files)?;
// add mapped directories to wasi context , do not create dirs
let wasi_ctx_builder = populate_mapped_dirs(wasi_ctx_builder, mapped_dirs)?;
let wasi_ctx = wasi_ctx_builder.build();
add_wasi_to_linker(store, linker, wasi_ctx)
}
fn get_wasi_state<'s>(
_instance: &'s mut <WasmtimeWasmBackend as WasmBackend>::Instance,
) -> Box<dyn WasiState + 's> {
// TODO give actual state
Box::new(WasmtimeWasiState {})
}
}
pub struct WasmtimeWasiState {}
impl WasiState for WasmtimeWasiState {
fn envs(&self) -> &[Vec<u8>] {
&[]
}
}
fn add_wasi_to_linker(
store: &mut WasmtimeContextMut<'_>,
linker: &mut WasmtimeImports,
wasi_ctx: wasmtime_wasi::WasiCtx,
) -> Result<(), WasiError> {
// wasmtime-wasi gets its context from Caller<T>, which can hold any user info
// the only convenient method is to be provided with a closure that extracts context
// from used-defined type.
// So, here each module has its own wasi context which is stored in a vector in store.
let id = store.inner.data().wasi.len();
wasmtime_wasi::add_to_linker(&mut linker.linker, move |s: &mut StoreState| {
&mut s.wasi[id]
})
.map_err(|e| WasiError::EngineWasiError(anyhow!(e)))?;
store.inner.data_mut().wasi.push(wasi_ctx);
Ok(())
}
fn populate_args(builder: WasiCtxBuilder, args: Vec<Vec<u8>>) -> Result<WasiCtxBuilder, WasiError> {
let args = args
.into_iter()
.map(|arg| unsafe { String::from_utf8_unchecked(arg) })
.collect::<Vec<String>>();
builder
.args(&args)
.map_err(|_| WasiError::TooLargeArgsArray)
}
fn populate_preopens(
builder: WasiCtxBuilder,
preopened_files: HashSet<PathBuf>,
) -> Result<WasiCtxBuilder, WasiError> {
preopened_files
.iter()
.try_fold(builder, |builder, host_path| -> Result<_, WasiError> {
let guest_dir = wasmtime_wasi::Dir::open_ambient_dir(host_path, ambient_authority())?;
builder
.preopened_dir(guest_dir, host_path)
.map_err(|e| WasiError::EngineWasiError(anyhow!(e)))
})
}
fn populate_mapped_dirs(
builder: WasiCtxBuilder,
mapped_dirs: HashMap<String, PathBuf>,
) -> Result<WasiCtxBuilder, WasiError> {
mapped_dirs.iter().try_fold(
builder,
|builder, (guest_name, host_path)| -> Result<_, WasiError> {
let host_dir = wasmtime_wasi::Dir::open_ambient_dir(host_path, ambient_authority())?;
let guest_path = Path::new(&guest_name);
builder
.preopened_dir(host_dir, guest_path)
.map_err(|e| WasiError::EngineWasiError(anyhow!(e)))
},
)
}
fn populate_envs(
builder: WasiCtxBuilder,
envs: HashMap<Vec<u8>, Vec<u8>>,
) -> Result<WasiCtxBuilder, WasiError> {
let envs = envs
.into_iter()
.map(|(key, value)| {
unsafe {
// TODO maybe use strings in signature?
(
String::from_utf8_unchecked(key),
String::from_utf8_unchecked(value),
)
}
})
.collect::<Vec<_>>();
builder
.envs(&envs)
.map_err(|_| WasiError::TooLargeEnvsArray)
}

View File

@ -4,7 +4,7 @@ version = "0.1.0"
authors = ["Fluence Labs"]
description = "The greeting module for the Fluence network"
repository = "https://github.com/fluencelabs/marine/tree/master/examples/build_rs"
edition = "2018"
edition = "2021"
publish = false
[[bin]]

View File

@ -2,7 +2,7 @@
name = "call_parameters"
version = "0.1.0"
authors = ["Fluence Labs"]
edition = "2018"
edition = "2021"
publish = false
[[bin]]

View File

@ -43,7 +43,6 @@ pub fn call_parameters() -> String {
mod tests {
use marine_rs_sdk_test::marine_test;
use marine_rs_sdk_test::CallParameters;
use marine_rs_sdk_test::SecurityTetraplet;
#[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts")]
fn empty_string(call_parameters: marine_test_env::call_parameters::ModuleInterface) {
@ -53,9 +52,12 @@ mod tests {
let host_id = "host_id";
let particle_id = "particle_id";
let mut tetraplet = SecurityTetraplet::default();
tetraplet.function_name = "some_func_name".to_string();
tetraplet.json_path = "some_json_path".to_string();
let tetraplet = marine_rs_sdk::SecurityTetraplet {
function_name: "some_func_name".to_string(),
json_path: "some_json_path".to_string(),
..Default::default()
};
let tetraplets = vec![vec![tetraplet]];
let cp = CallParameters {

View File

@ -4,7 +4,7 @@ version = "0.1.0"
authors = ["Fluence Labs"]
description = "The failing module for the Fluence network"
repository = "https://github.com/fluencelabs/marine/tree/master/examples/failing"
edition = "2018"
edition = "2021"
publish = false
[[bin]]

View File

@ -4,7 +4,7 @@ version = "0.1.0"
authors = ["Fluence Labs"]
description = "The greeting module for the Fluence network"
repository = "https://github.com/fluencelabs/marine/tree/master/examples/greeting"
edition = "2018"
edition = "2021"
publish = false
[[bin]]

View File

@ -4,7 +4,7 @@ version = "0.1.0"
authors = ["Fluence Labs"]
description = "The greeting module with records for the Fluence network"
repository = "https://github.com/fluencelabs/marine/tree/master/examples/greeting_record"
edition = "2018"
edition = "2021"
publish = false
[[bin]]

Some files were not shown because too many files have changed in this diff Show More