mirror of
https://github.com/fluencelabs/marine.git
synced 2025-06-22 11:11:37 +00:00
feat!: decouple wasmer from Marine, replace it with generic backend interface (#219)
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
reorder_imports = false
|
reorder_imports = false
|
||||||
reorder_modules = false
|
reorder_modules = false
|
||||||
|
2422
Cargo.lock
generated
2422
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,8 @@ members = [
|
|||||||
"crates/min-it-version",
|
"crates/min-it-version",
|
||||||
"crates/module-info-parser",
|
"crates/module-info-parser",
|
||||||
"crates/module-interface",
|
"crates/module-interface",
|
||||||
|
"crates/wasm-backend-traits",
|
||||||
|
"crates/wasmtime-backend",
|
||||||
"crates/utils",
|
"crates/utils",
|
||||||
"examples/call_parameters",
|
"examples/call_parameters",
|
||||||
"examples/failing",
|
"examples/failing",
|
||||||
@ -40,6 +42,12 @@ members = [
|
|||||||
"tools/repl",
|
"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]
|
[profile.release]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
debug = false
|
debug = false
|
||||||
|
@ -4,7 +4,7 @@ description = "Core of Marine, the Fluence Wasm Runtime"
|
|||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "marine_core"
|
name = "marine_core"
|
||||||
@ -18,14 +18,12 @@ marine-it-generator = { path = "../crates/it-generator", version = "0.9.5" }
|
|||||||
marine-module-interface = { path = "../crates/module-interface", version = "0.6.1" }
|
marine-module-interface = { path = "../crates/module-interface", version = "0.6.1" }
|
||||||
marine-utils = { path = "../crates/utils", version = "0.4.0" }
|
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" }
|
wasmer-it = { workspace = true, default-features = false }
|
||||||
# dynamicfunc-fat-closures allows using state inside DynamicFunc
|
it-lilo = { workspace = true}
|
||||||
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1", features = ["dynamicfunc-fat-closures"] }
|
it-memory-traits = { workspace = true }
|
||||||
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"
|
|
||||||
bytesize = "1.1.0"
|
bytesize = "1.1.0"
|
||||||
|
|
||||||
multimap = "0.8.3"
|
multimap = "0.8.3"
|
||||||
|
@ -18,9 +18,8 @@ use super::IValue;
|
|||||||
use super::IType;
|
use super::IType;
|
||||||
use crate::HostImportError;
|
use crate::HostImportError;
|
||||||
|
|
||||||
use wasmer_wasi::WasiVersion;
|
use marine_wasm_backend_traits::WasiParameters;
|
||||||
use wasmer_runtime::ImportObject;
|
use marine_wasm_backend_traits::WasmBackend;
|
||||||
use wasmer_core::vm::Ctx;
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -29,12 +28,21 @@ use std::collections::HashSet;
|
|||||||
// 65536*1600 ~ 100 Mb (Wasm page size is 64 Kb)
|
// 65536*1600 ~ 100 Mb (Wasm page size is 64 Kb)
|
||||||
const DEFAULT_HEAP_PAGES_COUNT: u32 = 1600;
|
const DEFAULT_HEAP_PAGES_COUNT: u32 = 1600;
|
||||||
|
|
||||||
pub type HostExportedFunc = Box<dyn Fn(&mut Ctx, Vec<IValue>) -> Option<IValue> + 'static>;
|
pub type ErrorHandler =
|
||||||
pub type ErrorHandler = Option<Box<dyn Fn(&HostImportError) -> Option<IValue> + 'static>>;
|
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.
|
/// This closure will be invoked for corresponding import.
|
||||||
pub host_exported_func: HostExportedFunc,
|
pub host_exported_func: HostExportedFunc<WB>,
|
||||||
|
|
||||||
/// Type of the closure arguments.
|
/// Type of the closure arguments.
|
||||||
pub argument_types: Vec<IType>,
|
pub argument_types: Vec<IType>,
|
||||||
@ -47,41 +55,29 @@ pub struct HostImportDescriptor {
|
|||||||
pub error_handler: ErrorHandler,
|
pub error_handler: ErrorHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MModuleConfig {
|
pub struct MModuleConfig<WB: WasmBackend> {
|
||||||
/// Maximum number of Wasm memory pages that loaded module can use.
|
/// Maximum number of Wasm memory pages that loaded module can use.
|
||||||
/// Each Wasm page is 65536 bytes long.
|
/// Each Wasm page is 65536 bytes long.
|
||||||
pub max_heap_pages_count: u32,
|
pub max_heap_pages_count: u32,
|
||||||
|
|
||||||
/// Import object that will be used in module instantiation process.
|
/// 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.
|
/// 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.
|
/// WASI parameters: env variables, mapped dirs, preopened files and args
|
||||||
pub wasi_version: WasiVersion,
|
pub wasi_parameters: WasiParameters,
|
||||||
|
|
||||||
/// 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>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MModuleConfig {
|
impl<WB: WasmBackend> Default for MModuleConfig<WB> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// some reasonable defaults
|
// some reasonable defaults
|
||||||
Self {
|
Self {
|
||||||
max_heap_pages_count: DEFAULT_HEAP_PAGES_COUNT,
|
max_heap_pages_count: DEFAULT_HEAP_PAGES_COUNT,
|
||||||
raw_imports: ImportObject::new(),
|
raw_imports: HashMap::new(),
|
||||||
host_imports: HashMap::new(),
|
host_imports: HashMap::new(),
|
||||||
wasi_version: WasiVersion::Latest,
|
wasi_parameters: WasiParameters::default(),
|
||||||
wasi_envs: HashMap::new(),
|
|
||||||
wasi_preopened_files: HashSet::new(),
|
|
||||||
wasi_mapped_dirs: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,29 +85,24 @@ impl Default for MModuleConfig {
|
|||||||
// TODO: implement debug for MModuleConfig
|
// TODO: implement debug for MModuleConfig
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl MModuleConfig {
|
impl<WB: WasmBackend> MModuleConfig<WB> {
|
||||||
pub fn with_mem_pages_count(mut self, mem_pages_count: u32) -> Self {
|
pub fn with_mem_pages_count(mut self, mem_pages_count: u32) -> Self {
|
||||||
self.max_heap_pages_count = mem_pages_count;
|
self.max_heap_pages_count = mem_pages_count;
|
||||||
self
|
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 {
|
pub fn with_wasi_envs(mut self, envs: HashMap<Vec<u8>, Vec<u8>>) -> Self {
|
||||||
self.wasi_envs = envs;
|
self.wasi_parameters.envs = envs;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_wasi_preopened_files(mut self, preopened_files: HashSet<PathBuf>) -> 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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_wasi_mapped_dirs(mut self, mapped_dirs: HashMap<String, PathBuf>) -> 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
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,7 @@ use crate::misc::PrepareError;
|
|||||||
use marine_it_interfaces::MITInterfacesError;
|
use marine_it_interfaces::MITInterfacesError;
|
||||||
use marine_it_parser::ITParserError;
|
use marine_it_parser::ITParserError;
|
||||||
use marine_module_interface::it_interface::ITInterfaceError;
|
use marine_module_interface::it_interface::ITInterfaceError;
|
||||||
|
use marine_wasm_backend_traits::errors::*;
|
||||||
use wasmer_runtime::error as wasmer_error;
|
|
||||||
|
|
||||||
use thiserror::Error as ThisError;
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
@ -30,41 +29,13 @@ use thiserror::Error as ThisError;
|
|||||||
|
|
||||||
#[derive(Debug, ThisError)]
|
#[derive(Debug, ThisError)]
|
||||||
pub enum MError {
|
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.
|
/// Errors related to failed resolving of records.
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
RecordResolveError(String),
|
RecordResolveError(String), // TODO: use a proper error type
|
||||||
|
|
||||||
/// Errors arisen during creation of a WASI context.
|
/// Errors arisen during creation of a WASI context.
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
WASIPrepareError(String),
|
WASIPrepareError(#[from] WasiError),
|
||||||
|
|
||||||
/// Errors occurred inside marine-module-interface crate.
|
/// Errors occurred inside marine-module-interface crate.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
@ -100,7 +71,10 @@ pub enum MError {
|
|||||||
|
|
||||||
/// Incorrect IT section.
|
/// Incorrect IT section.
|
||||||
#[error("{0}")]
|
#[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 {
|
impl From<MITInterfacesError> for MError {
|
||||||
@ -109,26 +83,32 @@ impl From<MITInterfacesError> for MError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<wasmer_error::RuntimeError> for MError {
|
impl From<ModuleCreationError> for MError {
|
||||||
fn from(err: wasmer_error::RuntimeError) -> Self {
|
fn from(value: ModuleCreationError) -> Self {
|
||||||
Self::WasmerRuntimeError(err.to_string())
|
Into::<WasmBackendError>::into(value).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<wasmer_error::Error> for MError {
|
impl From<ResolveError> for MError {
|
||||||
fn from(err: wasmer_error::Error) -> Self {
|
fn from(value: ResolveError) -> Self {
|
||||||
Self::WasmerError(err.to_string())
|
Into::<WasmBackendError>::into(value).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<wasmer_error::InvokeError> for MError {
|
impl From<ImportError> for MError {
|
||||||
fn from(err: wasmer_error::InvokeError) -> Self {
|
fn from(value: ImportError) -> Self {
|
||||||
Self::WasmerInvokeError(err.to_string())
|
Into::<WasmBackendError>::into(value).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<()> for MError {
|
impl From<InstantiationError> for MError {
|
||||||
fn from(_err: ()) -> Self {
|
fn from(value: InstantiationError) -> Self {
|
||||||
MError::IncorrectWIT("failed to parse instructions for adapter type".to_string())
|
Into::<WasmBackendError>::into(value).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RuntimeError> for MError {
|
||||||
|
fn from(value: RuntimeError) -> Self {
|
||||||
|
Into::<WasmBackendError>::into(value).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,15 +36,15 @@ pub enum HostImportError {
|
|||||||
#[error("Not enough WValue arguments are provided from the Wasm side")]
|
#[error("Not enough WValue arguments are provided from the Wasm side")]
|
||||||
MismatchWValuesCount,
|
MismatchWValuesCount,
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
LifterError(#[from] LiError),
|
LifterError(#[from] LiError),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
LowererError(#[from] LoError),
|
LowererError(#[from] LoError),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
RecordNotFound(#[from] RecordResolvableError),
|
RecordNotFound(#[from] RecordResolvableError),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
InvalidUTF8String(#[from] std::string::FromUtf8Error),
|
InvalidUTF8String(#[from] std::string::FromUtf8Error),
|
||||||
}
|
}
|
||||||
|
@ -22,86 +22,132 @@ use super::lowering::LoHelper;
|
|||||||
use super::utils::itypes_args_to_wtypes;
|
use super::utils::itypes_args_to_wtypes;
|
||||||
use super::utils::itypes_output_to_wtypes;
|
use super::utils::itypes_output_to_wtypes;
|
||||||
|
|
||||||
|
use crate::IType;
|
||||||
|
use crate::IValue;
|
||||||
use crate::MRecordTypes;
|
use crate::MRecordTypes;
|
||||||
use crate::init_wasm_func_once;
|
use crate::init_wasm_func;
|
||||||
use crate::call_wasm_func;
|
use crate::call_wasm_func;
|
||||||
use crate::HostImportDescriptor;
|
use crate::generic::HostImportDescriptor;
|
||||||
use crate::module::wit_prelude::WITMemoryView;
|
|
||||||
|
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::lifter::ILifter;
|
||||||
use it_lilo::lowerer::ILowerer;
|
use it_lilo::lowerer::ILowerer;
|
||||||
|
use it_memory_traits::Memory as ITMemory;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::sync::Arc;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub(crate) fn create_host_import_func(
|
pub(crate) fn create_host_import_func<WB: WasmBackend>(
|
||||||
descriptor: HostImportDescriptor,
|
store: &mut <WB as WasmBackend>::Store,
|
||||||
record_types: Rc<MRecordTypes>,
|
descriptor: HostImportDescriptor<WB>,
|
||||||
) -> DynamicFunc<'static> {
|
record_types: Arc<MRecordTypes>,
|
||||||
let allocate_func: AllocateFunc = Box::new(RefCell::new(None));
|
) -> <WB as WasmBackend>::Function {
|
||||||
let set_result_ptr_func: SetResultPtrFunc = Box::new(RefCell::new(None));
|
let raw_args = itypes_args_to_wtypes(&descriptor.argument_types);
|
||||||
let set_result_size_func: SetResultSizeFunc = Box::new(RefCell::new(None));
|
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 {
|
let HostImportDescriptor {
|
||||||
host_exported_func,
|
host_exported_func,
|
||||||
argument_types,
|
argument_types,
|
||||||
output_type,
|
|
||||||
error_handler,
|
error_handler,
|
||||||
|
..
|
||||||
} = descriptor;
|
} = descriptor;
|
||||||
|
|
||||||
let output_type_to_types = |output_type| match output_type {
|
let memory = caller
|
||||||
Some(ty) => vec![ty],
|
.memory(STANDARD_MEMORY_INDEX)
|
||||||
None => vec![],
|
.unwrap_or_else(|| panic!("Host import called directly, not from wasm"));
|
||||||
};
|
|
||||||
|
|
||||||
let raw_args = itypes_args_to_wtypes(&argument_types);
|
let inputs = lift_inputs::<WB>(
|
||||||
let raw_output = itypes_output_to_wtypes(&output_type_to_types(output_type));
|
&mut caller,
|
||||||
|
memory.clone(),
|
||||||
let func = move |ctx: &mut Ctx, inputs: &[WValue]| -> Vec<WValue> {
|
record_types,
|
||||||
let result = {
|
inputs,
|
||||||
let memory_index = 0;
|
argument_types,
|
||||||
let memory = ctx.memory(memory_index);
|
);
|
||||||
let memory_view = WITMemoryView(memory.view::<u8>());
|
let output = match inputs {
|
||||||
let li_helper = LiHelper::new(record_types.clone());
|
Ok(ivalues) => host_exported_func(&mut caller, ivalues),
|
||||||
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) => {
|
Err(e) => {
|
||||||
log::error!("error occurred while lifting values in host import: {}", e);
|
log::error!("error occurred while lifting values in host import: {}", e);
|
||||||
error_handler
|
error_handler
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| default_error_handler(&e), |h| h(&e))
|
.map_or_else(|| default_error_handler(&e), |h| h(&e))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
init_wasm_func_once!(allocate_func, ctx, (i32, i32), i32, ALLOCATE_FUNC_NAME, 2);
|
lower_outputs::<WB>(&mut caller, memory, output)
|
||||||
|
}
|
||||||
|
|
||||||
let memory_index = 0;
|
fn lift_inputs<WB: WasmBackend>(
|
||||||
let memory = ctx.memory(memory_index);
|
caller: &mut <WB as WasmBackend>::Caller<'_>,
|
||||||
let memory_view = WITMemoryView(memory.view::<u8>());
|
memory: <WB as WasmBackend>::Memory,
|
||||||
let lo_helper = LoHelper::new(&allocate_func, ctx);
|
record_types: Arc<MRecordTypes>,
|
||||||
let t = ILowerer::new(memory_view, &lo_helper)
|
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)
|
.map_err(HostImportError::LowererError)
|
||||||
.and_then(|lowerer| ivalue_to_wvalues(&lowerer, result));
|
.and_then(|mut lowerer| {
|
||||||
|
ivalue_to_wvalues(&mut caller.as_context_mut(), &mut lowerer, output)
|
||||||
|
});
|
||||||
|
|
||||||
let wvalues = match t {
|
let wvalues = match lowering_result {
|
||||||
Ok(wvalues) => wvalues,
|
Ok(wvalues) => wvalues,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("host closure failed: {}", e);
|
log::error!("host closure failed: {}", e);
|
||||||
|
|
||||||
// returns 0 to a Wasm module in case of errors
|
// 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!(set_result_ptr_func, caller, i32, (), SET_PTR_FUNC_NAME, 4);
|
||||||
init_wasm_func_once!(set_result_size_func, ctx, i32, (), SET_SIZE_FUNC_NAME, 4);
|
init_wasm_func!(set_result_size_func, caller, i32, (), SET_SIZE_FUNC_NAME, 4);
|
||||||
|
|
||||||
call_wasm_func!(set_result_ptr_func, 0);
|
call_wasm_func!(set_result_ptr_func, &mut caller.as_context_mut(), 0);
|
||||||
call_wasm_func!(set_result_size_func, 0);
|
call_wasm_func!(set_result_size_func, &mut caller.as_context_mut(), 0);
|
||||||
return vec![WValue::I32(0)];
|
return vec![WValue::I32(0)];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -109,24 +155,40 @@ pub(crate) fn create_host_import_func(
|
|||||||
// TODO: refactor this when multi-value is supported
|
// TODO: refactor this when multi-value is supported
|
||||||
match wvalues.len() {
|
match wvalues.len() {
|
||||||
// strings and arrays are passed back to the Wasm module by pointer and size
|
// 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 => {
|
2 => {
|
||||||
init_wasm_func_once!(set_result_ptr_func, ctx, i32, (), SET_PTR_FUNC_NAME, 4);
|
init_wasm_func!(set_result_ptr_func, caller, i32, (), SET_PTR_FUNC_NAME, 4);
|
||||||
init_wasm_func_once!(set_result_size_func, ctx, i32, (), SET_SIZE_FUNC_NAME, 4);
|
init_wasm_func!(set_result_size_func, caller, i32, (), SET_SIZE_FUNC_NAME, 4);
|
||||||
|
|
||||||
call_wasm_func!(set_result_ptr_func, wvalues[0].to_u128() as _);
|
call_wasm_func!(
|
||||||
call_wasm_func!(set_result_size_func, wvalues[1].to_u128() as _);
|
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![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
// records and primitive types are passed to the Wasm module by pointer
|
// records lowerer returns only pointer which has to be used and consumed via set_result_ptr
|
||||||
// and value on the stack
|
1 if is_record => {
|
||||||
1 => {
|
init_wasm_func!(set_result_ptr_func, caller, i32, (), SET_PTR_FUNC_NAME, 3);
|
||||||
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 _);
|
call_wasm_func!(
|
||||||
vec![wvalues[0].clone()]
|
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
|
// when None is passed
|
||||||
0 => vec![],
|
0 => vec![],
|
||||||
|
|
||||||
@ -134,12 +196,13 @@ pub(crate) fn create_host_import_func(
|
|||||||
// 0, 1, 2 values
|
// 0, 1, 2 values
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
DynamicFunc::new(
|
fn output_type_to_types(output_type: Option<&IType>) -> Vec<IType> {
|
||||||
std::sync::Arc::new(FuncSig::new(raw_args, raw_output)),
|
match output_type {
|
||||||
func,
|
Some(ty) => vec![ty.clone()],
|
||||||
)
|
None => vec![],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_error_handler(err: &HostImportError) -> Option<crate::IValue> {
|
fn default_error_handler(err: &HostImportError) -> Option<crate::IValue> {
|
||||||
|
@ -19,14 +19,14 @@ use crate::IRecordType;
|
|||||||
use it_lilo::traits::RecordResolvable;
|
use it_lilo::traits::RecordResolvable;
|
||||||
use it_lilo::traits::RecordResolvableError;
|
use it_lilo::traits::RecordResolvableError;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub(crate) struct LiHelper {
|
pub(crate) struct LiHelper {
|
||||||
record_types: Rc<MRecordTypes>,
|
record_types: Arc<MRecordTypes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LiHelper {
|
impl LiHelper {
|
||||||
pub(crate) fn new(record_types: Rc<MRecordTypes>) -> Self {
|
pub(crate) fn new(record_types: Arc<MRecordTypes>) -> Self {
|
||||||
Self { record_types }
|
Self { record_types }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,13 @@ macro_rules! simple_wvalue_to_ivalue {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn wvalues_to_ivalues<R: RecordResolvable, MV: MemoryView>(
|
pub(crate) fn wvalues_to_ivalues<
|
||||||
lifter: &ILifter<'_, R, MV>,
|
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],
|
wvalues: &[WValue],
|
||||||
itypes: &[IType],
|
itypes: &[IType],
|
||||||
) -> HostImportResult<Vec<IValue>> {
|
) -> HostImportResult<Vec<IValue>> {
|
||||||
@ -73,7 +78,9 @@ pub(crate) fn wvalues_to_ivalues<R: RecordResolvable, MV: MemoryView>(
|
|||||||
let offset = next_wvalue!(wvalue, I32);
|
let offset = next_wvalue!(wvalue, I32);
|
||||||
let size = 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)?;
|
let str = String::from_utf8(raw_str)?;
|
||||||
result.push(IValue::String(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 offset = next_wvalue!(wvalue, I32);
|
||||||
let size = 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));
|
result.push(IValue::ByteArray(array));
|
||||||
}
|
}
|
||||||
IType::Array(ty) => {
|
IType::Array(ty) => {
|
||||||
let offset = next_wvalue!(wvalue, I32);
|
let offset = next_wvalue!(wvalue, I32);
|
||||||
let size = 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);
|
result.push(array);
|
||||||
}
|
}
|
||||||
IType::Record(record_type_id) => {
|
IType::Record(record_type_id) => {
|
||||||
let record_type = lifter.resolver.resolve_record(*record_type_id)?;
|
let record_type = lifter.resolver.resolve_record(*record_type_id)?;
|
||||||
let offset = next_wvalue!(wvalue, I32);
|
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);
|
result.push(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,34 +15,59 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::AllocateFunc;
|
use super::AllocateFunc;
|
||||||
use crate::module::wit_prelude::WITMemoryView;
|
|
||||||
|
|
||||||
use crate::call_wasm_func;
|
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::Allocatable;
|
||||||
use it_lilo::traits::AllocatableError;
|
use it_lilo::traits::AllocatableError;
|
||||||
|
use it_memory_traits::MemoryView;
|
||||||
|
use it_memory_traits::Memory;
|
||||||
|
|
||||||
use it_lilo::traits::DEFAULT_MEMORY_INDEX;
|
use std::marker::PhantomData;
|
||||||
use wasmer_core::vm::Ctx;
|
|
||||||
|
|
||||||
pub(crate) struct LoHelper<'c> {
|
pub(crate) struct LoHelper<
|
||||||
allocate_func: &'c AllocateFunc,
|
'c,
|
||||||
ctx: &'c Ctx,
|
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> {
|
impl<
|
||||||
pub(crate) fn new(allocate_func: &'c AllocateFunc, ctx: &'c Ctx) -> Self {
|
'c,
|
||||||
Self { allocate_func, ctx }
|
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(
|
fn allocate(
|
||||||
&self,
|
&mut self,
|
||||||
|
store: &mut <WB as WasmBackend>::ContextMut<'_>,
|
||||||
size: u32,
|
size: u32,
|
||||||
type_tag: u32,
|
type_tag: u32,
|
||||||
) -> Result<(u32, WITMemoryView<'s>), AllocatableError> {
|
) -> Result<(u32, MV), AllocatableError> {
|
||||||
let offset = call_wasm_func!(self.allocate_func, size as _, type_tag as _);
|
let offset = call_wasm_func!(self.allocate_func, store, size as _, type_tag as _);
|
||||||
let view = WITMemoryView(self.ctx.memory(DEFAULT_MEMORY_INDEX as u32).view());
|
Ok((offset as u32, self.memory.view()))
|
||||||
Ok((offset as u32, view))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,13 @@ use it_lilo::lowerer::*;
|
|||||||
use it_lilo::traits::Allocatable;
|
use it_lilo::traits::Allocatable;
|
||||||
use it_memory_traits::MemoryView;
|
use it_memory_traits::MemoryView;
|
||||||
|
|
||||||
pub(crate) fn ivalue_to_wvalues<A: Allocatable<MV>, MV: MemoryView>(
|
pub(crate) fn ivalue_to_wvalues<
|
||||||
lowerer: &ILowerer<'_, A, MV>,
|
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>,
|
ivalue: Option<IValue>,
|
||||||
) -> HostImportResult<Vec<WValue>> {
|
) -> HostImportResult<Vec<WValue>> {
|
||||||
let result = match ivalue {
|
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::F32(v)) => vec![WValue::F32(v)],
|
||||||
Some(IValue::F64(v)) => vec![WValue::F64(v)],
|
Some(IValue::F64(v)) => vec![WValue::F64(v)],
|
||||||
Some(IValue::String(str)) => {
|
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 _)]
|
vec![WValue::I32(offset as _), WValue::I32(str.len() as _)]
|
||||||
}
|
}
|
||||||
Some(IValue::ByteArray(array)) => {
|
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 _)]
|
vec![WValue::I32(offset as _), WValue::I32(array.len() as _)]
|
||||||
}
|
}
|
||||||
Some(IValue::Array(values)) => {
|
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 _)]
|
vec![WValue::I32(offset as _), WValue::I32(size as _)]
|
||||||
}
|
}
|
||||||
Some(IValue::Record(values)) => {
|
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)]
|
vec![WValue::I32(offset as i32)]
|
||||||
}
|
}
|
||||||
None => vec![],
|
None => vec![],
|
||||||
|
@ -20,20 +20,20 @@ mod lowering;
|
|||||||
mod imports;
|
mod imports;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use marine_wasm_backend_traits::RuntimeResult;
|
||||||
use wasmer_core::Func;
|
use marine_wasm_backend_traits::WasmBackend;
|
||||||
|
|
||||||
pub use errors::HostImportError;
|
pub use errors::HostImportError;
|
||||||
pub(crate) use imports::create_host_import_func;
|
pub(crate) use imports::create_host_import_func;
|
||||||
|
|
||||||
pub(self) use wasmer_core::types::Value as WValue;
|
pub(self) use marine_wasm_backend_traits::WValue;
|
||||||
pub(self) use wasmer_core::types::Type as WType;
|
pub(self) use marine_wasm_backend_traits::WType;
|
||||||
|
|
||||||
pub(self) type HostImportResult<T> = std::result::Result<T, HostImportError>;
|
pub(self) type HostImportResult<T> = std::result::Result<T, HostImportError>;
|
||||||
pub(self) type WasmModuleFunc<Args, Rets> = Box<RefCell<Option<Func<'static, Args, Rets>>>>;
|
pub(self) type WasmModuleFunc<WB, Args, Rets> = Box<
|
||||||
pub(self) type AllocateFunc = WasmModuleFunc<(i32, i32), i32>;
|
dyn FnMut(&mut <WB as WasmBackend>::ContextMut<'_>, Args) -> RuntimeResult<Rets> + Sync + Send,
|
||||||
pub(self) type SetResultPtrFunc = WasmModuleFunc<i32, ()>;
|
>;
|
||||||
pub(self) type SetResultSizeFunc = WasmModuleFunc<i32, ()>;
|
pub(self) type AllocateFunc<WB> = WasmModuleFunc<WB, (i32, i32), i32>;
|
||||||
|
|
||||||
pub(self) const ALLOCATE_FUNC_NAME: &str = "allocate";
|
pub(self) const ALLOCATE_FUNC_NAME: &str = "allocate";
|
||||||
pub(self) const SET_PTR_FUNC_NAME: &str = "set_result_ptr";
|
pub(self) const SET_PTR_FUNC_NAME: &str = "set_result_ptr";
|
||||||
|
@ -17,85 +17,6 @@
|
|||||||
use super::WType;
|
use super::WType;
|
||||||
use crate::IType;
|
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> {
|
pub(super) fn itypes_args_to_wtypes(itypes: &[IType]) -> Vec<WType> {
|
||||||
itypes
|
itypes
|
||||||
.iter()
|
.iter()
|
||||||
@ -122,35 +43,26 @@ pub(super) fn itypes_output_to_wtypes(itypes: &[IType]) -> Vec<WType> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export] // https://github.com/rust-lang/rust/issues/57966#issuecomment-461077932
|
#[macro_export]
|
||||||
/// Initialize Wasm function in form of Box<RefCell<Option<Func<'static, args, rets>>>> only once.
|
/// Initialize Wasm function in form of Box<RefCell<Option<Func<'static, args, rets>>>>.
|
||||||
macro_rules! init_wasm_func_once {
|
/// 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) => {
|
($func:ident, $ctx:ident, $args:ty, $rets:ty, $func_name:ident, $ret_error_code: expr) => {
|
||||||
if $func.borrow().is_none() {
|
let mut $func: Box<
|
||||||
let raw_func = match unsafe {
|
dyn FnMut(&mut <WB as WasmBackend>::ContextMut<'_>, $args) -> RuntimeResult<$rets>
|
||||||
super::utils::get_export_func_by_name::<$args, $rets>($ctx, $func_name)
|
+ Send
|
||||||
} {
|
+ Sync,
|
||||||
|
> = match { $ctx.get_func($func_name) } {
|
||||||
Ok(func) => func,
|
Ok(func) => func,
|
||||||
Err(_) => return vec![WValue::I32($ret_error_code)],
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
/// Call Wasm function that have Box<RefCell<Option<Func<'static, args, rets>>>> type.
|
/// Call Wasm function that have Box<RefCell<Option<Func<'static, args, rets>>>> type.
|
||||||
macro_rules! call_wasm_func {
|
macro_rules! call_wasm_func {
|
||||||
($func:expr, $($arg:expr),*) => {
|
($func:expr, $store:expr, $($arg:expr),*) => {
|
||||||
$func.borrow().as_ref().unwrap().call($($arg),*).unwrap()
|
$func.as_mut()($store, ($($arg),*)).unwrap()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,6 @@ mod misc;
|
|||||||
mod module;
|
mod module;
|
||||||
mod memory_statistic;
|
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 crate::marine_core::MModuleInterface;
|
||||||
pub use errors::MError;
|
pub use errors::MError;
|
||||||
pub use host_imports::HostImportError;
|
pub use host_imports::HostImportError;
|
||||||
@ -59,3 +55,21 @@ pub mod ne_vec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type MResult<T> = std::result::Result<T, MError>;
|
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::*;
|
||||||
|
@ -14,15 +14,22 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::generic::*;
|
||||||
use crate::module::MModule;
|
use crate::module::MModule;
|
||||||
use crate::module::MRecordTypes;
|
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 serde::Serialize;
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
/// Represent Marine module interface.
|
/// Represent Marine module interface.
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
|
#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
|
||||||
@ -31,17 +38,36 @@ pub struct MModuleInterface<'a> {
|
|||||||
pub function_signatures: Vec<MFunctionSignature>,
|
pub function_signatures: Vec<MFunctionSignature>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Description
|
||||||
|
///
|
||||||
/// The base struct of Marine, the Fluence compute runtime.
|
/// 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
|
// 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 {
|
impl<WB: WasmBackend> MarineCore<WB> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> MResult<Self> {
|
||||||
Self {
|
let wasm_backend = WB::new()?;
|
||||||
|
let store = <WB as WasmBackend>::Store::new(&wasm_backend);
|
||||||
|
Ok(Self {
|
||||||
modules: HashMap::new(),
|
modules: HashMap::new(),
|
||||||
}
|
wasm_backend,
|
||||||
|
store: RefCell::new(store),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke a function of a module inside Marine by given function name with given arguments.
|
/// Invoke a function of a module inside Marine by given function name with given arguments.
|
||||||
@ -52,10 +78,17 @@ impl MarineCore {
|
|||||||
arguments: &[IValue],
|
arguments: &[IValue],
|
||||||
) -> MResult<Vec<IValue>> {
|
) -> MResult<Vec<IValue>> {
|
||||||
let module_name = module_name.as_ref();
|
let module_name = module_name.as_ref();
|
||||||
|
let store = &mut self.store;
|
||||||
self.modules.get_mut(module_name).map_or_else(
|
self.modules.get_mut(module_name).map_or_else(
|
||||||
|| Err(MError::NoSuchModule(module_name.to_string())),
|
|| 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,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
wasm_bytes: &[u8],
|
wasm_bytes: &[u8],
|
||||||
config: MModuleConfig,
|
config: MModuleConfig<WB>,
|
||||||
) -> MResult<()> {
|
) -> MResult<()> {
|
||||||
self.load_module_(name.into(), wasm_bytes, config)
|
self.load_module_(name.into(), wasm_bytes, config)
|
||||||
}
|
}
|
||||||
@ -73,11 +106,17 @@ impl MarineCore {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: String,
|
name: String,
|
||||||
wasm_bytes: &[u8],
|
wasm_bytes: &[u8],
|
||||||
config: MModuleConfig,
|
config: MModuleConfig<WB>,
|
||||||
) -> MResult<()> {
|
) -> MResult<()> {
|
||||||
let _prepared_wasm_bytes =
|
let _prepared_wasm_bytes =
|
||||||
crate::misc::prepare_module(wasm_bytes, config.max_heap_pages_count)?;
|
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) {
|
match self.modules.entry(name) {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
@ -97,10 +136,10 @@ impl MarineCore {
|
|||||||
.ok_or_else(|| MError::NoSuchModule(name.as_ref().to_string()))
|
.ok_or_else(|| MError::NoSuchModule(name.as_ref().to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module_wasi_state(
|
pub fn module_wasi_state<'s>(
|
||||||
&mut self,
|
&'s mut self,
|
||||||
module_name: impl AsRef<str>,
|
module_name: impl AsRef<str>,
|
||||||
) -> Option<&wasmer_wasi::state::WasiState> {
|
) -> Option<Box<dyn WasiState + 's>> {
|
||||||
self.modules
|
self.modules
|
||||||
.get_mut(module_name.as_ref())
|
.get_mut(module_name.as_ref())
|
||||||
.map(|module| module.get_wasi_state())
|
.map(|module| module.get_wasi_state())
|
||||||
@ -132,7 +171,7 @@ impl MarineCore {
|
|||||||
&self,
|
&self,
|
||||||
module_name: impl AsRef<str>,
|
module_name: impl AsRef<str>,
|
||||||
record_id: u64,
|
record_id: u64,
|
||||||
) -> Option<&Rc<IRecordType>> {
|
) -> Option<&Arc<IRecordType>> {
|
||||||
self.modules
|
self.modules
|
||||||
.get(module_name.as_ref())
|
.get(module_name.as_ref())
|
||||||
.and_then(|module| module.export_record_type_by_id(record_id))
|
.and_then(|module| module.export_record_type_by_id(record_id))
|
||||||
@ -144,14 +183,18 @@ impl MarineCore {
|
|||||||
.modules
|
.modules
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(module_name, module)| {
|
.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<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
records.into()
|
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 record_types = module.export_record_types();
|
||||||
|
|
||||||
let function_signatures = module.get_exports_signatures().collect::<Vec<_>>();
|
let function_signatures = module.get_exports_signatures().collect::<Vec<_>>();
|
||||||
@ -162,9 +205,3 @@ impl MarineCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MarineCore {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -44,7 +44,8 @@ impl ModuleBootstrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_max_heap_pages_count(self, max_heap_pages_count: u32) -> PrepareResult<Self> {
|
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 Self { mut module } = self;
|
||||||
let globals_pages_count = get_heap_base(&module)
|
let globals_pages_count = get_heap_base(&module)
|
||||||
|
@ -57,7 +57,8 @@ fn find_global_by_index(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn u32_from_global_entry(global_entry: &elements::GlobalEntry) -> HResult<u32> {
|
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();
|
let entry_type = global_entry.global_type().content_type();
|
||||||
if !matches!(entry_type, ValueType::I32) {
|
if !matches!(entry_type, ValueType::I32) {
|
||||||
|
@ -20,23 +20,18 @@ use super::PrepareError;
|
|||||||
use marine_module_info_parser::sdk_version;
|
use marine_module_info_parser::sdk_version;
|
||||||
use marine_min_it_version::min_sdk_version;
|
use marine_min_it_version::min_sdk_version;
|
||||||
use marine_min_it_version::min_it_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<WB: WasmBackend>(
|
||||||
|
name: String,
|
||||||
pub(crate) fn check_sdk_version(
|
wasmer_module: &<WB as WasmBackend>::Module,
|
||||||
name: impl Into<String>,
|
|
||||||
wasmer_module: &Module,
|
|
||||||
) -> PrepareResult<()> {
|
) -> PrepareResult<()> {
|
||||||
let module_version = sdk_version::extract_from_wasmer_module(wasmer_module)?;
|
let module_version = sdk_version::extract_from_compiled_module::<WB>(wasmer_module)?;
|
||||||
let module_version = match module_version {
|
|
||||||
Some(module_version) => module_version,
|
|
||||||
None => return Err(PrepareError::ModuleWithoutVersion(name.into())),
|
|
||||||
};
|
|
||||||
|
|
||||||
let required_version = min_sdk_version();
|
let required_version = min_sdk_version();
|
||||||
if module_version < *required_version {
|
if module_version < *required_version {
|
||||||
return Err(PrepareError::IncompatibleSDKVersions {
|
return Err(PrepareError::IncompatibleSDKVersions {
|
||||||
module_name: name.into(),
|
module_name: name,
|
||||||
required: required_version.clone(),
|
required: required_version.clone(),
|
||||||
provided: module_version,
|
provided: module_version,
|
||||||
});
|
});
|
||||||
|
@ -17,140 +17,140 @@
|
|||||||
use super::wit_prelude::*;
|
use super::wit_prelude::*;
|
||||||
use super::MFunctionSignature;
|
use super::MFunctionSignature;
|
||||||
use super::MRecordTypes;
|
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::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_interfaces::MITInterfaces;
|
||||||
use marine_it_parser::extract_it_from_module;
|
use marine_it_parser::extract_it_from_module;
|
||||||
use marine_utils::SharedString;
|
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 wasmer_it::interpreter::Interpreter;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::rc::Rc;
|
use std::borrow::BorrowMut;
|
||||||
|
|
||||||
const MEMORY_INDEX: u32 = 0;
|
|
||||||
const START_FUNC: &str = "_start";
|
const START_FUNC: &str = "_start";
|
||||||
const INITIALIZE_FUNC: &str = "_initialize";
|
const INITIALIZE_FUNC: &str = "_initialize";
|
||||||
|
|
||||||
type ITInterpreter =
|
type ITInterpreter<WB> = Interpreter<
|
||||||
Interpreter<ITInstance, ITExport, WITFunction, WITMemory, WITMemoryView<'static>>;
|
ITInstance<WB>,
|
||||||
|
ITExport,
|
||||||
|
WITFunction<WB>,
|
||||||
|
<WB as WasmBackend>::Memory,
|
||||||
|
<WB as WasmBackend>::MemoryView,
|
||||||
|
DelayedContextLifetime<WB>,
|
||||||
|
>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct ITModuleFunc {
|
pub(super) struct ITModuleFunc<WB: WasmBackend> {
|
||||||
interpreter: Arc<ITInterpreter>,
|
interpreter: Arc<ITInterpreter<WB>>,
|
||||||
pub(super) arguments: Rc<Vec<IFunctionArg>>,
|
pub(super) arguments: Arc<Vec<IFunctionArg>>,
|
||||||
pub(super) output_types: Rc<Vec<IType>>,
|
pub(super) output_types: Arc<Vec<IType>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct Callable {
|
pub(super) struct Callable<WB: WasmBackend> {
|
||||||
pub(super) it_instance: Arc<ITInstance>,
|
pub(super) it_instance: Arc<ITInstance<WB>>,
|
||||||
pub(super) it_module_func: ITModuleFunc,
|
pub(super) it_module_func: ITModuleFunc<WB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Callable {
|
impl<WB: WasmBackend> Callable<WB> {
|
||||||
pub fn call(&mut self, args: &[IValue]) -> MResult<Vec<IValue>> {
|
pub fn call(
|
||||||
|
&mut self,
|
||||||
|
store: &mut <WB as WasmBackend>::ContextMut<'_>,
|
||||||
|
args: &[IValue],
|
||||||
|
) -> MResult<Vec<IValue>> {
|
||||||
use wasmer_it::interpreter::stack::Stackable;
|
use wasmer_it::interpreter::stack::Stackable;
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.it_module_func
|
.it_module_func
|
||||||
.interpreter
|
.interpreter
|
||||||
.run(args, Arc::make_mut(&mut self.it_instance))?
|
.run(args, Arc::make_mut(&mut self.it_instance), store)?
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExportFunctions = HashMap<SharedString, Rc<Callable>>;
|
type ExportFunctions<WB> = HashMap<SharedString, Arc<Callable<WB>>>;
|
||||||
|
|
||||||
pub(crate) struct MModule {
|
pub(crate) struct MModule<WB: WasmBackend> {
|
||||||
// wasmer_instance is needed because WITInstance contains dynamic functions
|
wasm_instance: Box<<WB as WasmBackend>::Instance>,
|
||||||
// that internally keep pointer to it.
|
|
||||||
#[allow(unused)]
|
|
||||||
wasmer_instance: Box<WasmerInstance>,
|
|
||||||
|
|
||||||
// import_object is needed because ImportObject::extend doesn't really deep copy
|
export_funcs: ExportFunctions<WB>,
|
||||||
// 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,
|
|
||||||
|
|
||||||
// TODO: save refs instead copying of a record types HashMap.
|
// TODO: save refs instead copying of a record types HashMap.
|
||||||
/// Record types used in exported functions as arguments or return values.
|
/// Record types used in exported functions as arguments or return values.
|
||||||
export_record_types: MRecordTypes,
|
export_record_types: MRecordTypes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MModule {
|
impl<WB: WasmBackend> MModule<WB> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
name: &str,
|
name: &str,
|
||||||
|
store: &mut <WB as WasmBackend>::Store,
|
||||||
wasm_bytes: &[u8],
|
wasm_bytes: &[u8],
|
||||||
config: MModuleConfig,
|
config: MModuleConfig<WB>,
|
||||||
modules: &HashMap<String, MModule>,
|
modules: &HashMap<String, MModule<WB>>,
|
||||||
) -> MResult<Self> {
|
) -> MResult<Self> {
|
||||||
let wasmer_module = compile(wasm_bytes)?;
|
let wasm_module = <WB as WasmBackend>::Module::new(store, wasm_bytes)?;
|
||||||
crate::misc::check_sdk_version(name, &wasmer_module)?;
|
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)?;
|
crate::misc::check_it_version(name, &it.version)?;
|
||||||
|
|
||||||
let mit = MITInterfaces::new(it);
|
let mit = MITInterfaces::new(it);
|
||||||
|
|
||||||
let mut wit_instance = Arc::new_uninit();
|
let mut wit_instance = Arc::new_uninit();
|
||||||
let wit_import_object = Self::adjust_wit_imports(&mit, wit_instance.clone())?;
|
let mut linker = <WB as WasmBackend>::Imports::new(store);
|
||||||
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 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 {
|
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
|
// get_mut_unchecked here is safe because currently only this modules have reference to
|
||||||
// it and the environment is single-threaded
|
// it and the environment is single-threaded
|
||||||
*Arc::get_mut_unchecked(&mut wit_instance) =
|
*Arc::get_mut_unchecked(&mut wit_instance) =
|
||||||
MaybeUninit::new(ITInstance::new(&wasmer_instance, name, &mit, modules)?);
|
MaybeUninit::new(ITInstance::new(&wasm_instance, store, name, &mit, modules)?);
|
||||||
std::mem::transmute::<_, Arc<ITInstance>>(wit_instance)
|
std::mem::transmute::<_, Arc<ITInstance<WB>>>(wit_instance)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (export_funcs, export_record_types) = Self::instantiate_exports(&it_instance, &mit)?;
|
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
|
// call _initialize to populate the WASI state of the module
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
if let Ok(initialize_func) = wasmer_instance.exports.get::<wasmer_runtime::Func<'_, (), ()>>(INITIALIZE_FUNC) {
|
if let Ok(initialize_func) = wasm_instance.get_function(store, INITIALIZE_FUNC) {
|
||||||
initialize_func.call()?;
|
initialize_func.call(store, &[])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// call _start to call module's main function
|
// call _start to call module's main function
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
if let Ok(start_func) = wasmer_instance.exports.get::<wasmer_runtime::Func<'_, (), ()>>(START_FUNC) {
|
if let Ok(start_func) = wasm_instance.get_function(store, START_FUNC) {
|
||||||
start_func.call()?;
|
start_func.call(store, &[])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
wasmer_instance: Box::new(wasmer_instance),
|
wasm_instance: Box::new(wasm_instance),
|
||||||
it_import_object: wit_import_object,
|
|
||||||
host_import_object: raw_imports,
|
|
||||||
host_closures_import_object,
|
|
||||||
export_funcs,
|
export_funcs,
|
||||||
export_record_types,
|
export_record_types,
|
||||||
})
|
})
|
||||||
@ -158,19 +158,33 @@ impl MModule {
|
|||||||
|
|
||||||
pub(crate) fn call(
|
pub(crate) fn call(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
store: &mut <WB as WasmBackend>::ContextMut<'_>,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
function_name: &str,
|
function_name: &str,
|
||||||
args: &[IValue],
|
args: &[IValue],
|
||||||
) -> MResult<Vec<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(
|
Err(MError::NoSuchFunction(
|
||||||
module_name.to_string(),
|
module_name.to_string(),
|
||||||
function_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> + '_ {
|
pub(crate) fn get_exports_signatures(&self) -> impl Iterator<Item = MFunctionSignature> + '_ {
|
||||||
@ -187,30 +201,28 @@ impl MModule {
|
|||||||
&self.export_record_types
|
&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)
|
self.export_record_types.get(&record_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_wasi_state(&mut self) -> &wasmer_wasi::state::WasiState {
|
pub(crate) fn get_wasi_state<'s>(&'s mut self) -> Box<dyn WasiState + 's> {
|
||||||
unsafe { wasmer_wasi::state::get_wasi_state(self.wasmer_instance.context_mut()) }
|
<WB as WasmBackend>::Wasi::get_wasi_state(self.wasm_instance.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns Wasm linear memory size that this module consumes in bytes.
|
/// Returns Wasm linear memory size that this module consumes in bytes.
|
||||||
pub(crate) fn memory_size(&self) -> usize {
|
pub(crate) fn memory_size(&self, store: &mut <WB as WasmBackend>::ContextMut<'_>) -> usize {
|
||||||
let pages = self.wasmer_instance.context().memory(MEMORY_INDEX).size();
|
let memory = self
|
||||||
pages.bytes().0
|
.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.
|
/// Returns max Wasm linear memory size that this module could consume in bytes.
|
||||||
pub(crate) fn max_memory_size(&self) -> Option<usize> {
|
pub(crate) fn max_memory_size(&self) -> Option<usize> {
|
||||||
let maybe_pages = self
|
// TODO: provide limits API to marine wasm backend traits
|
||||||
.wasmer_instance
|
None
|
||||||
.context()
|
|
||||||
.memory(MEMORY_INDEX)
|
|
||||||
.descriptor()
|
|
||||||
.maximum;
|
|
||||||
|
|
||||||
maybe_pages.map(|pages| pages.bytes().0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change the cloning Callable behaviour after changes of Wasmer API
|
// TODO: change the cloning Callable behaviour after changes of Wasmer API
|
||||||
@ -218,7 +230,7 @@ impl MModule {
|
|||||||
&self,
|
&self,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
function_name: &str,
|
function_name: &str,
|
||||||
) -> MResult<Rc<Callable>> {
|
) -> MResult<Arc<Callable<WB>>> {
|
||||||
match self.export_funcs.get(function_name) {
|
match self.export_funcs.get(function_name) {
|
||||||
Some(func) => Ok(func.clone()),
|
Some(func) => Ok(func.clone()),
|
||||||
None => Err(MError::NoSuchFunction(
|
None => Err(MError::NoSuchFunction(
|
||||||
@ -228,121 +240,101 @@ impl MModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_import_objects(
|
fn add_wasi_imports(
|
||||||
config: MModuleConfig,
|
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<'_>,
|
mit: &MITInterfaces<'_>,
|
||||||
wit_import_object: ImportObject,
|
) -> MResult<()> {
|
||||||
) -> MResult<(ImportObject, ImportObject)> {
|
|
||||||
use crate::host_imports::create_host_import_func;
|
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
|
let record_types = mit
|
||||||
.record_types()
|
.record_types()
|
||||||
.map(|(id, r)| (id, r.clone()))
|
.map(|(id, r)| (id, r.clone()))
|
||||||
.collect::<HashMap<_, _>>();
|
.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_imports = 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
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|sign| {
|
.map(|(import_name, descriptor)| {
|
||||||
let adapter_instructions = mit.adapter_by_type_r(sign.adapter_function_type)?;
|
let func = create_host_import_func::<WB>(store, descriptor, record_types.clone());
|
||||||
|
(import_name, func)
|
||||||
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))
|
|
||||||
})
|
})
|
||||||
.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
|
// 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: &MITInterfaces<'_>,
|
||||||
wit_instance: Arc<MaybeUninit<ITInstance>>,
|
wit_instance: Arc<MaybeUninit<ITInstance<WB>>>,
|
||||||
) -> MResult<ImportObject> {
|
) -> MResult<()> {
|
||||||
use marine_it_interfaces::ITAstType;
|
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
|
// returns function that will be called from imports of Wasmer module
|
||||||
fn dyn_func_from_raw_import<'a, 'b, F>(
|
fn func_from_raw_import<'a, 'b, F, WB, I1, I2>(
|
||||||
inputs: impl Iterator<Item = &'a IType>,
|
store: &mut <WB as WasmBackend>::Store,
|
||||||
outputs: impl Iterator<Item = &'b IType>,
|
inputs: I1,
|
||||||
|
outputs: I2,
|
||||||
raw_import: F,
|
raw_import: F,
|
||||||
) -> DynamicFunc<'static>
|
) -> <WB as WasmBackend>::Function
|
||||||
where
|
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;
|
use super::type_converters::itype_to_wtype;
|
||||||
|
|
||||||
let inputs = inputs.map(itype_to_wtype).collect::<Vec<_>>();
|
let inputs = inputs.map(itype_to_wtype).collect::<Vec<_>>();
|
||||||
let outputs = outputs.map(itype_to_wtype).collect::<Vec<_>>();
|
let outputs = outputs.map(itype_to_wtype).collect::<Vec<_>>();
|
||||||
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
|
// creates a closure that is represent a IT module import
|
||||||
fn create_raw_import(
|
fn create_raw_import<WB: WasmBackend>(
|
||||||
wit_instance: Arc<MaybeUninit<ITInstance>>,
|
wit_instance: Arc<MaybeUninit<ITInstance<WB>>>,
|
||||||
interpreter: ITInterpreter,
|
interpreter: ITInterpreter<WB>,
|
||||||
import_namespace: String,
|
import_namespace: String,
|
||||||
import_name: String,
|
import_name: String,
|
||||||
) -> impl Fn(&mut Ctx, &[WValue]) -> Vec<WValue> + 'static {
|
) -> impl for<'c> Fn(<WB as WasmBackend>::Caller<'c>, &[WValue]) -> Vec<WValue>
|
||||||
move |_: &mut Ctx, inputs: &[WValue]| -> Vec<WValue> {
|
+ Sync
|
||||||
|
+ Send
|
||||||
|
+ 'static {
|
||||||
|
move |mut ctx: <WB as WasmBackend>::Caller<'_>, inputs: &[WValue]| -> Vec<WValue> {
|
||||||
use wasmer_it::interpreter::stack::Stackable;
|
use wasmer_it::interpreter::stack::Stackable;
|
||||||
|
|
||||||
use super::type_converters::wval_to_ival;
|
use super::type_converters::wval_to_ival;
|
||||||
@ -363,6 +355,7 @@ impl MModule {
|
|||||||
interpreter.run(
|
interpreter.run(
|
||||||
&wit_inputs,
|
&wit_inputs,
|
||||||
Arc::make_mut(&mut wit_instance_callable.assume_init()),
|
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
|
// TODO: optimize by prevent copying stack values
|
||||||
outputs
|
outputs
|
||||||
|
.map_err(|e| log::error!("interpreter got error {e}"))
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.iter()
|
.iter()
|
||||||
@ -402,7 +396,12 @@ impl MModule {
|
|||||||
arguments,
|
arguments,
|
||||||
output_types,
|
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(
|
let raw_import = create_raw_import(
|
||||||
wit_instance.clone(),
|
wit_instance.clone(),
|
||||||
@ -411,7 +410,8 @@ impl MModule {
|
|||||||
import_name.to_string(),
|
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),
|
arguments.iter().map(|IFunctionArg { ty, .. }| ty),
|
||||||
output_types.iter(),
|
output_types.iter(),
|
||||||
raw_import,
|
raw_import,
|
||||||
@ -427,17 +427,49 @@ impl MModule {
|
|||||||
})
|
})
|
||||||
.collect::<MResult<multimap::MultiMap<_, _>>>()?;
|
.collect::<MResult<multimap::MultiMap<_, _>>>()?;
|
||||||
|
|
||||||
let mut import_object = ImportObject::new();
|
|
||||||
|
|
||||||
// TODO: refactor this
|
|
||||||
for (namespace_name, funcs) in wit_import_funcs.into_iter() {
|
for (namespace_name, funcs) in wit_import_funcs.into_iter() {
|
||||||
let mut namespace = Namespace::new();
|
let funcs = funcs.into_iter().map(|(name, f)| (name.to_string(), f));
|
||||||
for (import_name, import_func) in funcs.into_iter() {
|
linker.register(store, namespace_name, funcs)?;
|
||||||
namespace.insert(import_name.to_string(), import_func);
|
|
||||||
}
|
|
||||||
import_object.register(namespace_name, namespace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)) }
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,12 +16,12 @@
|
|||||||
|
|
||||||
mod exports;
|
mod exports;
|
||||||
mod marine_module;
|
mod marine_module;
|
||||||
mod memory;
|
|
||||||
mod wit_function;
|
mod wit_function;
|
||||||
mod wit_instance;
|
mod wit_instance;
|
||||||
mod type_converters;
|
mod type_converters;
|
||||||
|
|
||||||
pub use wit_instance::MRecordTypes;
|
pub use wit_instance::MRecordTypes;
|
||||||
|
|
||||||
pub use wasmer_it::IType;
|
pub use wasmer_it::IType;
|
||||||
pub use wasmer_it::IRecordType;
|
pub use wasmer_it::IRecordType;
|
||||||
pub use wasmer_it::ast::FunctionArg as IFunctionArg;
|
pub use wasmer_it::ast::FunctionArg as IFunctionArg;
|
||||||
@ -31,19 +31,18 @@ pub use wasmer_it::to_interface_value;
|
|||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Represent a function type inside Marine module.
|
/// Represent a function type inside Marine module.
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
|
||||||
pub struct MFunctionSignature {
|
pub struct MFunctionSignature {
|
||||||
pub name: Rc<String>,
|
pub name: Arc<String>,
|
||||||
pub arguments: Rc<Vec<IFunctionArg>>,
|
pub arguments: Arc<Vec<IFunctionArg>>,
|
||||||
pub outputs: Rc<Vec<IType>>,
|
pub outputs: Arc<Vec<IType>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use marine_module::MModule;
|
pub(crate) use marine_module::MModule;
|
||||||
pub(self) use wasmer_core::types::Type as WType;
|
pub(self) use marine_wasm_backend_traits::WValue;
|
||||||
pub(self) use wasmer_core::types::Value as WValue;
|
|
||||||
|
|
||||||
// types that often used together
|
// types that often used together
|
||||||
pub(crate) mod wit_prelude {
|
pub(crate) mod wit_prelude {
|
||||||
@ -51,7 +50,4 @@ pub(crate) mod wit_prelude {
|
|||||||
pub(super) use super::exports::ITExport;
|
pub(super) use super::exports::ITExport;
|
||||||
pub(super) use crate::MError;
|
pub(super) use crate::MError;
|
||||||
pub(super) use super::wit_function::WITFunction;
|
pub(super) use super::wit_function::WITFunction;
|
||||||
|
|
||||||
pub(crate) use super::memory::WITMemoryView;
|
|
||||||
pub(crate) use super::memory::WITMemory;
|
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// Contains converters of types and values between Wasmer and wasmer_interface_types.
|
use super::IType;
|
||||||
use super::{WType, WValue, IType, IValue};
|
use super::IValue;
|
||||||
|
|
||||||
|
use marine_wasm_backend_traits::WType;
|
||||||
|
use marine_wasm_backend_traits::WValue;
|
||||||
|
|
||||||
pub(super) fn wtype_to_itype(ty: &WType) -> IType {
|
pub(super) fn wtype_to_itype(ty: &WType) -> IType {
|
||||||
match ty {
|
match ty {
|
||||||
@ -23,7 +26,10 @@ pub(super) fn wtype_to_itype(ty: &WType) -> IType {
|
|||||||
WType::I64 => IType::I64,
|
WType::I64 => IType::I64,
|
||||||
WType::F32 => IType::F32,
|
WType::F32 => IType::F32,
|
||||||
WType::F64 => IType::F64,
|
WType::F64 => IType::F64,
|
||||||
WType::V128 => unimplemented!(),
|
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::I64(v) => IValue::I64(*v),
|
||||||
WValue::F32(v) => IValue::F32(*v),
|
WValue::F32(v) => IValue::F32(*v),
|
||||||
WValue::F64(v) => IValue::F64(*v),
|
WValue::F64(v) => IValue::F64(*v),
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,42 +15,50 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::marine_module::MModule;
|
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 super::marine_module::Callable;
|
||||||
use crate::MResult;
|
use crate::MResult;
|
||||||
|
|
||||||
use wasmer_it::interpreter::wasm;
|
use marine_wasm_backend_traits::DelayedContextLifetime;
|
||||||
use wasmer_core::instance::DynFunc;
|
use marine_wasm_backend_traits::WasmBackend;
|
||||||
|
use marine_wasm_backend_traits::WValue;
|
||||||
|
use marine_wasm_backend_traits::Function;
|
||||||
|
|
||||||
// use std::sync::Arc;
|
use wasmer_it::interpreter::wasm;
|
||||||
use std::rc::Rc;
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum WITFunctionInner {
|
enum WITFunctionInner<WB: WasmBackend> {
|
||||||
Export {
|
Export {
|
||||||
func: Rc<DynFunc<'static>>,
|
func: Arc<<WB as WasmBackend>::Function>,
|
||||||
},
|
},
|
||||||
Import {
|
Import {
|
||||||
// TODO: use dyn Callable here
|
// 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.
|
/// Represents all import and export functions that could be called from IT context by call-core.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct WITFunction {
|
pub(super) struct WITFunction<WB: WasmBackend> {
|
||||||
name: String,
|
name: String,
|
||||||
arguments: Rc<Vec<IFunctionArg>>,
|
arguments: Arc<Vec<IFunctionArg>>,
|
||||||
outputs: Rc<Vec<IType>>,
|
outputs: Arc<Vec<IType>>,
|
||||||
inner: WITFunctionInner,
|
inner: WITFunctionInner<WB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WITFunction {
|
impl<WB: WasmBackend> WITFunction<WB> {
|
||||||
/// Creates functions from a "usual" (not IT) module export.
|
/// Creates functions from a "usual" (not IT) module export.
|
||||||
pub(super) fn from_export(dyn_func: DynFunc<'static>, name: String) -> MResult<Self> {
|
pub(super) fn from_export(
|
||||||
|
store: &mut <WB as WasmBackend>::Store,
|
||||||
|
dyn_func: <WB as WasmBackend>::Function,
|
||||||
|
name: String,
|
||||||
|
) -> MResult<Self> {
|
||||||
use super::type_converters::wtype_to_itype;
|
use super::type_converters::wtype_to_itype;
|
||||||
|
let signature = dyn_func.signature(store);
|
||||||
let signature = dyn_func.signature();
|
|
||||||
let arguments = signature
|
let arguments = signature
|
||||||
.params()
|
.params()
|
||||||
.iter()
|
.iter()
|
||||||
@ -67,11 +75,11 @@ impl WITFunction {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let inner = WITFunctionInner::Export {
|
let inner = WITFunctionInner::Export {
|
||||||
func: Rc::new(dyn_func),
|
func: Arc::new(dyn_func),
|
||||||
};
|
};
|
||||||
|
|
||||||
let arguments = Rc::new(arguments);
|
let arguments = Arc::new(arguments);
|
||||||
let outputs = Rc::new(outputs);
|
let outputs = Arc::new(outputs);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name,
|
name,
|
||||||
@ -83,11 +91,11 @@ impl WITFunction {
|
|||||||
|
|
||||||
/// Creates function from a module import.
|
/// Creates function from a module import.
|
||||||
pub(super) fn from_import(
|
pub(super) fn from_import(
|
||||||
wit_module: &MModule,
|
wit_module: &MModule<WB>,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
function_name: &str,
|
function_name: &str,
|
||||||
arguments: Rc<Vec<IFunctionArg>>,
|
arguments: Arc<Vec<IFunctionArg>>,
|
||||||
outputs: Rc<Vec<IType>>,
|
outputs: Arc<Vec<IType>>,
|
||||||
) -> MResult<Self> {
|
) -> MResult<Self> {
|
||||||
let callable = wit_module.get_callable(module_name, function_name)?;
|
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 {
|
fn name(&self) -> &str {
|
||||||
self.name.as_str()
|
self.name.as_str()
|
||||||
}
|
}
|
||||||
@ -125,17 +135,28 @@ impl wasm::structures::LocalImport for WITFunction {
|
|||||||
&self.outputs
|
&self.outputs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, arguments: &[IValue]) -> std::result::Result<Vec<IValue>, ()> {
|
fn call(
|
||||||
use super::type_converters::{ival_to_wval, wval_to_ival};
|
&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 {
|
match &self.inner {
|
||||||
WITFunctionInner::Export { func, .. } => func
|
WITFunctionInner::Export { func, .. } => func
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.call(&arguments.iter().map(ival_to_wval).collect::<Vec<WValue>>())
|
.call(
|
||||||
.map(|result| result.iter().map(wval_to_ival).collect())
|
store,
|
||||||
.map_err(|_| ()),
|
arguments
|
||||||
WITFunctionInner::Import { callable, .. } => Rc::make_mut(&mut callable.clone())
|
.iter()
|
||||||
.call(arguments)
|
.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(|_| ()),
|
.map_err(|_| ()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,40 +19,49 @@ use super::marine_module::MModule;
|
|||||||
use super::IRecordType;
|
use super::IRecordType;
|
||||||
use crate::MResult;
|
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::MITInterfaces;
|
||||||
use marine_it_interfaces::ITAstType;
|
use marine_it_interfaces::ITAstType;
|
||||||
|
|
||||||
use wasmer_it::interpreter::wasm;
|
use wasmer_it::interpreter::wasm;
|
||||||
use wasmer_it::interpreter::wasm::structures::{LocalImportIndex, Memory, TypedIndex};
|
use wasmer_it::interpreter::wasm::structures::LocalImportIndex;
|
||||||
use wasmer_core::Instance as WasmerInstance;
|
use wasmer_it::interpreter::wasm::structures::Memory;
|
||||||
|
use wasmer_it::interpreter::wasm::structures::TypedIndex;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
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.
|
/// Contains all import and export functions that could be called from IT context by call-core.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct ITInstance {
|
pub(super) struct ITInstance<WB: WasmBackend> {
|
||||||
/// IT functions indexed by id.
|
/// IT functions indexed by id.
|
||||||
funcs: HashMap<usize, WITFunction>,
|
funcs: HashMap<usize, WITFunction<WB>>,
|
||||||
|
|
||||||
/// IT memories.
|
/// IT memories.
|
||||||
memories: Vec<WITMemory>,
|
memories: Vec<<WB as WasmBackend>::Memory>,
|
||||||
|
|
||||||
/// All record types that instance contains.
|
/// All record types that instance contains.
|
||||||
record_types_by_id: MRecordTypes,
|
record_types_by_id: MRecordTypes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ITInstance {
|
impl<WB: WasmBackend> ITInstance<WB> {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
wasmer_instance: &WasmerInstance,
|
wasm_instance: &<WB as WasmBackend>::Instance,
|
||||||
|
store: &mut <WB as WasmBackend>::Store,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
wit: &MITInterfaces<'_>,
|
wit: &MITInterfaces<'_>,
|
||||||
modules: &HashMap<String, MModule>,
|
modules: &HashMap<String, MModule<WB>>,
|
||||||
) -> MResult<Self> {
|
) -> 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 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);
|
exports.extend(imports);
|
||||||
let funcs = exports;
|
let funcs = exports;
|
||||||
@ -67,27 +76,18 @@ impl ITInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extract_raw_exports(
|
fn extract_raw_exports(
|
||||||
wasmer_instance: &WasmerInstance,
|
wasm_instance: &<WB as WasmBackend>::Instance,
|
||||||
|
store: &mut <WB as WasmBackend>::Store,
|
||||||
it: &MITInterfaces<'_>,
|
it: &MITInterfaces<'_>,
|
||||||
) -> MResult<HashMap<usize, WITFunction>> {
|
) -> MResult<HashMap<usize, WITFunction<WB>>> {
|
||||||
use wasmer_core::DynFunc;
|
|
||||||
|
|
||||||
let module_exports = &wasmer_instance.exports;
|
|
||||||
|
|
||||||
it.exports()
|
it.exports()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(export_id, export)| {
|
.map(|(export_id, export)| {
|
||||||
let export_func = module_exports.get(export.name)?;
|
let export_func = wasm_instance.get_function(store, 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((
|
Ok((
|
||||||
export_id,
|
export_id,
|
||||||
WITFunction::from_export(export_func, export.name.to_string())?,
|
WITFunction::from_export(store, export_func, export.name.to_string())?,
|
||||||
))
|
))
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -95,10 +95,10 @@ impl ITInstance {
|
|||||||
/// Extracts only those imports that don't have implementations.
|
/// Extracts only those imports that don't have implementations.
|
||||||
fn extract_imports(
|
fn extract_imports(
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
modules: &HashMap<String, MModule>,
|
modules: &HashMap<String, MModule<WB>>,
|
||||||
wit: &MITInterfaces<'_>,
|
wit: &MITInterfaces<'_>,
|
||||||
start_index: usize,
|
start_index: usize,
|
||||||
) -> MResult<HashMap<usize, WITFunction>> {
|
) -> MResult<HashMap<usize, WITFunction<WB>>> {
|
||||||
wit.imports()
|
wit.imports()
|
||||||
.filter(|import|
|
.filter(|import|
|
||||||
// filter out imports that have implementations
|
// filter out imports that have implementations
|
||||||
@ -129,29 +129,29 @@ impl ITInstance {
|
|||||||
output_types,
|
output_types,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok((start_index + idx as usize, func))
|
Ok((start_index + idx, func))
|
||||||
}
|
}
|
||||||
None => Err(MError::NoSuchModule(import.namespace.to_string())),
|
None => Err(MError::NoSuchModule(import.namespace.to_string())),
|
||||||
})
|
})
|
||||||
.collect::<MResult<HashMap<_, _>>>()
|
.collect::<MResult<HashMap<_, _>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_memories(wasmer_instance: &WasmerInstance) -> Vec<WITMemory> {
|
fn extract_memories(
|
||||||
use wasmer_core::export::Export::Memory;
|
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
|
let mut memories = wasm_instance
|
||||||
.exports()
|
.export_iter(store.as_context_mut())
|
||||||
.filter_map(|(_, export)| match export {
|
.filter_map(|(_, export)| match export {
|
||||||
Memory(memory) => Some(WITMemory(memory)),
|
Memory(memory) => Some(memory),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if let Some(Memory(memory)) = wasmer_instance
|
if let Ok(memory) = wasm_instance.get_memory(store, STANDARD_MEMORY_EXPORT_NAME) {
|
||||||
.import_object
|
memories.push(memory);
|
||||||
.maybe_with_namespace("env", |env| env.get_export("memory"))
|
|
||||||
{
|
|
||||||
memories.push(WITMemory(memory));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memories
|
memories
|
||||||
@ -175,19 +175,28 @@ impl ITInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'v> wasm::structures::Instance<ITExport, WITFunction, WITMemory, WITMemoryView<'v>>
|
impl<WB: WasmBackend>
|
||||||
for ITInstance
|
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> {
|
fn export(&self, _export_name: &str) -> Option<&ITExport> {
|
||||||
// exports aren't used in this version of IT
|
// exports aren't used in this version of IT
|
||||||
None
|
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())
|
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() {
|
if index >= self.memories.len() {
|
||||||
None
|
None
|
||||||
} else {
|
} 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() {
|
if index >= self.memories.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let memory = &self.memories[index];
|
let memory = &self.memories[index];
|
||||||
let view: WITMemoryView<'static> = memory.view();
|
let view: <WB as WasmBackend>::MemoryView = memory.view();
|
||||||
Some(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)
|
self.record_types_by_id.get(&index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,9 @@ static GREETING_WASM_BYTES: Lazy<Vec<u8>> = Lazy::new(|| {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn greeting_basic() {
|
pub fn greeting_basic() {
|
||||||
let mut marine_core = MarineCore::new();
|
let mut marine_core = MarineCore::new().unwrap();
|
||||||
marine_core
|
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));
|
.unwrap_or_else(|e| panic!("can't load a module into Marine: {:?}", e));
|
||||||
|
|
||||||
let result1 = marine_core
|
let result1 = marine_core
|
||||||
@ -50,13 +50,13 @@ pub fn greeting_basic() {
|
|||||||
#[test]
|
#[test]
|
||||||
// test loading module with the same name twice
|
// test loading module with the same name twice
|
||||||
pub fn non_unique_module_name() {
|
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");
|
let module_name = String::from("greeting");
|
||||||
marine_core
|
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));
|
.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!(load_result.is_err());
|
||||||
assert!(std::matches!(
|
assert!(std::matches!(
|
||||||
load_result.err().unwrap(),
|
load_result.err().unwrap(),
|
||||||
@ -68,9 +68,9 @@ pub fn non_unique_module_name() {
|
|||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
// test calling Marine with non-exist module and function names
|
// test calling Marine with non-exist module and function names
|
||||||
pub fn non_exist_module_func() {
|
pub fn non_exist_module_func() {
|
||||||
let mut marine_core = MarineCore::new();
|
let mut marine_core = MarineCore::new().unwrap();
|
||||||
marine_core
|
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));
|
.unwrap_or_else(|e| panic!("can't load a module into Marine: {:?}", e));
|
||||||
|
|
||||||
let module_name = "greeting";
|
let module_name = "greeting";
|
||||||
|
@ -25,7 +25,7 @@ pub fn records() {
|
|||||||
let pure_wasm_bytes = std::fs::read("../examples/records/artifacts/records_pure.wasm")
|
let pure_wasm_bytes = std::fs::read("../examples/records/artifacts/records_pure.wasm")
|
||||||
.expect("../examples/records/artifacts/records_pure.wasm should presence");
|
.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());
|
let load_result = marine_core.load_module("pure", &pure_wasm_bytes, <_>::default());
|
||||||
assert!(load_result.is_err());
|
assert!(load_result.is_err());
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ pub async fn download(url: &str) -> bytes::Bytes {
|
|||||||
async fn redis() {
|
async fn redis() {
|
||||||
let wasm_bytes = download(REDIS_DOWNLOAD_URL).await;
|
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 module_name = "redis";
|
||||||
let config = <_>::default();
|
let config = <_>::default();
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ async fn redis() {
|
|||||||
async fn sqlite() {
|
async fn sqlite() {
|
||||||
let wasm_bytes = download(SQLITE_DOWNLOAD_URL).await;
|
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 module_name = "sqlite";
|
||||||
let config = <_>::default();
|
let config = <_>::default();
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "lilo-after-2gb-test"
|
name = "lilo-after-2gb-test"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@ -4,11 +4,13 @@ description = "Fluence Application Service"
|
|||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
marine-runtime = { path = "../../marine", version = "0.25.0" }
|
marine-runtime = { path = "../../marine", version = "0.25.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 = "../wasm-backend-traits", version = "0.1.0"}
|
||||||
|
marine-wasmtime-backend = { path = "../wasmtime-backend", version = "0.1.0"}
|
||||||
|
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
@ -16,7 +18,6 @@ serde = "1.0.147"
|
|||||||
serde_derive = "1.0.147"
|
serde_derive = "1.0.147"
|
||||||
serde_json = "1.0.89"
|
serde_json = "1.0.89"
|
||||||
toml = "0.5.9"
|
toml = "0.5.9"
|
||||||
wasmer-wasi = { package = "wasmer-wasi-fl", version = "0.17.1" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
raw-module-api = ["marine-runtime/raw-module-api"]
|
raw-module-api = ["marine-runtime/raw-module-api"]
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use marine::MarineConfig;
|
use marine::MarineConfig;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// Describes behaviour of the Fluence AppService.
|
/// Describes behaviour of the Fluence AppService.
|
||||||
|
@ -19,6 +19,7 @@ use crate::AppServiceError;
|
|||||||
use crate::config::AppServiceConfig;
|
use crate::config::AppServiceConfig;
|
||||||
|
|
||||||
use marine::TomlMarineConfig;
|
use marine::TomlMarineConfig;
|
||||||
|
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
|
@ -20,9 +20,12 @@ use crate::MemoryStats;
|
|||||||
use crate::service_interface::ServiceInterface;
|
use crate::service_interface::ServiceInterface;
|
||||||
use super::AppServiceError;
|
use super::AppServiceError;
|
||||||
|
|
||||||
|
#[cfg(feature = "raw-module-api")]
|
||||||
|
use marine_wasm_backend_traits::WasiState;
|
||||||
use marine::Marine;
|
use marine::Marine;
|
||||||
use marine::MarineModuleConfig;
|
use marine::MarineModuleConfig;
|
||||||
use marine::IValue;
|
use marine::IValue;
|
||||||
|
|
||||||
use serde_json::Value as JValue;
|
use serde_json::Value as JValue;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
|
|
||||||
@ -44,8 +47,8 @@ impl AppService {
|
|||||||
/// Create Service with given modules and service id.
|
/// 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>
|
pub fn new<C, S>(config: C, service_id: S, envs: HashMap<Vec<u8>, Vec<u8>>) -> Result<Self>
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
|
||||||
C: TryInto<AppServiceConfig>,
|
C: TryInto<AppServiceConfig>,
|
||||||
|
S: Into<String>,
|
||||||
AppServiceError: From<C::Error>,
|
AppServiceError: From<C::Error>,
|
||||||
{
|
{
|
||||||
let mut config: AppServiceConfig = config.try_into()?;
|
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<()>
|
pub fn load_module<C, S>(&mut self, name: S, wasm_bytes: &[u8], config: Option<C>) -> Result<()>
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
C: TryInto<crate::MarineModuleConfig>,
|
C: TryInto<marine::MarineModuleConfig>,
|
||||||
marine::MarineError: From<C::Error>,
|
marine::MarineError: From<C::Error>,
|
||||||
{
|
{
|
||||||
self.marine
|
self.marine
|
||||||
@ -243,7 +246,7 @@ impl AppService {
|
|||||||
pub fn get_wasi_state(
|
pub fn get_wasi_state(
|
||||||
&mut self,
|
&mut self,
|
||||||
module_name: impl AsRef<str>,
|
module_name: impl AsRef<str>,
|
||||||
) -> Result<&wasmer_wasi::state::WasiState> {
|
) -> Result<Box<dyn WasiState + '_>> {
|
||||||
self.marine
|
self.marine
|
||||||
.module_wasi_state(module_name)
|
.module_wasi_state(module_name)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
|
@ -22,7 +22,7 @@ use marine::itype_text_view;
|
|||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct FunctionSignature {
|
pub struct FunctionSignature {
|
||||||
@ -91,7 +91,7 @@ fn serialize_function_signature(
|
|||||||
|
|
||||||
fn serialize_record_type(
|
fn serialize_record_type(
|
||||||
id: u64,
|
id: u64,
|
||||||
record: Rc<IRecordType>,
|
record: Arc<IRecordType>,
|
||||||
record_types: &MRecordTypes,
|
record_types: &MRecordTypes,
|
||||||
) -> RecordType {
|
) -> RecordType {
|
||||||
let fields = record
|
let fields = record
|
||||||
|
@ -4,7 +4,7 @@ description = "Fluence Marine interface types generator"
|
|||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "marine_it_generator"
|
name = "marine_it_generator"
|
||||||
@ -14,8 +14,8 @@ path = "src/lib.rs"
|
|||||||
marine-it-parser = { path = "../it-parser", version = "0.11.1" }
|
marine-it-parser = { path = "../it-parser", version = "0.11.1" }
|
||||||
marine-macro-impl = "0.7.1"
|
marine-macro-impl = "0.7.1"
|
||||||
|
|
||||||
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1"}
|
wasmer-it = { workspace = true }
|
||||||
it-lilo = "0.4.1"
|
it-lilo = { workspace = true}
|
||||||
|
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
walrus = "0.19.0"
|
walrus = "0.19.0"
|
||||||
|
@ -19,7 +19,7 @@ use wasmer_it::IType;
|
|||||||
use wasmer_it::ast::FunctionArg as IFunctionArg;
|
use wasmer_it::ast::FunctionArg as IFunctionArg;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub(crate) struct ApiExportFuncDescriptor {
|
pub(crate) struct ApiExportFuncDescriptor {
|
||||||
pub(crate) name: &'static str,
|
pub(crate) name: &'static str,
|
||||||
@ -31,8 +31,8 @@ pub(crate) struct ApiExportFuncDescriptor {
|
|||||||
impl ApiExportFuncDescriptor {
|
impl ApiExportFuncDescriptor {
|
||||||
pub fn update_interfaces(&self, interfaces: &mut Interfaces<'_>) {
|
pub fn update_interfaces(&self, interfaces: &mut Interfaces<'_>) {
|
||||||
let func_type = wasmer_it::ast::Type::Function {
|
let func_type = wasmer_it::ast::Type::Function {
|
||||||
arguments: Rc::new(self.arguments.clone()),
|
arguments: Arc::new(self.arguments.clone()),
|
||||||
output_types: Rc::new(self.output_types.clone()),
|
output_types: Arc::new(self.output_types.clone()),
|
||||||
};
|
};
|
||||||
interfaces.types.push(func_type);
|
interfaces.types.push(func_type);
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ use wasmer_it::IType;
|
|||||||
use wasmer_it::ast::Interfaces;
|
use wasmer_it::ast::Interfaces;
|
||||||
use wasmer_it::IRecordType;
|
use wasmer_it::IRecordType;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Default)]
|
#[derive(PartialEq, Eq, Debug, Default)]
|
||||||
pub(crate) struct ITResolver<'a> {
|
pub(crate) struct ITResolver<'a> {
|
||||||
@ -58,7 +58,7 @@ impl<'a> ITResolver<'a> {
|
|||||||
self.types.insert(record_name.to_string(), new_type_id);
|
self.types.insert(record_name.to_string(), new_type_id);
|
||||||
self.interfaces
|
self.interfaces
|
||||||
.types
|
.types
|
||||||
.push(Type::Record(Rc::new(IRecordType::default())));
|
.push(Type::Record(Arc::new(IRecordType::default())));
|
||||||
|
|
||||||
self.unresolved_types_count += 1;
|
self.unresolved_types_count += 1;
|
||||||
new_type_id
|
new_type_id
|
||||||
@ -87,14 +87,14 @@ impl<'a> ITResolver<'a> {
|
|||||||
|
|
||||||
match self.types.get(&record.name) {
|
match self.types.get(&record.name) {
|
||||||
Some(pos) => {
|
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;
|
self.unresolved_types_count -= 1;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.types
|
self.types
|
||||||
.insert(record.name.clone(), self.interfaces.types.len());
|
.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(
|
pub(crate) fn add_fn_type(
|
||||||
&mut self,
|
&mut self,
|
||||||
arguments: Rc<Vec<wasmer_it::ast::FunctionArg>>,
|
arguments: Arc<Vec<wasmer_it::ast::FunctionArg>>,
|
||||||
output_types: Rc<Vec<IType>>,
|
output_types: Arc<Vec<IType>>,
|
||||||
) {
|
) {
|
||||||
let fn_type = wasmer_it::ast::Type::Function {
|
let fn_type = wasmer_it::ast::Type::Function {
|
||||||
arguments,
|
arguments,
|
||||||
|
@ -25,16 +25,16 @@ use wasmer_it::IType;
|
|||||||
|
|
||||||
/// Generates IT instructions for a argument of an export function.
|
/// Generates IT instructions for a argument of an export function.
|
||||||
pub(super) trait ArgumentITGenerator {
|
pub(super) trait ArgumentITGenerator {
|
||||||
fn generate_instructions_for_arg<'a>(
|
fn generate_instructions_for_arg(
|
||||||
&self,
|
&self,
|
||||||
arg_id: u32,
|
arg_id: u32,
|
||||||
it_resolver: &mut ITResolver<'a>,
|
it_resolver: &mut ITResolver<'_>,
|
||||||
) -> Result<Vec<Instruction>>;
|
) -> Result<Vec<Instruction>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArgumentITGenerator for ParsedType {
|
impl ArgumentITGenerator for ParsedType {
|
||||||
#[rustfmt::skip]
|
#[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 {
|
let instructions = match self {
|
||||||
ParsedType::Boolean(_) => vec![Instruction::ArgumentGet { index }, Instruction::I32FromBool],
|
ParsedType::Boolean(_) => vec![Instruction::ArgumentGet { index }, Instruction::I32FromBool],
|
||||||
ParsedType::I8(_) => vec![Instruction::ArgumentGet { index }, Instruction::I32FromS8],
|
ParsedType::I8(_) => vec![Instruction::ArgumentGet { index }, Instruction::I32FromS8],
|
||||||
|
@ -25,15 +25,15 @@ use wasmer_it::IType;
|
|||||||
|
|
||||||
/// Generates IT instructions for a output type of an export function.
|
/// Generates IT instructions for a output type of an export function.
|
||||||
pub(super) trait OutputITGenerator {
|
pub(super) trait OutputITGenerator {
|
||||||
fn generate_instructions_for_output_type<'a>(
|
fn generate_instructions_for_output_type(
|
||||||
&self,
|
&self,
|
||||||
it_resolver: &mut ITResolver<'a>,
|
it_resolver: &mut ITResolver<'_>,
|
||||||
) -> Result<Vec<Instruction>>;
|
) -> Result<Vec<Instruction>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputITGenerator for ParsedType {
|
impl OutputITGenerator for ParsedType {
|
||||||
#[rustfmt::skip]
|
#[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 {
|
let instructions = match self {
|
||||||
ParsedType::Boolean(_) => vec![Instruction::BoolFromI32],
|
ParsedType::Boolean(_) => vec![Instruction::BoolFromI32],
|
||||||
ParsedType::I8(_) => vec![Instruction::S8FromI32],
|
ParsedType::I8(_) => vec![Instruction::S8FromI32],
|
||||||
|
@ -33,7 +33,7 @@ use wasmer_it::ast::FunctionArg as IFunctionArg;
|
|||||||
use wasmer_it::interpreter::Instruction;
|
use wasmer_it::interpreter::Instruction;
|
||||||
use wasmer_it::IType;
|
use wasmer_it::IType;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
const HOST_NAMESPACE_NAME: &str = "host";
|
const HOST_NAMESPACE_NAME: &str = "host";
|
||||||
|
|
||||||
@ -148,17 +148,17 @@ fn generate_it_instructions<'f>(
|
|||||||
Ok(())
|
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
|
let raw_inputs = signature
|
||||||
.arguments
|
.arguments
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(to_raw_input_types)
|
.flat_map(to_raw_input_types)
|
||||||
.collect::<Vec<_>>();
|
.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
|
let raw_outputs = signature
|
||||||
.output_types
|
.output_types
|
||||||
.iter()
|
.iter()
|
||||||
@ -170,7 +170,7 @@ pub(crate) fn generate_raw_output_type(signature: &FnSignature) -> Rc<Vec<IType>
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Rc::new(raw_outputs)
|
Arc::new(raw_outputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
use marine_macro_impl::RustType;
|
use marine_macro_impl::RustType;
|
||||||
|
@ -24,19 +24,19 @@ use wasmer_it::IType;
|
|||||||
|
|
||||||
/// Generate IT instructions for a foreign mod.
|
/// Generate IT instructions for a foreign mod.
|
||||||
pub(super) trait ArgumentITGenerator {
|
pub(super) trait ArgumentITGenerator {
|
||||||
fn generate_instructions_for_arg<'a>(
|
fn generate_instructions_for_arg(
|
||||||
&self,
|
&self,
|
||||||
arg_id: u32,
|
arg_id: u32,
|
||||||
it_resolver: &mut ITResolver<'a>,
|
it_resolver: &mut ITResolver<'_>,
|
||||||
) -> Result<(Vec<Instruction>, u32)>;
|
) -> Result<(Vec<Instruction>, u32)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
impl ArgumentITGenerator for ParsedType {
|
impl ArgumentITGenerator for ParsedType {
|
||||||
fn generate_instructions_for_arg<'a>(
|
fn generate_instructions_for_arg(
|
||||||
&self,
|
&self,
|
||||||
index: u32,
|
index: u32,
|
||||||
it_resolver: &mut ITResolver<'a>,
|
it_resolver: &mut ITResolver<'_>,
|
||||||
) -> Result<(Vec<Instruction>, u32)> {
|
) -> Result<(Vec<Instruction>, u32)> {
|
||||||
let instructions = match self {
|
let instructions = match self {
|
||||||
ParsedType::Boolean(_) => (vec![Instruction::ArgumentGet { index }, Instruction::BoolFromI32], 1),
|
ParsedType::Boolean(_) => (vec![Instruction::ArgumentGet { index }, Instruction::BoolFromI32], 1),
|
||||||
|
@ -25,16 +25,16 @@ use wasmer_it::IType;
|
|||||||
|
|
||||||
/// Generate IT instructions for a foreign mod.
|
/// Generate IT instructions for a foreign mod.
|
||||||
pub(super) trait OutputITGenerator {
|
pub(super) trait OutputITGenerator {
|
||||||
fn generate_instructions_for_output_type<'a>(
|
fn generate_instructions_for_output_type(
|
||||||
&self,
|
&self,
|
||||||
it_resolver: &mut ITResolver<'a>,
|
it_resolver: &mut ITResolver<'_>,
|
||||||
) -> Result<Vec<Instruction>>;
|
) -> Result<Vec<Instruction>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
impl OutputITGenerator for ParsedType {
|
impl OutputITGenerator for ParsedType {
|
||||||
#[rustfmt::skip]
|
#[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 {
|
let instructions = match self {
|
||||||
ParsedType::Boolean(_) => vec![Instruction::I32FromBool],
|
ParsedType::Boolean(_) => vec![Instruction::I32FromBool],
|
||||||
ParsedType::I8(_) => vec![Instruction::I32FromS8],
|
ParsedType::I8(_) => vec![Instruction::I32FromS8],
|
||||||
|
@ -23,7 +23,7 @@ use marine_macro_impl::ParsedType;
|
|||||||
use marine_macro_impl::RustType;
|
use marine_macro_impl::RustType;
|
||||||
use wasmer_it::ast::FunctionArg as IFunctionArg;
|
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
|
// return error if there is no record with such name
|
||||||
pub(crate) fn ptype_to_itype_checked(
|
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,
|
signature: &FnSignature,
|
||||||
it_resolver: &mut ITResolver<'f>,
|
it_resolver: &mut ITResolver<'_>,
|
||||||
) -> Result<Rc<Vec<IFunctionArg>>> {
|
) -> Result<Arc<Vec<IFunctionArg>>> {
|
||||||
let arguments = signature
|
let arguments = signature
|
||||||
.arguments
|
.arguments
|
||||||
.iter()
|
.iter()
|
||||||
@ -122,21 +122,21 @@ pub(crate) fn generate_it_args<'f>(
|
|||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
let arguments = Rc::new(arguments);
|
let arguments = Arc::new(arguments);
|
||||||
Ok(arguments)
|
Ok(arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generate_it_output_type<'f>(
|
pub(crate) fn generate_it_output_type(
|
||||||
signature: &FnSignature,
|
signature: &FnSignature,
|
||||||
it_resolver: &mut ITResolver<'f>,
|
it_resolver: &mut ITResolver<'_>,
|
||||||
) -> Result<Rc<Vec<IType>>> {
|
) -> Result<Arc<Vec<IType>>> {
|
||||||
let output_types = signature
|
let output_types = signature
|
||||||
.output_types
|
.output_types
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ptype_to_itype_checked(ty, it_resolver))
|
.map(|ty| ptype_to_itype_checked(ty, it_resolver))
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
let output_types = Rc::new(output_types);
|
let output_types = Arc::new(output_types);
|
||||||
|
|
||||||
Ok(output_types)
|
Ok(output_types)
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ description = "Fluence Marine interface types helper crate"
|
|||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "marine_it_interfaces"
|
name = "marine_it_interfaces"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1"}
|
wasmer-it = { workspace = true}
|
||||||
multimap = "0.8.3"
|
multimap = "0.8.3"
|
||||||
|
@ -23,7 +23,7 @@ use multimap::MultiMap;
|
|||||||
|
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub type CoreFunctionType = u32;
|
pub type CoreFunctionType = u32;
|
||||||
pub type AdapterFunctionType = u32;
|
pub type AdapterFunctionType = u32;
|
||||||
@ -111,7 +111,7 @@ impl<'a> MITInterfaces<'a> {
|
|||||||
self.types.iter()
|
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 {
|
self.types.iter().enumerate().filter_map(|(id, t)| match t {
|
||||||
ITAstType::Record(r) => Some((id as u64, r)),
|
ITAstType::Record(r) => Some((id as u64, r)),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -4,14 +4,14 @@ description = "Fluence Marine interface-types serde tools"
|
|||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "it_json_serde"
|
name = "it_json_serde"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1"}
|
wasmer-it = { workspace = true}
|
||||||
|
|
||||||
serde = { version = "1.0.147", features = ["derive"] }
|
serde = { version = "1.0.147", features = ["derive"] }
|
||||||
serde_json = "1.0.89"
|
serde_json = "1.0.89"
|
||||||
|
@ -33,9 +33,9 @@ pub use ivalues_to_json::ivalues_to_json;
|
|||||||
pub use json_to_ivalues::json_to_ivalues;
|
pub use json_to_ivalues::json_to_ivalues;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub(crate) use wasmer_it::IValue;
|
pub(crate) use wasmer_it::IValue;
|
||||||
pub(crate) use wasmer_it::IType;
|
pub(crate) use wasmer_it::IType;
|
||||||
pub(crate) use wasmer_it::IRecordType;
|
pub(crate) use wasmer_it::IRecordType;
|
||||||
pub(crate) type MRecordTypes = HashMap<u64, Rc<IRecordType>>;
|
pub(crate) type MRecordTypes = HashMap<u64, Arc<IRecordType>>;
|
||||||
|
@ -4,7 +4,7 @@ description = "Fluence Marine interface types parser"
|
|||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "marine_it_parser"
|
name = "marine_it_parser"
|
||||||
@ -13,12 +13,12 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
marine-it-interfaces = { path = "../it-interfaces", version = "0.7.3" }
|
marine-it-interfaces = { path = "../it-interfaces", version = "0.7.3" }
|
||||||
marine-module-interface = { path = "../module-interface", version = "0.6.1" }
|
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"
|
anyhow = "1.0.66"
|
||||||
walrus = "0.19.0"
|
walrus = "0.19.0"
|
||||||
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1"}
|
wasmer-it = { workspace = true }
|
||||||
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1" }
|
nom = "7.1"
|
||||||
nom = "5.1"
|
|
||||||
|
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
semver = "1.0.14"
|
semver = "1.0.14"
|
||||||
|
@ -27,7 +27,7 @@ pub fn delete_it_section_from_file(
|
|||||||
out_wasm_path: PathBuf,
|
out_wasm_path: PathBuf,
|
||||||
) -> Result<(), ITParserError> {
|
) -> Result<(), ITParserError> {
|
||||||
let module = ModuleConfig::new()
|
let module = ModuleConfig::new()
|
||||||
.parse_file(&in_wasm_path)
|
.parse_file(in_wasm_path)
|
||||||
.map_err(ITParserError::CorruptedWasmFile)?;
|
.map_err(ITParserError::CorruptedWasmFile)?;
|
||||||
|
|
||||||
let mut module = delete_it_section(module);
|
let mut module = delete_it_section(module);
|
||||||
|
@ -19,10 +19,9 @@ use super::errors::ITParserError;
|
|||||||
use crate::ParserResult;
|
use crate::ParserResult;
|
||||||
|
|
||||||
use walrus::ModuleConfig;
|
use walrus::ModuleConfig;
|
||||||
use wasmer_it::{
|
use wasmer_it::ast::Interfaces;
|
||||||
ast::Interfaces,
|
use wasmer_it::decoders::wat::parse;
|
||||||
decoders::wat::{parse, Buffer},
|
use wasmer_it::decoders::wat::Buffer;
|
||||||
};
|
|
||||||
use wasmer_it::ToBytes;
|
use wasmer_it::ToBytes;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -45,15 +45,15 @@ pub enum ITParserError {
|
|||||||
CorruptedITSection(nom::Err<(Vec<u8>, nom::error::ErrorKind)>),
|
CorruptedITSection(nom::Err<(Vec<u8>, nom::error::ErrorKind)>),
|
||||||
|
|
||||||
/// An error related to incorrect data in IT section.
|
/// An error related to incorrect data in IT section.
|
||||||
#[error("{0}")]
|
#[error("0")]
|
||||||
IncorrectITFormat(String),
|
IncorrectITFormat(String), // TODO: use a proper error type
|
||||||
|
|
||||||
/// An error occurred while processing module interface.
|
/// An error occurred while processing module interface.
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
ModuleInterfaceError(#[from] InterfaceError),
|
ModuleInterfaceError(#[from] InterfaceError),
|
||||||
|
|
||||||
/// An error occurred while processing module IT interface.
|
/// An error occurred while processing module IT interface.
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
ModuleITInterfaceError(#[from] ITInterfaceError),
|
ModuleITInterfaceError(#[from] ITInterfaceError),
|
||||||
|
|
||||||
/// An error occurred while parsing file in Wat format.
|
/// An error occurred while parsing file in Wat format.
|
||||||
|
@ -20,7 +20,8 @@ use crate::ParserResult;
|
|||||||
|
|
||||||
use walrus::IdsToIndices;
|
use walrus::IdsToIndices;
|
||||||
use wasmer_it::ast::Interfaces;
|
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::borrow::Cow;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@ -42,16 +43,18 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts IT section of provided Wasm binary and converts it to a MITInterfaces.
|
/// Extracts IT section of provided Wasm binary and converts it to a MITInterfaces.
|
||||||
pub fn extract_it_from_module(wasmer_module: &WasmerModule) -> ParserResult<Interfaces<'_>> {
|
pub fn extract_it_from_module<WB: WasmBackend>(
|
||||||
let wit_sections = wasmer_module
|
wasm_module: &<WB as WasmBackend>::Module,
|
||||||
.custom_sections(IT_SECTION_NAME)
|
) -> ParserResult<Interfaces<'_>> {
|
||||||
.ok_or(ITParserError::NoITSection)?;
|
let wit_sections = wasm_module.custom_sections(IT_SECTION_NAME);
|
||||||
|
|
||||||
if wit_sections.len() > 1 {
|
let it_section = match wit_sections.len() {
|
||||||
return Err(ITParserError::MultipleITSections);
|
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> {
|
pub fn extract_version_from_module(module: &walrus::Module) -> ParserResult<semver::Version> {
|
||||||
|
@ -4,7 +4,7 @@ version = "0.2.1"
|
|||||||
description = "Fluence Marine interface types minimum supported version checker"
|
description = "Fluence Marine interface types minimum supported version checker"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "marine_min_it_version"
|
name = "marine_min_it_version"
|
||||||
|
@ -4,7 +4,7 @@ description = "Fluence Marine Wasm module info (manifest and version) parser"
|
|||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "marine_module_info_parser"
|
name = "marine_module_info_parser"
|
||||||
@ -13,7 +13,7 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
marine-rs-sdk-main = "0.7.1"
|
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"
|
anyhow = "1.0.66"
|
||||||
chrono = "0.4.23"
|
chrono = "0.4.23"
|
||||||
|
@ -30,11 +30,11 @@ pub enum ModuleInfoError {
|
|||||||
MultipleCustomSections(&'static str, usize),
|
MultipleCustomSections(&'static str, usize),
|
||||||
|
|
||||||
/// Errors related to corrupted version.
|
/// Errors related to corrupted version.
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
VersionError(#[from] SDKVersionError),
|
VersionError(#[from] SDKVersionError),
|
||||||
|
|
||||||
/// Errors related to corrupted manifest.
|
/// Errors related to corrupted manifest.
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
ManifestError(#[from] ManifestError),
|
ManifestError(#[from] ManifestError),
|
||||||
|
|
||||||
/// An error occurred while parsing Wasm file.
|
/// An error occurred while parsing Wasm file.
|
||||||
|
@ -20,7 +20,9 @@ use crate::ModuleInfoError;
|
|||||||
use crate::extract_custom_sections_by_name;
|
use crate::extract_custom_sections_by_name;
|
||||||
use crate::try_as_one_section;
|
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 marine_rs_sdk_main::MANIFEST_SECTION_NAME;
|
||||||
use walrus::ModuleConfig;
|
use walrus::ModuleConfig;
|
||||||
use walrus::Module;
|
use walrus::Module;
|
||||||
@ -29,7 +31,7 @@ use std::borrow::Cow;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::convert::TryInto;
|
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
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
@ -40,12 +42,8 @@ where
|
|||||||
extract_from_module(&module)
|
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)?;
|
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(§ions, MANIFEST_SECTION_NAME)?;
|
let section = try_as_one_section(§ions, MANIFEST_SECTION_NAME)?;
|
||||||
|
|
||||||
let manifest = match section {
|
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(),
|
Cow::Owned(vec) => vec.as_slice().try_into(),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
Ok(Some(manifest))
|
Ok(manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_from_wasmer_module(
|
pub fn extract_from_compiled_module<WB: WasmBackend>(
|
||||||
wasmer_module: &WasmerModule,
|
module: &<WB as WasmBackend>::Module,
|
||||||
) -> ModuleInfoResult<Option<ModuleManifest>> {
|
) -> ModuleInfoResult<ModuleManifest> {
|
||||||
let sections = wasmer_module.custom_sections(MANIFEST_SECTION_NAME);
|
let sections = module.custom_sections(MANIFEST_SECTION_NAME);
|
||||||
let sections = match sections {
|
|
||||||
Some(sections) => sections,
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let section = try_as_one_section(sections, MANIFEST_SECTION_NAME)?;
|
let section = try_as_one_section(sections, MANIFEST_SECTION_NAME)?;
|
||||||
let manifest = section.as_slice().try_into()?;
|
let manifest = section.as_slice().try_into()?;
|
||||||
|
|
||||||
Ok(Some(manifest))
|
Ok(manifest)
|
||||||
}
|
}
|
||||||
|
@ -23,5 +23,5 @@ mod tests;
|
|||||||
pub use errors::ManifestError;
|
pub use errors::ManifestError;
|
||||||
pub use manifest_extractor::extract_from_path;
|
pub use manifest_extractor::extract_from_path;
|
||||||
pub use manifest_extractor::extract_from_module;
|
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;
|
pub use module_manifest::ModuleManifest;
|
||||||
|
@ -109,7 +109,7 @@ fn test_too_big_field_len() {
|
|||||||
array.add_utf8_field("repository");
|
array.add_utf8_field("repository");
|
||||||
|
|
||||||
let actual: Result<ModuleManifest, _> = array.as_bytes().try_into();
|
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);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ fn test_without_one_field() {
|
|||||||
array.add_utf8_field("description");
|
array.add_utf8_field("description");
|
||||||
|
|
||||||
let actual: Result<ModuleManifest, _> = array.as_bytes().try_into();
|
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);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
@ -131,7 +131,7 @@ fn test_without_one_field() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_with_empty_slice() {
|
fn test_with_empty_slice() {
|
||||||
let actual: Result<ModuleManifest, _> = vec![].as_slice().try_into();
|
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);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ fn test_not_enough_data_for_field() {
|
|||||||
array.add_utf8_string("repository");
|
array.add_utf8_string("repository");
|
||||||
|
|
||||||
let actual: Result<ModuleManifest, _> = array.as_bytes().try_into();
|
let actual: Result<ModuleManifest, _> = array.as_bytes().try_into();
|
||||||
let expected = Err(ManifestError::NotEnoughBytesForField(
|
let expected: Result<_, _> = Err(ManifestError::NotEnoughBytesForField(
|
||||||
"repository",
|
"repository",
|
||||||
too_big_size as usize,
|
too_big_size as usize,
|
||||||
));
|
));
|
||||||
|
@ -21,6 +21,6 @@ mod version_extractor;
|
|||||||
pub use errors::SDKVersionError;
|
pub use errors::SDKVersionError;
|
||||||
pub use version_extractor::extract_from_path;
|
pub use version_extractor::extract_from_path;
|
||||||
pub use version_extractor::extract_from_module;
|
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_path;
|
||||||
pub use version_embedder::embed_from_module;
|
pub use version_embedder::embed_from_module;
|
||||||
|
@ -20,7 +20,9 @@ use super::SDKVersionError;
|
|||||||
use crate::extract_custom_sections_by_name;
|
use crate::extract_custom_sections_by_name;
|
||||||
use crate::try_as_one_section;
|
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 marine_rs_sdk_main::VERSION_SECTION_NAME;
|
||||||
use walrus::ModuleConfig;
|
use walrus::ModuleConfig;
|
||||||
use walrus::Module;
|
use walrus::Module;
|
||||||
@ -29,7 +31,7 @@ use std::borrow::Cow;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::path::Path;
|
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
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
@ -40,12 +42,8 @@ where
|
|||||||
extract_from_module(&module)
|
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)?;
|
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(§ions, VERSION_SECTION_NAME)?;
|
let section = try_as_one_section(§ions, VERSION_SECTION_NAME)?;
|
||||||
|
|
||||||
let version = match section {
|
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),
|
Cow::Owned(vec) => as_semver(vec),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
Ok(Some(version))
|
Ok(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_from_wasmer_module(
|
pub fn extract_from_compiled_module<WB: WasmBackend>(
|
||||||
wasmer_module: &WasmerModule,
|
wasm_module: &<WB as WasmBackend>::Module,
|
||||||
) -> ModuleInfoResult<Option<semver::Version>> {
|
) -> ModuleInfoResult<semver::Version> {
|
||||||
let sections = wasmer_module.custom_sections(VERSION_SECTION_NAME);
|
let sections = wasm_module.custom_sections(VERSION_SECTION_NAME);
|
||||||
|
|
||||||
let sections = match sections {
|
|
||||||
Some(sections) => sections,
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let section = try_as_one_section(sections, VERSION_SECTION_NAME)?;
|
let section = try_as_one_section(sections, VERSION_SECTION_NAME)?;
|
||||||
let version = as_semver(section)?;
|
let version = as_semver(section)?;
|
||||||
|
|
||||||
Ok(Some(version))
|
Ok(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_semver(version_as_bytes: &[u8]) -> Result<semver::Version, super::SDKVersionError> {
|
fn as_semver(version_as_bytes: &[u8]) -> Result<semver::Version, super::SDKVersionError> {
|
||||||
|
@ -4,7 +4,7 @@ description = "Fluence Marine module interface"
|
|||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "marine_module_interface"
|
name = "marine_module_interface"
|
||||||
@ -15,8 +15,8 @@ marine-it-interfaces = { path = "../it-interfaces", version = "0.7.3" }
|
|||||||
|
|
||||||
anyhow = "1.0.66"
|
anyhow = "1.0.66"
|
||||||
walrus = "0.19.0"
|
walrus = "0.19.0"
|
||||||
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.24.1" }
|
wasmer-it = { workspace = true }
|
||||||
nom = "5.1"
|
nom = "7.1.3"
|
||||||
|
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
semver = "1.0.14"
|
semver = "1.0.14"
|
||||||
|
@ -22,6 +22,6 @@ pub enum InterfaceError {
|
|||||||
#[error("record type with type id {0} not found")]
|
#[error("record type with type id {0} not found")]
|
||||||
NotFoundRecordTypeId(u64),
|
NotFoundRecordTypeId(u64),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
ITInterfaceError(#[from] ITInterfaceError),
|
ITInterfaceError(#[from] ITInterfaceError),
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ use wasmer_it::IRecordType;
|
|||||||
use wasmer_it::IType;
|
use wasmer_it::IType;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
pub(crate) struct RecordsTransformer {
|
pub(crate) struct RecordsTransformer {
|
||||||
@ -60,7 +60,7 @@ impl RecordsTransformer {
|
|||||||
fn dfs(
|
fn dfs(
|
||||||
&mut self,
|
&mut self,
|
||||||
record_id: u64,
|
record_id: u64,
|
||||||
record: &Rc<IRecordType>,
|
record: &Arc<IRecordType>,
|
||||||
exported_records: &IRecordTypes,
|
exported_records: &IRecordTypes,
|
||||||
) -> InterfaceResult<()> {
|
) -> InterfaceResult<()> {
|
||||||
if !self.used.insert(record_id) {
|
if !self.used.insert(record_id) {
|
||||||
@ -107,7 +107,7 @@ impl RecordsTransformer {
|
|||||||
|
|
||||||
fn convert_record(
|
fn convert_record(
|
||||||
id: u64,
|
id: u64,
|
||||||
record: &Rc<IRecordType>,
|
record: &Arc<IRecordType>,
|
||||||
record_types: &IRecordTypes,
|
record_types: &IRecordTypes,
|
||||||
) -> RecordType {
|
) -> RecordType {
|
||||||
use super::itype_text_view;
|
use super::itype_text_view;
|
||||||
|
@ -28,6 +28,6 @@ pub enum ITInterfaceError {
|
|||||||
#[error("mailformed module: a record contains more recursion level then allowed")]
|
#[error("mailformed module: a record contains more recursion level then allowed")]
|
||||||
TooManyRecursionLevels,
|
TooManyRecursionLevels,
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error(transparent)]
|
||||||
MITInterfacesError(#[from] MITInterfacesError),
|
MITInterfacesError(#[from] MITInterfacesError),
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ use super::RIResult;
|
|||||||
|
|
||||||
use marine_it_interfaces::MITInterfaces;
|
use marine_it_interfaces::MITInterfaces;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct ITExportFuncDescriptor<'n> {
|
pub struct ITExportFuncDescriptor<'n> {
|
||||||
pub adapter_function_type: u32,
|
pub adapter_function_type: u32,
|
||||||
@ -69,7 +69,7 @@ pub fn get_export_funcs(mit: &MITInterfaces<'_>) -> RIResult<Vec<IFunctionSignat
|
|||||||
output_types,
|
output_types,
|
||||||
} => {
|
} => {
|
||||||
let signature = IFunctionSignature {
|
let signature = IFunctionSignature {
|
||||||
name: Rc::new(descriptor.name.to_string()),
|
name: Arc::new(descriptor.name.to_string()),
|
||||||
arguments: arguments.clone(),
|
arguments: arguments.clone(),
|
||||||
outputs: output_types.clone(),
|
outputs: output_types.clone(),
|
||||||
adapter_function_type: descriptor.adapter_function_type,
|
adapter_function_type: descriptor.adapter_function_type,
|
||||||
|
@ -22,16 +22,16 @@ use serde::Serialize;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
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.
|
/// Represent a function type inside Marine module.
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
|
||||||
pub struct IFunctionSignature {
|
pub struct IFunctionSignature {
|
||||||
pub name: Rc<String>,
|
pub name: Arc<String>,
|
||||||
pub arguments: Rc<Vec<IFunctionArg>>,
|
pub arguments: Arc<Vec<IFunctionArg>>,
|
||||||
pub outputs: Rc<Vec<IType>>,
|
pub outputs: Arc<Vec<IType>>,
|
||||||
pub adapter_function_type: u32,
|
pub adapter_function_type: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ description = "Fluence Marine utils crate"
|
|||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "marine_utils"
|
name = "marine_utils"
|
||||||
|
@ -29,10 +29,10 @@ mod wasm_mem_pages_conversion;
|
|||||||
|
|
||||||
pub use 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)]
|
#[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 {
|
impl std::borrow::Borrow<str> for SharedString {
|
||||||
fn borrow(&self) -> &str {
|
fn borrow(&self) -> &str {
|
||||||
|
16
crates/wasm-backend-traits/Cargo.toml
Normal file
16
crates/wasm-backend-traits/Cargo.toml
Normal 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"
|
36
crates/wasm-backend-traits/src/caller.rs
Normal file
36
crates/wasm-backend-traits/src/caller.rs
Normal 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>;
|
||||||
|
}
|
145
crates/wasm-backend-traits/src/errors.rs
Normal file
145
crates/wasm-backend-traits/src/errors.rs
Normal 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),
|
||||||
|
}
|
43
crates/wasm-backend-traits/src/exports.rs
Normal file
43
crates/wasm-backend-traits/src/exports.rs
Normal 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;
|
||||||
|
}
|
169
crates/wasm-backend-traits/src/function.rs
Normal file
169
crates/wasm-backend-traits/src/function.rs
Normal 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);
|
||||||
|
}
|
24
crates/wasm-backend-traits/src/impl_utils.rs
Normal file
24
crates/wasm-backend-traits/src/impl_utils.rs
Normal 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()
|
||||||
|
}
|
95
crates/wasm-backend-traits/src/imports.rs
Normal file
95
crates/wasm-backend-traits/src/imports.rs
Normal 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>>;
|
||||||
|
}
|
56
crates/wasm-backend-traits/src/instance.rs
Normal file
56
crates/wasm-backend-traits/src/instance.rs
Normal 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>;
|
||||||
|
}
|
96
crates/wasm-backend-traits/src/lib.rs
Normal file
96
crates/wasm-backend-traits/src/lib.rs
Normal 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>;
|
||||||
|
}
|
47
crates/wasm-backend-traits/src/macros.rs
Normal file
47
crates/wasm-backend-traits/src/macros.rs
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
40
crates/wasm-backend-traits/src/module.rs
Normal file
40
crates/wasm-backend-traits/src/module.rs
Normal 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>;
|
||||||
|
}
|
41
crates/wasm-backend-traits/src/store.rs
Normal file
41
crates/wasm-backend-traits/src/store.rs
Normal 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<'_>;
|
||||||
|
}
|
52
crates/wasm-backend-traits/src/wasi.rs
Normal file
52
crates/wasm-backend-traits/src/wasi.rs
Normal 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>];
|
||||||
|
}
|
92
crates/wasm-backend-traits/src/wtype.rs
Normal file
92
crates/wasm-backend-traits/src/wtype.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
19
crates/wasmtime-backend/Cargo.toml
Normal file
19
crates/wasmtime-backend/Cargo.toml
Normal 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"
|
119
crates/wasmtime-backend/src/caller.rs
Normal file
119
crates/wasmtime-backend/src/caller.rs
Normal 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!((), ());
|
178
crates/wasmtime-backend/src/function.rs
Normal file
178
crates/wasmtime-backend/src/function.rs
Normal 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(())
|
||||||
|
}
|
68
crates/wasmtime-backend/src/imports.rs
Normal file
68
crates/wasmtime-backend/src/imports.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
93
crates/wasmtime-backend/src/instance.rs
Normal file
93
crates/wasmtime-backend/src/instance.rs
Normal 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 })
|
||||||
|
}
|
||||||
|
}
|
74
crates/wasmtime-backend/src/lib.rs
Normal file
74
crates/wasmtime-backend/src/lib.rs
Normal 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
|
||||||
|
}
|
131
crates/wasmtime-backend/src/memory.rs
Normal file
131
crates/wasmtime-backend/src/memory.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
crates/wasmtime-backend/src/module.rs
Normal file
66
crates/wasmtime-backend/src/module.rs
Normal 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 })
|
||||||
|
}
|
||||||
|
}
|
132
crates/wasmtime-backend/src/store.rs
Normal file
132
crates/wasmtime-backend/src/store.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
108
crates/wasmtime-backend/src/utils.rs
Normal file
108
crates/wasmtime-backend/src/utils.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
159
crates/wasmtime-backend/src/wasi.rs
Normal file
159
crates/wasmtime-backend/src/wasi.rs
Normal 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)
|
||||||
|
}
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
description = "The greeting module for the Fluence network"
|
description = "The greeting module for the Fluence network"
|
||||||
repository = "https://github.com/fluencelabs/marine/tree/master/examples/build_rs"
|
repository = "https://github.com/fluencelabs/marine/tree/master/examples/build_rs"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "call_parameters"
|
name = "call_parameters"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@ -43,7 +43,6 @@ pub fn call_parameters() -> String {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use marine_rs_sdk_test::marine_test;
|
use marine_rs_sdk_test::marine_test;
|
||||||
use marine_rs_sdk_test::CallParameters;
|
use marine_rs_sdk_test::CallParameters;
|
||||||
use marine_rs_sdk_test::SecurityTetraplet;
|
|
||||||
|
|
||||||
#[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts")]
|
#[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts")]
|
||||||
fn empty_string(call_parameters: marine_test_env::call_parameters::ModuleInterface) {
|
fn empty_string(call_parameters: marine_test_env::call_parameters::ModuleInterface) {
|
||||||
@ -53,9 +52,12 @@ mod tests {
|
|||||||
let host_id = "host_id";
|
let host_id = "host_id";
|
||||||
let particle_id = "particle_id";
|
let particle_id = "particle_id";
|
||||||
|
|
||||||
let mut tetraplet = SecurityTetraplet::default();
|
let tetraplet = marine_rs_sdk::SecurityTetraplet {
|
||||||
tetraplet.function_name = "some_func_name".to_string();
|
function_name: "some_func_name".to_string(),
|
||||||
tetraplet.json_path = "some_json_path".to_string();
|
json_path: "some_json_path".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
let tetraplets = vec![vec![tetraplet]];
|
let tetraplets = vec![vec![tetraplet]];
|
||||||
|
|
||||||
let cp = CallParameters {
|
let cp = CallParameters {
|
||||||
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
description = "The failing module for the Fluence network"
|
description = "The failing module for the Fluence network"
|
||||||
repository = "https://github.com/fluencelabs/marine/tree/master/examples/failing"
|
repository = "https://github.com/fluencelabs/marine/tree/master/examples/failing"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
description = "The greeting module for the Fluence network"
|
description = "The greeting module for the Fluence network"
|
||||||
repository = "https://github.com/fluencelabs/marine/tree/master/examples/greeting"
|
repository = "https://github.com/fluencelabs/marine/tree/master/examples/greeting"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
Binary file not shown.
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
description = "The greeting module with records for the Fluence network"
|
description = "The greeting module with records for the Fluence network"
|
||||||
repository = "https://github.com/fluencelabs/marine/tree/master/examples/greeting_record"
|
repository = "https://github.com/fluencelabs/marine/tree/master/examples/greeting_record"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user