mirror of
https://github.com/fluencelabs/marine.git
synced 2025-03-15 05:50:49 +00:00
compilable state with Exports and Memory/MemoryView
This commit is contained in:
parent
b96013535b
commit
6c94964f8e
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1883,6 +1883,7 @@ version = "0.4.0"
|
|||||||
name = "marine-wasm-backend-traits"
|
name = "marine-wasm-backend-traits"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"it-memory-traits",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"wasmer-interface-types-fl 0.21.1",
|
"wasmer-interface-types-fl 0.21.1",
|
||||||
"wasmer-runtime-core-fl",
|
"wasmer-runtime-core-fl",
|
||||||
@ -1894,6 +1895,7 @@ dependencies = [
|
|||||||
name = "marine-wasmer-backend"
|
name = "marine-wasmer-backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"it-memory-traits",
|
||||||
"marine-wasm-backend-traits",
|
"marine-wasm-backend-traits",
|
||||||
"wasmer-interface-types-fl 0.21.1",
|
"wasmer-interface-types-fl 0.21.1",
|
||||||
"wasmer-runtime-core-fl",
|
"wasmer-runtime-core-fl",
|
||||||
|
@ -8,6 +8,8 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0.24"
|
thiserror = "1.0.24"
|
||||||
|
|
||||||
|
it-memory-traits = "0.1.0"
|
||||||
|
|
||||||
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.21.1" }
|
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.21.1" }
|
||||||
wasmer-runtime = { package = "wasmer-runtime-fl", version = "=0.17.1" }
|
wasmer-runtime = { package = "wasmer-runtime-fl", version = "=0.17.1" }
|
||||||
# dynamicfunc-fat-closures allows using state inside DynamicFunc
|
# dynamicfunc-fat-closures allows using state inside DynamicFunc
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
//pub mod errors;
|
||||||
|
//pub mod it_memory_traits;
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use it_memory_traits::{SequentialMemoryView, SequentialReader, SequentialWriter};
|
||||||
|
|
||||||
pub struct Value {}
|
pub struct Value {}
|
||||||
|
|
||||||
@ -14,7 +18,13 @@ pub type WasmBackendResult<T> = Result<T, WasmBackendError>;
|
|||||||
|
|
||||||
pub trait WasmBackend: Clone + 'static {
|
pub trait WasmBackend: Clone + 'static {
|
||||||
type IO: ImportObject<Self>;
|
type IO: ImportObject<Self>;
|
||||||
type E: Export;
|
type Exports: Exports<Self>;
|
||||||
|
type MemoryExport: MemoryExport;
|
||||||
|
type WITMemory: Memory<Self> + it_memory_traits::Memory<Self::WITMemoryView> + Clone + 'static;
|
||||||
|
//type SR: SequentialReader;
|
||||||
|
//type SW: SequentialWriter;
|
||||||
|
type WITMemoryView: for<'a> SequentialMemoryView<'a,/* SR = Self::SR, SW = Self::SW*/> + 'static;
|
||||||
|
type FunctionExport: FunctionExport;
|
||||||
type M: Module<Self>;
|
type M: Module<Self>;
|
||||||
type I: Instance<Self>;
|
type I: Instance<Self>;
|
||||||
type Wasi: WasiImplementation<Self>;
|
type Wasi: WasiImplementation<Self>;
|
||||||
@ -31,9 +41,17 @@ pub trait Module<WB: WasmBackend> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance<WB: WasmBackend> {
|
pub trait Instance<WB: WasmBackend> {
|
||||||
fn export_iter<'a>(&'a self)
|
fn export_iter<'a>(
|
||||||
-> Box<dyn Iterator<Item = (String, wasmer_runtime::Export)> + 'a>;
|
&'a self,
|
||||||
fn exports(&self) -> &wasmer_core::instance::Exports;
|
) -> Box<
|
||||||
|
dyn Iterator<
|
||||||
|
Item = (
|
||||||
|
String,
|
||||||
|
Export<<WB as WasmBackend>::MemoryExport, <WB as WasmBackend>::FunctionExport>,
|
||||||
|
),
|
||||||
|
> + 'a,
|
||||||
|
>;
|
||||||
|
fn exports(&self) -> &<WB as WasmBackend>::Exports;
|
||||||
fn import_object(&self) -> &<WB as WasmBackend>::IO;
|
fn import_object(&self) -> &<WB as WasmBackend>::IO;
|
||||||
|
|
||||||
// maybe hide them inside impl
|
// maybe hide them inside impl
|
||||||
@ -41,10 +59,26 @@ pub trait Instance<WB: WasmBackend> {
|
|||||||
fn context_mut(&mut self) -> &mut wasmer_core::vm::Ctx;
|
fn context_mut(&mut self) -> &mut wasmer_core::vm::Ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Export {}
|
pub trait Exports<WB: WasmBackend> {
|
||||||
|
fn get<'a, T: wasmer_core::export::Exportable<'a>>(
|
||||||
|
&'a self,
|
||||||
|
name: &str,
|
||||||
|
) -> wasmer_core::error::ResolveResult<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Export<M: MemoryExport, F: FunctionExport> {
|
||||||
|
Memory(M),
|
||||||
|
Function(F),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ImportObject<WB: WasmBackend>:
|
pub trait ImportObject<WB: WasmBackend>:
|
||||||
Clone + Extend<(String, String, <WB as WasmBackend>::E)>
|
Clone
|
||||||
|
+ Extend<(
|
||||||
|
String,
|
||||||
|
String,
|
||||||
|
Export<<WB as WasmBackend>::MemoryExport, <WB as WasmBackend>::FunctionExport>,
|
||||||
|
)>
|
||||||
{
|
{
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
fn extend_with_self(&mut self, other: Self);
|
fn extend_with_self(&mut self, other: Self);
|
||||||
@ -58,10 +92,13 @@ pub trait ImportObject<WB: WasmBackend>:
|
|||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
N: wasmer_runtime::LikeNamespace + Send + 'static;
|
N: wasmer_runtime::LikeNamespace + Send + 'static;
|
||||||
|
|
||||||
|
|
||||||
|
fn get_memory_env(&self) -> Option<Export<<WB as WasmBackend>::MemoryExport, <WB as WasmBackend>::FunctionExport>>;
|
||||||
|
/*
|
||||||
fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>
|
fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>
|
||||||
where
|
where
|
||||||
Func: FnOnce(&(dyn wasmer_runtime::LikeNamespace + Send)) -> Option<InnerRet>,
|
Func: FnOnce(&(dyn wasmer_runtime::LikeNamespace + Send)) -> Option<InnerRet>,
|
||||||
InnerRet: Sized;
|
InnerRet: Sized;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WasiImplementation<WB: WasmBackend> {
|
pub trait WasiImplementation<WB: WasmBackend> {
|
||||||
@ -73,3 +110,13 @@ pub trait WasiImplementation<WB: WasmBackend> {
|
|||||||
mapped_dirs: Vec<(String, PathBuf)>,
|
mapped_dirs: Vec<(String, PathBuf)>,
|
||||||
) -> Result<<WB as WasmBackend>::IO, String>;
|
) -> Result<<WB as WasmBackend>::IO, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait MemoryExport {
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FunctionExport {}
|
||||||
|
|
||||||
|
pub trait Memory<WB: WasmBackend> {
|
||||||
|
fn new(export: <WB as WasmBackend>::MemoryExport) -> Self;
|
||||||
|
fn view_from_ctx(ctx: &wasmer_runtime::Ctx, memory_index: u32) -> <WB as WasmBackend>::WITMemoryView;
|
||||||
|
}
|
||||||
|
@ -7,6 +7,8 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
marine-wasm-backend-traits = {path = "../wasm-backend-traits", version = "0.1.0"}
|
marine-wasm-backend-traits = {path = "../wasm-backend-traits", version = "0.1.0"}
|
||||||
|
it-memory-traits = "0.1.0"
|
||||||
|
|
||||||
wasmer-runtime = { package = "wasmer-runtime-fl", version = "=0.17.1" }
|
wasmer-runtime = { package = "wasmer-runtime-fl", version = "=0.17.1" }
|
||||||
# dynamicfunc-fat-closures allows using state inside DynamicFunc
|
# dynamicfunc-fat-closures allows using state inside DynamicFunc
|
||||||
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1", features = ["dynamicfunc-fat-closures"] }
|
wasmer-core = { package = "wasmer-runtime-core-fl", version = "=0.17.1", features = ["dynamicfunc-fat-closures"] }
|
||||||
|
@ -1,22 +1,45 @@
|
|||||||
use marine_wasm_backend_traits::WasmBackend;
|
use std::marker::PhantomData;
|
||||||
|
use marine_wasm_backend_traits::{Export, Memory, WasmBackend};
|
||||||
use marine_wasm_backend_traits::WasmBackendResult;
|
use marine_wasm_backend_traits::WasmBackendResult;
|
||||||
use marine_wasm_backend_traits::WasmBackendError;
|
use marine_wasm_backend_traits::WasmBackendError;
|
||||||
use marine_wasm_backend_traits::Module;
|
use marine_wasm_backend_traits::Module;
|
||||||
use marine_wasm_backend_traits::Instance;
|
use marine_wasm_backend_traits::Instance;
|
||||||
use marine_wasm_backend_traits::ImportObject;
|
use marine_wasm_backend_traits::ImportObject;
|
||||||
use marine_wasm_backend_traits::Export;
|
use marine_wasm_backend_traits::FunctionExport;
|
||||||
|
use marine_wasm_backend_traits::MemoryExport;
|
||||||
|
use marine_wasm_backend_traits::Exports;
|
||||||
use marine_wasm_backend_traits::WasiImplementation;
|
use marine_wasm_backend_traits::WasiImplementation;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::slice::Windows;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use wasmer_core::fault::raw::longjmp;
|
||||||
|
use wasmer_core::prelude::vm::Ctx;
|
||||||
|
|
||||||
|
mod memory_access;
|
||||||
|
mod memory;
|
||||||
|
|
||||||
|
//use wasmer_it::interpreter::wasm::structures::{SequentialMemoryView, SequentialReader, SequentialWriter};
|
||||||
|
use crate::memory::WITMemoryView;
|
||||||
|
use crate::memory::WITMemory;
|
||||||
|
use crate::memory_access::{WasmerSequentialReader, WasmerSequentialWriter};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WasmerBackend {}
|
pub struct WasmerBackend/*<'a>*/ {
|
||||||
|
// _data: &'a PhantomData<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
impl WasmBackend for WasmerBackend {
|
impl<'b> WasmBackend for WasmerBackend/*<'b>*/ {
|
||||||
type E = WasmerExport;
|
type Exports = WasmerInstance;
|
||||||
|
type MemoryExport = WasmerMemoryExport;
|
||||||
|
type FunctionExport = WasmerFunctionExport;
|
||||||
type M = WasmerModule;
|
type M = WasmerModule;
|
||||||
type I = WasmerInstance;
|
type I = WasmerInstance;
|
||||||
type IO = WasmerImportObject;
|
type IO = WasmerImportObject;
|
||||||
|
//type SR = WasmerSequentialReader<'b>;
|
||||||
|
//type SW = WasmerSequentialWriter<'b>;
|
||||||
|
type WITMemory = WITMemory;
|
||||||
|
type WITMemoryView = WITMemoryView<'static>;
|
||||||
type Wasi = WasmerWasiImplementation;
|
type Wasi = WasmerWasiImplementation;
|
||||||
|
|
||||||
fn compile(wasm: &[u8]) -> WasmBackendResult<WasmerModule> {
|
fn compile(wasm: &[u8]) -> WasmBackendResult<WasmerModule> {
|
||||||
@ -54,13 +77,14 @@ pub struct WasmerInstance {
|
|||||||
impl Instance<WasmerBackend> for WasmerInstance {
|
impl Instance<WasmerBackend> for WasmerInstance {
|
||||||
fn export_iter<'a>(
|
fn export_iter<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> Box<dyn Iterator<Item = (String, wasmer_runtime::Export)> + 'a> {
|
) -> Box<dyn Iterator<Item = (String, Export<WasmerMemoryExport, WasmerFunctionExport>)> + 'a>
|
||||||
let exports = self.instance.exports();
|
{
|
||||||
Box::new(exports)
|
let export_iter = self.instance.exports();
|
||||||
|
Box::new(export_iter.map(|(name, export)| (name, export_from_wasmer_export(export))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exports(&self) -> &wasmer_core::instance::Exports {
|
fn exports(&self) -> &Self {
|
||||||
&self.instance.exports
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_object(&self) -> &WasmerImportObject {
|
fn import_object(&self) -> &WasmerImportObject {
|
||||||
@ -80,15 +104,29 @@ pub struct WasmerImportObject {
|
|||||||
pub import_object: wasmer_runtime::ImportObject,
|
pub import_object: wasmer_runtime::ImportObject,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extend<(String, String, WasmerExport)> for WasmerImportObject {
|
impl
|
||||||
|
Extend<(
|
||||||
|
String,
|
||||||
|
String,
|
||||||
|
Export<WasmerMemoryExport, WasmerFunctionExport>,
|
||||||
|
)> for WasmerImportObject
|
||||||
|
{
|
||||||
fn extend<T>(&mut self, iter: T)
|
fn extend<T>(&mut self, iter: T)
|
||||||
where
|
where
|
||||||
T: IntoIterator<Item = (String, String, WasmerExport)>,
|
T: IntoIterator<
|
||||||
|
Item = (
|
||||||
|
String,
|
||||||
|
String,
|
||||||
|
Export<WasmerMemoryExport, WasmerFunctionExport>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
{
|
{
|
||||||
self.import_object.extend(
|
self.import_object
|
||||||
iter.into_iter()
|
.extend(iter.into_iter().map(|(s1, s2, export)| match export {
|
||||||
.map(|(s1, s2, export)| (s1, s2, export.export)),
|
Export::Memory(memory) => (s1, s2, memory.into()),
|
||||||
)
|
Export::Function(func) => (s1, s2, func.into()),
|
||||||
|
_ => unreachable!()
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,20 +153,43 @@ impl ImportObject<WasmerBackend> for WasmerImportObject {
|
|||||||
self.import_object.register(name, namespace)
|
self.import_object.register(name, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_memory_env(&self) -> Option<Export<WasmerMemoryExport, WasmerFunctionExport>> {
|
||||||
|
self.import_object
|
||||||
|
.maybe_with_namespace("env", |env| env.get_export("memory"))
|
||||||
|
.map(|export| {
|
||||||
|
match export {
|
||||||
|
wasmer_runtime::Export::Memory(memory) => Export::Memory(WasmerMemoryExport {memory}),
|
||||||
|
_ => Export::Other
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>
|
fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>
|
||||||
where
|
where
|
||||||
Func: FnOnce(&(dyn wasmer_runtime::LikeNamespace + Send)) -> Option<InnerRet>,
|
Func: FnOnce(&(dyn wasmer_runtime::LikeNamespace + Send)) -> Option<InnerRet>,
|
||||||
InnerRet: Sized,
|
InnerRet: Sized,
|
||||||
{
|
{
|
||||||
self.import_object.maybe_with_namespace(namespace, f)
|
self.import_object.maybe_with_namespace(namespace, f)
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WasmerExport {
|
pub struct WasmerFunctionExport {
|
||||||
export: wasmer_runtime::Export,
|
func: wasmer_core::export::FuncPointer,
|
||||||
|
/// A kind of context.
|
||||||
|
ctx: wasmer_core::export::Context,
|
||||||
|
/// The signature of the function.
|
||||||
|
signature: Arc<wasmer_runtime::types::FuncSig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Export for WasmerExport {}
|
impl FunctionExport for WasmerFunctionExport {}
|
||||||
|
|
||||||
|
pub struct WasmerMemoryExport {
|
||||||
|
memory: wasmer_runtime::Memory,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryExport for WasmerMemoryExport {
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WasmerWasiImplementation {}
|
pub struct WasmerWasiImplementation {}
|
||||||
|
|
||||||
@ -151,15 +212,65 @@ impl WasiImplementation<WasmerBackend> for WasmerWasiImplementation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
impl Exports<WasmerBackend> for WasmerInstance {
|
||||||
pub struct WasmerExportIter {
|
fn get<'a, T: wasmer_core::export::Exportable<'a>>(
|
||||||
export_iter: Box<dyn Iterator<Item = (String, wasmer_runtime::Export)> + 'a>
|
&'a self,
|
||||||
|
name: &str,
|
||||||
|
) -> wasmer_core::error::ResolveResult<T> {
|
||||||
|
self.instance.exports.get(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for WasmerExportIter<'a> {
|
fn export_from_wasmer_export(export: wasmer_core::export::Export) -> Export<WasmerMemoryExport, WasmerFunctionExport> {
|
||||||
type Item = (String, wasmer_runtime::Export);
|
match export {
|
||||||
|
wasmer_core::export::Export::Function {
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
func,
|
||||||
self.export_iter.as_mut().next()
|
ctx,
|
||||||
|
signature,
|
||||||
|
} => Export::Function(WasmerFunctionExport {
|
||||||
|
func,
|
||||||
|
ctx,
|
||||||
|
signature,
|
||||||
|
}),
|
||||||
|
wasmer_core::export::Export::Memory(memory) => {
|
||||||
|
Export::Memory(WasmerMemoryExport{memory})
|
||||||
|
}
|
||||||
|
wasmer_core::export::Export::Table(_table) => {
|
||||||
|
Export::Other
|
||||||
|
}
|
||||||
|
wasmer_core::export::Export::Global(_global) => {
|
||||||
|
Export::Other
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
impl Into<wasmer_runtime::Export> for WasmerMemoryExport {
|
||||||
|
fn into(self) -> wasmer_core::export::Export {
|
||||||
|
wasmer_runtime::Export::Memory(self.memory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<wasmer_runtime::Export> for WasmerFunctionExport {
|
||||||
|
fn into(self) -> wasmer_core::export::Export {
|
||||||
|
wasmer_runtime::Export::Function{
|
||||||
|
func: self.func,
|
||||||
|
ctx: self.ctx,
|
||||||
|
signature: self.signature,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Memory<WasmerBackend> for WITMemory {
|
||||||
|
fn new(export: WasmerMemoryExport) -> Self {
|
||||||
|
WITMemory(export.memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_from_ctx(ctx: &Ctx, memory_index: u32) -> WITMemoryView<'static> {
|
||||||
|
let memory = unsafe {
|
||||||
|
std::mem::transmute::<&'_ wasmer_runtime::Memory, &'static wasmer_runtime::Memory>(ctx.memory(memory_index))
|
||||||
|
};
|
||||||
|
|
||||||
|
WITMemoryView(memory.view::<u8>())
|
||||||
|
}
|
||||||
|
}
|
112
crates/wasmer-backend/src/memory.rs
Normal file
112
crates/wasmer-backend/src/memory.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::ops::Deref;
|
||||||
|
//use wasmer_it::interpreter::wasm;
|
||||||
|
use wasmer_core::memory::{Memory, MemoryView};
|
||||||
|
use wasmer_core::vm::LocalMemory;
|
||||||
|
//use wasmer_it::interpreter::wasm::structures::MemoryAccessError;
|
||||||
|
use it_memory_traits::MemoryAccessError;
|
||||||
|
//use marine_wasm_backend_traits::it_memory_traits;
|
||||||
|
use crate::memory_access::WasmerSequentialReader;
|
||||||
|
|
||||||
|
use crate::memory_access::WasmerSequentialWriter;
|
||||||
|
|
||||||
|
pub struct WITMemoryView<'a>(pub(crate) MemoryView<'a, u8>);
|
||||||
|
|
||||||
|
//pub struct WITMemoryView(pub(crate) Vec<Cell<u8>>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WITMemory(pub(super) Memory);
|
||||||
|
impl std::ops::Deref for WITMemory {
|
||||||
|
type Target = Memory;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WITMemoryView<'_> {
|
||||||
|
fn check_bounds(
|
||||||
|
&self,
|
||||||
|
offset: usize,
|
||||||
|
size: usize,
|
||||||
|
memory_size: usize,
|
||||||
|
) -> Result<(), MemoryAccessError> {
|
||||||
|
if offset + size >= memory_size {
|
||||||
|
Err(MemoryAccessError::OutOfBounds {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
memory_size,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s, 'v> it_memory_traits::SequentialMemoryView<'v> for WITMemoryView<'s> {
|
||||||
|
type SR = WasmerSequentialReader<'v>;
|
||||||
|
type SW = WasmerSequentialWriter<'v>;
|
||||||
|
|
||||||
|
fn sequential_writer(
|
||||||
|
&'v self,
|
||||||
|
offset: usize,
|
||||||
|
size: usize,
|
||||||
|
) -> Result<Self::SW, MemoryAccessError> {
|
||||||
|
let view = &self.0;
|
||||||
|
let slice = view.deref();
|
||||||
|
|
||||||
|
self.check_bounds(offset, size, slice.len())?;
|
||||||
|
|
||||||
|
let writer = WasmerSequentialWriter {
|
||||||
|
offset,
|
||||||
|
slice,
|
||||||
|
current_offset: Cell::new(offset),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sequential_reader(
|
||||||
|
&'v self,
|
||||||
|
offset: usize,
|
||||||
|
size: usize,
|
||||||
|
) -> Result<Self::SR, MemoryAccessError> {
|
||||||
|
let view = &self.0;
|
||||||
|
let slice: &[Cell<u8>] = view.deref();
|
||||||
|
|
||||||
|
self.check_bounds(offset, size, slice.len())?;
|
||||||
|
|
||||||
|
let reader = WasmerSequentialReader {
|
||||||
|
memory: slice,
|
||||||
|
offset: Cell::new(offset),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> /*wasm::structures::Memory*/it_memory_traits::Memory<WITMemoryView<'a>> for WITMemory {
|
||||||
|
fn view(&self) -> WITMemoryView<'a> {
|
||||||
|
let LocalMemory { base, .. } = unsafe { *self.0.vm_local_memory() };
|
||||||
|
let length = self.0.size().bytes().0 / std::mem::size_of::<u8>();
|
||||||
|
|
||||||
|
unsafe { WITMemoryView(MemoryView::new(base as _, length as u32)) }
|
||||||
|
//unsafe { WITMemoryView(vec![]) }
|
||||||
|
}
|
||||||
|
}
|
138
crates/wasmer-backend/src/memory_access.rs
Normal file
138
crates/wasmer-backend/src/memory_access.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use std::cell::Cell;
|
||||||
|
//use wasmer_it::interpreter::wasm::structures::{SequentialReader, SequentialWriter};
|
||||||
|
use it_memory_traits::{SequentialReader, SequentialWriter};
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! value_der {
|
||||||
|
($self:expr, $offset:expr, @seq_start $($ids:tt),* @seq_end) => {
|
||||||
|
[$($self.memory[$offset + $ids].get()),+]
|
||||||
|
};
|
||||||
|
|
||||||
|
($self:expr, $offset:expr, 1) => {
|
||||||
|
crate::value_der!($self, $offset, @seq_start 0 @seq_end);
|
||||||
|
};
|
||||||
|
|
||||||
|
($self:expr, $offset:expr, 2) => {
|
||||||
|
crate::value_der!($self, $offset, @seq_start 0, 1 @seq_end);
|
||||||
|
};
|
||||||
|
|
||||||
|
($self:expr, $offset:expr, 4) => {
|
||||||
|
crate::value_der!($self, $offset, @seq_start 0, 1, 2, 3 @seq_end);
|
||||||
|
};
|
||||||
|
|
||||||
|
($self:expr, $offset:expr, 8) => {
|
||||||
|
crate::value_der!($self, $offset, @seq_start 0, 1, 2, 3, 4, 5, 6, 7 @seq_end);
|
||||||
|
};
|
||||||
|
|
||||||
|
($self:expr, $offset:expr, 16) => {
|
||||||
|
crate::value_der!($self, $offset, @seq_start 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 @seq_end);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! read_ty {
|
||||||
|
($func_name:ident, $ty:ty, 1) => {
|
||||||
|
fn $func_name(&self) -> $ty {
|
||||||
|
let offset = self.offset.get();
|
||||||
|
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 1));
|
||||||
|
|
||||||
|
self.offset.set(offset + 1);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
($func_name:ident, $ty:ty, 2) => {
|
||||||
|
fn $func_name(&self) -> $ty {
|
||||||
|
let offset = self.offset.get();
|
||||||
|
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 2));
|
||||||
|
|
||||||
|
self.offset.set(offset + 2);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
($func_name:ident, $ty:ty, 4) => {
|
||||||
|
fn $func_name(&self) -> $ty {
|
||||||
|
let offset = self.offset.get();
|
||||||
|
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 4));
|
||||||
|
|
||||||
|
self.offset.set(offset + 4);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
($func_name:ident, $ty:ty, 8) => {
|
||||||
|
fn $func_name(&self) -> $ty {
|
||||||
|
let offset = self.offset.get();
|
||||||
|
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 8));
|
||||||
|
|
||||||
|
self.offset.set(offset + 8);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
($func_name:ident, $ty:ty, 16) => {
|
||||||
|
fn $func_name(&self) -> $ty {
|
||||||
|
let offset = self.offset.get();
|
||||||
|
let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 16));
|
||||||
|
|
||||||
|
self.offset.set(offset + 16);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WasmerSequentialReader<'s> {
|
||||||
|
pub memory: &'s [Cell<u8>],
|
||||||
|
pub offset: Cell<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WasmerSequentialWriter<'s> {
|
||||||
|
pub offset: usize,
|
||||||
|
pub slice: &'s [Cell<u8>],
|
||||||
|
pub current_offset: Cell<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SequentialReader for WasmerSequentialReader<'_> {
|
||||||
|
fn read_byte(&self) -> u8 {
|
||||||
|
let offset = self.offset.get();
|
||||||
|
let result = self.memory[offset].get();
|
||||||
|
self.offset.set(offset + 1);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// needed because clippy suggests using an iterator which looks worse
|
||||||
|
#[allow(clippy::needless_range_loop)]
|
||||||
|
fn read_bytes<const COUNT: usize>(&self) -> [u8; COUNT] {
|
||||||
|
let offset = self.offset.get();
|
||||||
|
let mut result = [0u8; COUNT];
|
||||||
|
for index in 0..COUNT {
|
||||||
|
result[index] = self.memory[offset + index].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.offset.set(offset + COUNT);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SequentialWriter for WasmerSequentialWriter<'_> {
|
||||||
|
fn start_offset(&self) -> usize {
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_u8(&self, value: u8) {
|
||||||
|
let offset = self.current_offset.get();
|
||||||
|
self.slice[offset].set(value);
|
||||||
|
self.current_offset.set(offset + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_u32(&self, value: u32) {
|
||||||
|
self.write_bytes(&value.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&self, bytes: &[u8]) {
|
||||||
|
for byte in bytes {
|
||||||
|
self.write_u8(*byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ use crate::MRecordTypes;
|
|||||||
use crate::init_wasm_func_once;
|
use crate::init_wasm_func_once;
|
||||||
use crate::call_wasm_func;
|
use crate::call_wasm_func;
|
||||||
use crate::HostImportDescriptor;
|
use crate::HostImportDescriptor;
|
||||||
use crate::module::wit_prelude::WITMemoryView;
|
//use crate::module::wit_prelude::WITMemoryView;
|
||||||
|
|
||||||
use wasmer_core::Func;
|
use wasmer_core::Func;
|
||||||
use wasmer_core::vm::Ctx;
|
use wasmer_core::vm::Ctx;
|
||||||
@ -39,7 +39,10 @@ use it_lilo::lowerer::ILowerer;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub(crate) fn create_host_import_func(
|
use marine_wasm_backend_traits::WasmBackend;
|
||||||
|
use marine_wasm_backend_traits::Memory;
|
||||||
|
|
||||||
|
pub(crate) fn create_host_import_func<WB: WasmBackend>(
|
||||||
descriptor: HostImportDescriptor,
|
descriptor: HostImportDescriptor,
|
||||||
record_types: Rc<MRecordTypes>,
|
record_types: Rc<MRecordTypes>,
|
||||||
) -> DynamicFunc<'static> {
|
) -> DynamicFunc<'static> {
|
||||||
@ -65,11 +68,13 @@ pub(crate) fn create_host_import_func(
|
|||||||
let func = move |ctx: &mut Ctx, inputs: &[WValue]| -> Vec<WValue> {
|
let func = move |ctx: &mut Ctx, inputs: &[WValue]| -> Vec<WValue> {
|
||||||
let result = {
|
let result = {
|
||||||
let memory_index = 0;
|
let memory_index = 0;
|
||||||
let memory = ctx.memory(memory_index);
|
//let memory = ctx.memory(memory_index);
|
||||||
let memory_view = WITMemoryView(memory.view::<u8>());
|
//let memory_view = WITMemoryView(memory.view::<u8>());
|
||||||
|
let memory_view = <WB as WasmBackend>::WITMemory::view_from_ctx(ctx, memory_index);
|
||||||
let li_helper = LiHelper::new(record_types.clone());
|
let li_helper = LiHelper::new(record_types.clone());
|
||||||
let lifter = ILifter::new(memory_view, &li_helper);
|
let lifter = ILifter::new(memory_view, &li_helper);
|
||||||
|
|
||||||
|
|
||||||
match wvalues_to_ivalues(&lifter, inputs, &argument_types) {
|
match wvalues_to_ivalues(&lifter, inputs, &argument_types) {
|
||||||
Ok(ivalues) => host_exported_func(ctx, ivalues),
|
Ok(ivalues) => host_exported_func(ctx, ivalues),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -84,8 +89,9 @@ pub(crate) fn create_host_import_func(
|
|||||||
init_wasm_func_once!(allocate_func, ctx, (i32, i32), i32, ALLOCATE_FUNC_NAME, 2);
|
init_wasm_func_once!(allocate_func, ctx, (i32, i32), i32, ALLOCATE_FUNC_NAME, 2);
|
||||||
|
|
||||||
let memory_index = 0;
|
let memory_index = 0;
|
||||||
let memory = ctx.memory(memory_index);
|
//let memory = ctx.memory(memory_index);
|
||||||
let memory_view = WITMemoryView(memory.view::<u8>());
|
//let memory_view = WITMemoryView(memory.view::<u8>());
|
||||||
|
let memory_view = <WB as WasmBackend>::WITMemory::view_from_ctx(ctx, memory_index);
|
||||||
let lo_helper = LoHelper::new(&allocate_func);
|
let lo_helper = LoHelper::new(&allocate_func);
|
||||||
let t = ILowerer::new(memory_view, &lo_helper)
|
let t = ILowerer::new(memory_view, &lo_helper)
|
||||||
.map_err(HostImportError::LowererError)
|
.map_err(HostImportError::LowererError)
|
||||||
|
@ -26,6 +26,7 @@ use marine_wasm_backend_traits::Module;
|
|||||||
use marine_wasm_backend_traits::Instance;
|
use marine_wasm_backend_traits::Instance;
|
||||||
use marine_wasm_backend_traits::ImportObject;
|
use marine_wasm_backend_traits::ImportObject;
|
||||||
use marine_wasm_backend_traits::WasiImplementation;
|
use marine_wasm_backend_traits::WasiImplementation;
|
||||||
|
use marine_wasm_backend_traits::Exports;
|
||||||
|
|
||||||
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;
|
||||||
@ -43,7 +44,7 @@ use std::sync::Arc;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
type ITInterpreter<WB> =
|
type ITInterpreter<WB> =
|
||||||
Interpreter<ITInstance<WB>, ITExport, WITFunction<WB>, WITMemory, WITMemoryView<'static>>;
|
Interpreter<ITInstance<WB>, ITExport, WITFunction<WB>, <WB as WasmBackend>::WITMemory, <WB as WasmBackend>::WITMemoryView>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct ITModuleFunc<WB: WasmBackend> {
|
pub(super) struct ITModuleFunc<WB: WasmBackend> {
|
||||||
@ -251,7 +252,7 @@ impl<WB: WasmBackend> MModule<WB> {
|
|||||||
let record_types = Rc::new(record_types);
|
let record_types = Rc::new(record_types);
|
||||||
|
|
||||||
for (import_name, descriptor) in config.host_imports {
|
for (import_name, descriptor) in config.host_imports {
|
||||||
let host_import = create_host_import_func(descriptor, record_types.clone());
|
let host_import = create_host_import_func::<WB>(descriptor, record_types.clone());
|
||||||
host_closures_namespace.insert(import_name, host_import);
|
host_closures_namespace.insert(import_name, host_import);
|
||||||
}
|
}
|
||||||
let mut host_closures_import_object = <WB as WasmBackend>::IO::new();
|
let mut host_closures_import_object = <WB as WasmBackend>::IO::new();
|
||||||
@ -323,7 +324,7 @@ impl<WB: WasmBackend> MModule<WB> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// creates a closure that is represent a IT module import
|
// creates a closure that is represent a IT module import
|
||||||
fn create_raw_import<WB: WasmBackend + 'static>(
|
fn create_raw_import<WB: WasmBackend>(
|
||||||
wit_instance: Arc<MaybeUninit<ITInstance<WB>>>,
|
wit_instance: Arc<MaybeUninit<ITInstance<WB>>>,
|
||||||
interpreter: ITInterpreter<WB>,
|
interpreter: ITInterpreter<WB>,
|
||||||
import_namespace: String,
|
import_namespace: String,
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
mod exports;
|
mod exports;
|
||||||
mod marine_module;
|
mod marine_module;
|
||||||
mod memory;
|
//mod memory;
|
||||||
mod memory_access;
|
//mod memory_access;
|
||||||
mod wit_function;
|
mod wit_function;
|
||||||
mod wit_instance;
|
mod wit_instance;
|
||||||
mod type_converters;
|
mod type_converters;
|
||||||
@ -30,8 +30,8 @@ pub use wasmer_it::IValue;
|
|||||||
pub use wasmer_it::from_interface_values;
|
pub use wasmer_it::from_interface_values;
|
||||||
pub use wasmer_it::to_interface_value;
|
pub use wasmer_it::to_interface_value;
|
||||||
|
|
||||||
pub(crate) use memory_access::WasmerSequentialWriter;
|
//pub(crate) use memory_access::WasmerSequentialWriter;
|
||||||
pub(crate) use memory_access::WasmerSequentialReader;
|
//pub(crate) use memory_access::WasmerSequentialReader;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -56,6 +56,6 @@ pub(crate) mod wit_prelude {
|
|||||||
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::WITMemoryView;
|
||||||
pub(crate) use super::memory::WITMemory;
|
//pub(crate) use super::memory::WITMemory;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ use marine_wasm_backend_traits::WasmBackend;
|
|||||||
//use marine_wasm_backend_traits::Module;
|
//use marine_wasm_backend_traits::Module;
|
||||||
use marine_wasm_backend_traits::Instance;
|
use marine_wasm_backend_traits::Instance;
|
||||||
use marine_wasm_backend_traits::ImportObject;
|
use marine_wasm_backend_traits::ImportObject;
|
||||||
|
use marine_wasm_backend_traits::Exports;
|
||||||
|
use marine_wasm_backend_traits::Memory as WBMemory;
|
||||||
|
|
||||||
use marine_it_interfaces::MITInterfaces;
|
use marine_it_interfaces::MITInterfaces;
|
||||||
use marine_it_interfaces::ITAstType;
|
use marine_it_interfaces::ITAstType;
|
||||||
@ -43,7 +45,7 @@ pub(super) struct ITInstance<WB: WasmBackend> {
|
|||||||
funcs: HashMap<usize, WITFunction<WB>>,
|
funcs: HashMap<usize, WITFunction<WB>>,
|
||||||
|
|
||||||
/// IT memories.
|
/// IT memories.
|
||||||
memories: Vec<WITMemory>,
|
memories: Vec<<WB as WasmBackend>::WITMemory>,
|
||||||
|
|
||||||
/// All record types that instance contains.
|
/// All record types that instance contains.
|
||||||
record_types_by_id: MRecordTypes,
|
record_types_by_id: MRecordTypes,
|
||||||
@ -142,22 +144,22 @@ impl<WB: WasmBackend> ITInstance<WB> {
|
|||||||
.collect::<MResult<HashMap<_, _>>>()
|
.collect::<MResult<HashMap<_, _>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_memories(wasmer_instance: &<WB as WasmBackend>::I) -> Vec<WITMemory> {
|
fn extract_memories(wasmer_instance: &<WB as WasmBackend>::I) -> Vec<<WB as WasmBackend>::WITMemory> {
|
||||||
use wasmer_core::export::Export::Memory;
|
use marine_wasm_backend_traits::Export::Memory;
|
||||||
|
|
||||||
let mut memories = wasmer_instance
|
let mut memories = wasmer_instance
|
||||||
.export_iter()
|
.export_iter()
|
||||||
.filter_map(|(_, export)| match export {
|
.filter_map(|(_, export)| match export {
|
||||||
Memory(memory) => Some(WITMemory(memory)),
|
Memory(memory) => Some(<WB as WasmBackend>::WITMemory::new(memory)),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if let Some(Memory(memory)) = wasmer_instance
|
if let Some(Memory(memory)) = wasmer_instance
|
||||||
.import_object()
|
.import_object().get_memory_env()
|
||||||
.maybe_with_namespace("env", |env| env.get_export("memory"))
|
//.maybe_with_namespace("env", |env| env.get_export("memory"))
|
||||||
{
|
{
|
||||||
memories.push(WITMemory(memory));
|
memories.push(<WB as WasmBackend>::WITMemory::new(memory));
|
||||||
}
|
}
|
||||||
|
|
||||||
memories
|
memories
|
||||||
@ -182,7 +184,7 @@ impl<WB: WasmBackend> ITInstance<WB> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'v, WB: WasmBackend>
|
impl<'v, WB: WasmBackend>
|
||||||
wasm::structures::Instance<ITExport, WITFunction<WB>, WITMemory, WITMemoryView<'v>>
|
wasm::structures::Instance<ITExport, WITFunction<WB>, <WB as WasmBackend>::WITMemory, <WB as WasmBackend>::WITMemoryView>
|
||||||
for ITInstance<WB>
|
for ITInstance<WB>
|
||||||
{
|
{
|
||||||
fn export(&self, _export_name: &str) -> Option<&ITExport> {
|
fn export(&self, _export_name: &str) -> Option<&ITExport> {
|
||||||
@ -197,7 +199,7 @@ impl<'v, WB: WasmBackend>
|
|||||||
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>::WITMemory> {
|
||||||
if index >= self.memories.len() {
|
if index >= self.memories.len() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -205,13 +207,13 @@ impl<'v, WB: WasmBackend>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn memory_view(&self, index: usize) -> Option<WITMemoryView<'static>> {
|
fn memory_view(&self, index: usize) -> Option<<WB as WasmBackend>::WITMemoryView> {
|
||||||
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>::WITMemoryView = memory.view();
|
||||||
Some(view)
|
Some(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user