mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-13 00:51:20 +00:00
Merge branch 'master' into docs/better-runtime-docs
# Conflicts: # lib/runtime-core/src/vm.rs
This commit is contained in:
@ -1,9 +1,8 @@
|
||||
use crate::{
|
||||
backing::ImportBacking,
|
||||
error::CompileResult,
|
||||
error::RuntimeResult,
|
||||
module::ModuleInner,
|
||||
types::{FuncIndex, LocalFuncIndex, Value},
|
||||
typed_func::Wasm,
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm,
|
||||
};
|
||||
|
||||
@ -14,6 +13,8 @@ use crate::{
|
||||
};
|
||||
use std::{any::Any, ptr::NonNull};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
pub mod sys {
|
||||
pub use crate::sys::*;
|
||||
}
|
||||
@ -22,6 +23,7 @@ pub use crate::sig_registry::SigRegistry;
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Backend {
|
||||
Cranelift,
|
||||
Singlepass,
|
||||
LLVM,
|
||||
}
|
||||
|
||||
@ -37,64 +39,49 @@ impl Token {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration data for the compiler
|
||||
pub struct CompilerConfig {
|
||||
/// Symbol information generated from emscripten; used for more detailed debug messages
|
||||
pub symbol_map: Option<HashMap<u32, String>>,
|
||||
}
|
||||
|
||||
impl Default for CompilerConfig {
|
||||
fn default() -> CompilerConfig {
|
||||
CompilerConfig { symbol_map: None }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Compiler {
|
||||
/// Compiles a `Module` from WebAssembly binary format.
|
||||
/// The `CompileToken` parameter ensures that this can only
|
||||
/// be called from inside the runtime.
|
||||
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner>;
|
||||
fn compile(
|
||||
&self,
|
||||
wasm: &[u8],
|
||||
comp_conf: CompilerConfig,
|
||||
_: Token,
|
||||
) -> CompileResult<ModuleInner>;
|
||||
|
||||
unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result<ModuleInner, CacheError>;
|
||||
}
|
||||
|
||||
/// The functionality exposed by this trait is expected to be used
|
||||
/// for calling functions exported by a webassembly module from
|
||||
/// host code only.
|
||||
pub trait ProtectedCaller: Send + Sync {
|
||||
/// This calls the exported function designated by `local_func_index`.
|
||||
/// Important to note, this supports calling imported functions that are
|
||||
/// then exported.
|
||||
///
|
||||
/// It's invalid to attempt to call a local function that isn't exported and
|
||||
/// the implementation is expected to check for that. The implementation
|
||||
/// is also expected to check for correct parameter types and correct
|
||||
/// parameter number.
|
||||
///
|
||||
/// The `returns` parameter is filled with dummy values when passed in and upon function
|
||||
/// return, will be filled with the return values of the wasm function, as long as the
|
||||
/// call completed successfully.
|
||||
///
|
||||
/// The existance of the Token parameter ensures that this can only be called from
|
||||
/// within the runtime crate.
|
||||
fn call(
|
||||
pub trait RunnableModule: Send + Sync {
|
||||
/// This returns a pointer to the function designated by the `local_func_index`
|
||||
/// parameter.
|
||||
fn get_func(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
func_index: FuncIndex,
|
||||
params: &[Value],
|
||||
import_backing: &ImportBacking,
|
||||
vmctx: *mut vm::Ctx,
|
||||
_: Token,
|
||||
) -> RuntimeResult<Vec<Value>>;
|
||||
info: &ModuleInfo,
|
||||
local_func_index: LocalFuncIndex,
|
||||
) -> Option<NonNull<vm::Func>>;
|
||||
|
||||
fn get_early_trapper(&self) -> Box<dyn UserTrapper>;
|
||||
}
|
||||
/// A wasm trampoline contains the necesarry data to dynamically call an exported wasm function.
|
||||
/// Given a particular signature index, we are returned a trampoline that is matched with that
|
||||
/// signature and an invoke function that can call the trampoline.
|
||||
fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm>;
|
||||
|
||||
pub trait UserTrapper {
|
||||
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> !;
|
||||
}
|
||||
|
||||
pub trait FuncResolver: Send + Sync {
|
||||
/// This returns a pointer to the function designated by the `local_func_index`
|
||||
/// parameter.
|
||||
fn get(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
local_func_index: LocalFuncIndex,
|
||||
) -> Option<NonNull<vm::Func>>;
|
||||
}
|
||||
|
||||
pub trait CacheGen: Send + Sync {
|
||||
fn generate_cache(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError>;
|
||||
fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError>;
|
||||
}
|
||||
|
@ -73,8 +73,8 @@ impl LocalBacking {
|
||||
(0..module.info.func_assoc.len() - module.info.imported_functions.len())
|
||||
.map(|index| {
|
||||
module
|
||||
.func_resolver
|
||||
.get(module, LocalFuncIndex::new(index))
|
||||
.runnable_module
|
||||
.get_func(&module.info, LocalFuncIndex::new(index))
|
||||
.unwrap()
|
||||
.as_ptr() as *const _
|
||||
})
|
||||
@ -223,8 +223,8 @@ impl LocalBacking {
|
||||
let (func, ctx) = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.func_resolver
|
||||
.get(module, local_func_index)
|
||||
.runnable_module
|
||||
.get_func(&module.info, local_func_index)
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
as *const vm::Func,
|
||||
@ -262,8 +262,8 @@ impl LocalBacking {
|
||||
let (func, ctx) = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.func_resolver
|
||||
.get(module, local_func_index)
|
||||
.runnable_module
|
||||
.get_func(&module.info, local_func_index)
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
as *const vm::Func,
|
||||
|
@ -207,3 +207,7 @@ pub trait Cache {
|
||||
fn load(&self, key: WasmHash) -> Result<Module, Self::LoadError>;
|
||||
fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::StoreError>;
|
||||
}
|
||||
|
||||
/// A unique ID generated from the version of Wasmer for use with cache versioning
|
||||
pub const WASMER_VERSION_HASH: &'static str =
|
||||
include_str!(concat!(env!("OUT_DIR"), "/wasmer_version_hash.txt"));
|
||||
|
264
lib/runtime-core/src/codegen.rs
Normal file
264
lib/runtime-core/src/codegen.rs
Normal file
@ -0,0 +1,264 @@
|
||||
use crate::{
|
||||
backend::RunnableModule,
|
||||
backend::{Backend, CacheGen, Compiler, CompilerConfig, Token},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
error::{CompileError, CompileResult},
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
structures::Map,
|
||||
types::{FuncIndex, FuncSig, SigIndex},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use wasmparser::{Operator, Type as WpType};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event<'a, 'b> {
|
||||
Internal(InternalEvent),
|
||||
Wasm(&'b Operator<'a>),
|
||||
}
|
||||
|
||||
pub enum InternalEvent {
|
||||
FunctionBegin(u32),
|
||||
FunctionEnd,
|
||||
Breakpoint(Box<Fn(BkptInfo) + Send + Sync + 'static>),
|
||||
SetInternal(u32),
|
||||
GetInternal(u32),
|
||||
}
|
||||
|
||||
impl fmt::Debug for InternalEvent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
InternalEvent::FunctionBegin(_) => write!(f, "FunctionBegin"),
|
||||
InternalEvent::FunctionEnd => write!(f, "FunctionEnd"),
|
||||
InternalEvent::Breakpoint(_) => write!(f, "Breakpoint"),
|
||||
InternalEvent::SetInternal(_) => write!(f, "SetInternal"),
|
||||
InternalEvent::GetInternal(_) => write!(f, "GetInternal"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BkptInfo {}
|
||||
|
||||
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> {
|
||||
fn new() -> Self;
|
||||
fn backend_id() -> Backend;
|
||||
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>;
|
||||
|
||||
/// Creates a new function and returns the function-scope code generator for it.
|
||||
fn next_function(&mut self) -> Result<&mut FCG, E>;
|
||||
fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box<dyn CacheGen>), E>;
|
||||
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
|
||||
|
||||
/// Sets function signatures.
|
||||
fn feed_function_signatures(&mut self, assoc: Map<FuncIndex, SigIndex>) -> Result<(), E>;
|
||||
|
||||
/// Adds an import function.
|
||||
fn feed_import_function(&mut self) -> Result<(), E>;
|
||||
|
||||
unsafe fn from_cache(cache: Artifact, _: Token) -> Result<ModuleInner, CacheError>;
|
||||
}
|
||||
|
||||
pub struct StreamingCompiler<
|
||||
MCG: ModuleCodeGenerator<FCG, RM, E>,
|
||||
FCG: FunctionCodeGenerator<E>,
|
||||
RM: RunnableModule + 'static,
|
||||
E: Debug,
|
||||
CGEN: Fn() -> MiddlewareChain,
|
||||
> {
|
||||
middleware_chain_generator: CGEN,
|
||||
_phantom_mcg: PhantomData<MCG>,
|
||||
_phantom_fcg: PhantomData<FCG>,
|
||||
_phantom_rm: PhantomData<RM>,
|
||||
_phantom_e: PhantomData<E>,
|
||||
}
|
||||
|
||||
pub struct SimpleStreamingCompilerGen<
|
||||
MCG: ModuleCodeGenerator<FCG, RM, E>,
|
||||
FCG: FunctionCodeGenerator<E>,
|
||||
RM: RunnableModule + 'static,
|
||||
E: Debug,
|
||||
> {
|
||||
_phantom_mcg: PhantomData<MCG>,
|
||||
_phantom_fcg: PhantomData<FCG>,
|
||||
_phantom_rm: PhantomData<RM>,
|
||||
_phantom_e: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<
|
||||
MCG: ModuleCodeGenerator<FCG, RM, E>,
|
||||
FCG: FunctionCodeGenerator<E>,
|
||||
RM: RunnableModule + 'static,
|
||||
E: Debug,
|
||||
> SimpleStreamingCompilerGen<MCG, FCG, RM, E>
|
||||
{
|
||||
pub fn new() -> StreamingCompiler<MCG, FCG, RM, E, impl Fn() -> MiddlewareChain> {
|
||||
StreamingCompiler::new(|| MiddlewareChain::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
MCG: ModuleCodeGenerator<FCG, RM, E>,
|
||||
FCG: FunctionCodeGenerator<E>,
|
||||
RM: RunnableModule + 'static,
|
||||
E: Debug,
|
||||
CGEN: Fn() -> MiddlewareChain,
|
||||
> StreamingCompiler<MCG, FCG, RM, E, CGEN>
|
||||
{
|
||||
pub fn new(chain_gen: CGEN) -> Self {
|
||||
Self {
|
||||
middleware_chain_generator: chain_gen,
|
||||
_phantom_mcg: PhantomData,
|
||||
_phantom_fcg: PhantomData,
|
||||
_phantom_rm: PhantomData,
|
||||
_phantom_e: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
MCG: ModuleCodeGenerator<FCG, RM, E>,
|
||||
FCG: FunctionCodeGenerator<E>,
|
||||
RM: RunnableModule + 'static,
|
||||
E: Debug,
|
||||
CGEN: Fn() -> MiddlewareChain,
|
||||
> Compiler for StreamingCompiler<MCG, FCG, RM, E, CGEN>
|
||||
{
|
||||
fn compile(
|
||||
&self,
|
||||
wasm: &[u8],
|
||||
compiler_config: CompilerConfig,
|
||||
_: Token,
|
||||
) -> CompileResult<ModuleInner> {
|
||||
let mut mcg = MCG::new();
|
||||
let mut chain = (self.middleware_chain_generator)();
|
||||
let info = crate::parse::read_module(
|
||||
wasm,
|
||||
MCG::backend_id(),
|
||||
&mut mcg,
|
||||
&mut chain,
|
||||
&compiler_config,
|
||||
)?;
|
||||
let (exec_context, cache_gen) =
|
||||
mcg.finalize(&info)
|
||||
.map_err(|x| CompileError::InternalError {
|
||||
msg: format!("{:?}", x),
|
||||
})?;
|
||||
Ok(ModuleInner {
|
||||
cache_gen,
|
||||
runnable_module: Box::new(exec_context),
|
||||
info,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_cache(
|
||||
&self,
|
||||
artifact: Artifact,
|
||||
token: Token,
|
||||
) -> Result<ModuleInner, CacheError> {
|
||||
MCG::from_cache(artifact, token)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventSink<'a, 'b> {
|
||||
buffer: SmallVec<[Event<'a, 'b>; 2]>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> EventSink<'a, 'b> {
|
||||
pub fn push(&mut self, ev: Event<'a, 'b>) {
|
||||
self.buffer.push(ev);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MiddlewareChain {
|
||||
chain: Vec<Box<GenericFunctionMiddleware>>,
|
||||
}
|
||||
|
||||
impl MiddlewareChain {
|
||||
pub fn new() -> MiddlewareChain {
|
||||
MiddlewareChain { chain: vec![] }
|
||||
}
|
||||
|
||||
pub fn push<M: FunctionMiddleware + 'static>(&mut self, m: M) {
|
||||
self.chain.push(Box::new(m));
|
||||
}
|
||||
|
||||
pub(crate) fn run<E: Debug, FCG: FunctionCodeGenerator<E>>(
|
||||
&mut self,
|
||||
fcg: Option<&mut FCG>,
|
||||
ev: Event,
|
||||
module_info: &ModuleInfo,
|
||||
) -> Result<(), String> {
|
||||
let mut sink = EventSink {
|
||||
buffer: SmallVec::new(),
|
||||
};
|
||||
sink.push(ev);
|
||||
for m in &mut self.chain {
|
||||
let prev: SmallVec<[Event; 2]> = sink.buffer.drain().collect();
|
||||
for ev in prev {
|
||||
m.feed_event(ev, module_info, &mut sink)?;
|
||||
}
|
||||
}
|
||||
if let Some(fcg) = fcg {
|
||||
for ev in sink.buffer {
|
||||
fcg.feed_event(ev, module_info)
|
||||
.map_err(|x| format!("{:?}", x))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FunctionMiddleware {
|
||||
type Error: Debug;
|
||||
fn feed_event<'a, 'b: 'a>(
|
||||
&mut self,
|
||||
op: Event<'a, 'b>,
|
||||
module_info: &ModuleInfo,
|
||||
sink: &mut EventSink<'a, 'b>,
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
pub(crate) trait GenericFunctionMiddleware {
|
||||
fn feed_event<'a, 'b: 'a>(
|
||||
&mut self,
|
||||
op: Event<'a, 'b>,
|
||||
module_info: &ModuleInfo,
|
||||
sink: &mut EventSink<'a, 'b>,
|
||||
) -> Result<(), String>;
|
||||
}
|
||||
|
||||
impl<E: Debug, T: FunctionMiddleware<Error = E>> GenericFunctionMiddleware for T {
|
||||
fn feed_event<'a, 'b: 'a>(
|
||||
&mut self,
|
||||
op: Event<'a, 'b>,
|
||||
module_info: &ModuleInfo,
|
||||
sink: &mut EventSink<'a, 'b>,
|
||||
) -> Result<(), String> {
|
||||
<Self as FunctionMiddleware>::feed_event(self, op, module_info, sink)
|
||||
.map_err(|x| format!("{:?}", x))
|
||||
}
|
||||
}
|
||||
|
||||
/// The function-scope code generator trait.
|
||||
pub trait FunctionCodeGenerator<E: Debug> {
|
||||
/// Sets the return type.
|
||||
fn feed_return(&mut self, ty: WpType) -> Result<(), E>;
|
||||
|
||||
/// Adds a parameter to the function.
|
||||
fn feed_param(&mut self, ty: WpType) -> Result<(), E>;
|
||||
|
||||
/// Adds `n` locals to the function.
|
||||
fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), E>;
|
||||
|
||||
/// Called before the first call to `feed_opcode`.
|
||||
fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>;
|
||||
|
||||
/// Called for each operator.
|
||||
fn feed_event(&mut self, op: Event, module_info: &ModuleInfo) -> Result<(), E>;
|
||||
|
||||
/// Finalizes the function.
|
||||
fn finalize(&mut self) -> Result<(), E>;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type, Value};
|
||||
use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type};
|
||||
use core::borrow::Borrow;
|
||||
use std::any::Any;
|
||||
|
||||
@ -8,6 +8,7 @@ pub type LinkResult<T> = std::result::Result<T, Vec<LinkError>>;
|
||||
pub type RuntimeResult<T> = std::result::Result<T, RuntimeError>;
|
||||
pub type CallResult<T> = std::result::Result<T, CallError>;
|
||||
pub type ResolveResult<T> = std::result::Result<T, ResolveError>;
|
||||
pub type ParseResult<T> = std::result::Result<T, ParseError>;
|
||||
|
||||
/// This is returned when the chosen compiler is unable to
|
||||
/// successfully compile the provided webassembly module into
|
||||
@ -118,11 +119,9 @@ impl std::error::Error for LinkError {}
|
||||
/// The main way to do this is `Instance.call`.
|
||||
///
|
||||
/// Comparing two `RuntimeError`s always evaluates to false.
|
||||
#[derive(Debug)]
|
||||
pub enum RuntimeError {
|
||||
Trap { msg: Box<str> },
|
||||
Exception { data: Box<[Value]> },
|
||||
Panic { data: Box<dyn Any> },
|
||||
Error { data: Box<dyn Any> },
|
||||
}
|
||||
|
||||
impl PartialEq for RuntimeError {
|
||||
@ -137,14 +136,25 @@ impl std::fmt::Display for RuntimeError {
|
||||
RuntimeError::Trap { ref msg } => {
|
||||
write!(f, "WebAssembly trap occured during runtime: {}", msg)
|
||||
}
|
||||
RuntimeError::Exception { ref data } => {
|
||||
write!(f, "Uncaught WebAssembly exception: {:?}", data)
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(s) = data.downcast_ref::<String>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(s) = data.downcast_ref::<&str>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else {
|
||||
write!(f, "unknown error")
|
||||
}
|
||||
}
|
||||
RuntimeError::Panic { data: _ } => write!(f, "User-defined \"panic\""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RuntimeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RuntimeError {}
|
||||
|
||||
/// This error type is produced by resolving a wasm function
|
||||
@ -196,7 +206,6 @@ impl std::error::Error for ResolveError {}
|
||||
/// be the `CallError::Runtime(RuntimeError)` variant.
|
||||
///
|
||||
/// Comparing two `CallError`s always evaluates to false.
|
||||
#[derive(Debug)]
|
||||
pub enum CallError {
|
||||
Resolve(ResolveError),
|
||||
Runtime(RuntimeError),
|
||||
@ -217,6 +226,15 @@ impl std::fmt::Display for CallError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for CallError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
CallError::Resolve(resolve_err) => write!(f, "ResolveError: {:?}", resolve_err),
|
||||
CallError::Runtime(runtime_err) => write!(f, "RuntimeError: {:?}", runtime_err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CallError {}
|
||||
|
||||
/// This error type is produced when creating something,
|
||||
@ -445,3 +463,14 @@ impl Into<GrowError> for MemoryProtectionError {
|
||||
GrowError::CouldNotProtectMemory(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParseError {
|
||||
BinaryReadError,
|
||||
}
|
||||
|
||||
impl From<wasmparser::BinaryReaderError> for ParseError {
|
||||
fn from(_: wasmparser::BinaryReaderError) -> Self {
|
||||
ParseError::BinaryReadError
|
||||
}
|
||||
}
|
||||
|
@ -40,13 +40,13 @@ impl FuncPointer {
|
||||
}
|
||||
|
||||
pub struct ExportIter<'a> {
|
||||
inner: &'a mut InstanceInner,
|
||||
inner: &'a InstanceInner,
|
||||
iter: hash_map::Iter<'a, String, ExportIndex>,
|
||||
module: &'a ModuleInner,
|
||||
}
|
||||
|
||||
impl<'a> ExportIter<'a> {
|
||||
pub(crate) fn new(module: &'a ModuleInner, inner: &'a mut InstanceInner) -> Self {
|
||||
pub(crate) fn new(module: &'a ModuleInner, inner: &'a InstanceInner) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
iter: module.info.exports.iter(),
|
||||
|
@ -1,12 +1,16 @@
|
||||
use crate::export::Export;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use std::collections::VecDeque;
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
ffi::c_void,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
pub trait LikeNamespace {
|
||||
fn get_export(&self, name: &str) -> Option<Export>;
|
||||
fn get_exports(&self) -> Vec<(String, Export)>;
|
||||
fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()>;
|
||||
}
|
||||
|
||||
pub trait IsExport {
|
||||
@ -42,6 +46,7 @@ impl IsExport for Export {
|
||||
/// ```
|
||||
pub struct ImportObject {
|
||||
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
|
||||
state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
|
||||
}
|
||||
|
||||
impl ImportObject {
|
||||
@ -49,9 +54,24 @@ impl ImportObject {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: Rc::new(RefCell::new(HashMap::new())),
|
||||
state_creator: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_data<F>(state_creator: F) -> Self
|
||||
where
|
||||
F: Fn() -> (*mut c_void, fn(*mut c_void)) + 'static,
|
||||
{
|
||||
Self {
|
||||
map: Rc::new(RefCell::new(HashMap::new())),
|
||||
state_creator: Some(Rc::new(state_creator)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn call_state_creator(&self) -> Option<(*mut c_void, fn(*mut c_void))> {
|
||||
self.state_creator.as_ref().map(|state_gen| state_gen())
|
||||
}
|
||||
|
||||
/// Register anything that implements `LikeNamespace` as a namespace.
|
||||
///
|
||||
/// # Usage:
|
||||
@ -95,6 +115,54 @@ impl ImportObject {
|
||||
pub fn clone_ref(&self) -> Self {
|
||||
Self {
|
||||
map: Rc::clone(&self.map),
|
||||
state_creator: self.state_creator.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_objects(&self) -> VecDeque<(String, String, Export)> {
|
||||
let mut out = VecDeque::new();
|
||||
for (name, ns) in self.map.borrow().iter() {
|
||||
for (id, exp) in ns.get_exports() {
|
||||
out.push_back((name.clone(), id, exp));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImportObjectIterator {
|
||||
elements: VecDeque<(String, String, Export)>,
|
||||
}
|
||||
|
||||
impl Iterator for ImportObjectIterator {
|
||||
type Item = (String, String, Export);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.elements.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for ImportObject {
|
||||
type IntoIter = ImportObjectIterator;
|
||||
type Item = (String, String, Export);
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
ImportObjectIterator {
|
||||
elements: self.get_objects(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<(String, String, Export)> for ImportObject {
|
||||
fn extend<T: IntoIterator<Item = (String, String, Export)>>(&mut self, iter: T) {
|
||||
let mut map = self.map.borrow_mut();
|
||||
for (ns, id, exp) in iter.into_iter() {
|
||||
if let Some(like_ns) = map.get_mut(&ns) {
|
||||
like_ns.maybe_insert(&id, exp);
|
||||
} else {
|
||||
let mut new_ns = Namespace::new();
|
||||
new_ns.insert(id, exp);
|
||||
map.insert(ns, Box::new(new_ns));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,4 +191,76 @@ impl LikeNamespace for Namespace {
|
||||
fn get_export(&self, name: &str) -> Option<Export> {
|
||||
self.map.get(name).map(|is_export| is_export.to_export())
|
||||
}
|
||||
|
||||
fn get_exports(&self) -> Vec<(String, Export)> {
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.to_export()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()> {
|
||||
self.map.insert(name.to_owned(), Box::new(export));
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::export::Export;
|
||||
use crate::global::Global;
|
||||
use crate::types::Value;
|
||||
|
||||
#[test]
|
||||
fn extending_works() {
|
||||
let mut imports1 = imports! {
|
||||
"dog" => {
|
||||
"happy" => Global::new(Value::I32(0)),
|
||||
},
|
||||
};
|
||||
|
||||
let imports2 = imports! {
|
||||
"dog" => {
|
||||
"small" => Global::new(Value::I32(2)),
|
||||
},
|
||||
"cat" => {
|
||||
"small" => Global::new(Value::I32(3)),
|
||||
},
|
||||
};
|
||||
|
||||
imports1.extend(imports2);
|
||||
|
||||
let cat_ns = imports1.get_namespace("cat").unwrap();
|
||||
assert!(cat_ns.get_export("small").is_some());
|
||||
|
||||
let dog_ns = imports1.get_namespace("dog").unwrap();
|
||||
assert!(dog_ns.get_export("happy").is_some());
|
||||
assert!(dog_ns.get_export("small").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extending_conflict_overwrites() {
|
||||
let mut imports1 = imports! {
|
||||
"dog" => {
|
||||
"happy" => Global::new(Value::I32(0)),
|
||||
},
|
||||
};
|
||||
|
||||
let imports2 = imports! {
|
||||
"dog" => {
|
||||
"happy" => Global::new(Value::I32(4)),
|
||||
},
|
||||
};
|
||||
|
||||
imports1.extend(imports2);
|
||||
let dog_ns = imports1.get_namespace("dog").unwrap();
|
||||
|
||||
assert!(
|
||||
if let Export::Global(happy_dog_global) = dog_ns.get_export("happy").unwrap() {
|
||||
happy_dog_global.get() == Value::I32(4)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,20 @@
|
||||
use crate::{
|
||||
backend::Token,
|
||||
backend::RunnableModule,
|
||||
backing::{ImportBacking, LocalBacking},
|
||||
error::{CallError, CallResult, ResolveError, ResolveResult, Result},
|
||||
error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError},
|
||||
export::{Context, Export, ExportIter, FuncPointer},
|
||||
global::Global,
|
||||
import::{ImportObject, LikeNamespace},
|
||||
memory::Memory,
|
||||
module::{ExportIndex, Module, ModuleInner},
|
||||
module::{ExportIndex, Module, ModuleInfo, ModuleInner},
|
||||
sig_registry::SigRegistry,
|
||||
table::Table,
|
||||
typed_func::{Func, Safe, WasmTypeList},
|
||||
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value},
|
||||
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
|
||||
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
|
||||
vm,
|
||||
};
|
||||
use std::{mem, sync::Arc};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::{mem, ptr::NonNull, sync::Arc};
|
||||
|
||||
pub(crate) struct InstanceInner {
|
||||
#[allow(dead_code)]
|
||||
@ -63,7 +64,16 @@ impl Instance {
|
||||
// Initialize the vm::Ctx in-place after the backing
|
||||
// has been boxed.
|
||||
unsafe {
|
||||
*inner.vmctx = vm::Ctx::new(&mut inner.backing, &mut inner.import_backing, &module)
|
||||
*inner.vmctx = match imports.call_state_creator() {
|
||||
Some((data, dtor)) => vm::Ctx::new_with_data(
|
||||
&mut inner.backing,
|
||||
&mut inner.import_backing,
|
||||
&module,
|
||||
data,
|
||||
dtor,
|
||||
),
|
||||
None => vm::Ctx::new(&mut inner.backing, &mut inner.import_backing, &module),
|
||||
};
|
||||
};
|
||||
|
||||
let instance = Instance {
|
||||
@ -73,7 +83,45 @@ impl Instance {
|
||||
};
|
||||
|
||||
if let Some(start_index) = instance.module.info.start_func {
|
||||
instance.call_with_index(start_index, &[])?;
|
||||
// We know that the start function takes no arguments and returns no values.
|
||||
// Therefore, we can call it without doing any signature checking, etc.
|
||||
|
||||
let func_ptr = match start_index.local_or_import(&instance.module.info) {
|
||||
LocalOrImport::Local(local_func_index) => instance
|
||||
.module
|
||||
.runnable_module
|
||||
.get_func(&instance.module.info, local_func_index)
|
||||
.unwrap(),
|
||||
LocalOrImport::Import(import_func_index) => NonNull::new(
|
||||
instance.inner.import_backing.vm_functions[import_func_index].func as *mut _,
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ctx_ptr = match start_index.local_or_import(&instance.module.info) {
|
||||
LocalOrImport::Local(_) => instance.inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
instance.inner.import_backing.vm_functions[imported_func_index].vmctx
|
||||
}
|
||||
};
|
||||
|
||||
let sig_index = *instance
|
||||
.module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(start_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
|
||||
let wasm_trampoline = instance
|
||||
.module
|
||||
.runnable_module
|
||||
.get_trampoline(&instance.module.info, sig_index)
|
||||
.expect("wasm trampoline");
|
||||
|
||||
let start_func: Func<(), (), Wasm> =
|
||||
unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, ctx_ptr) };
|
||||
|
||||
start_func.call()?;
|
||||
}
|
||||
|
||||
Ok(instance)
|
||||
@ -98,7 +146,7 @@ impl Instance {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn func<Args, Rets>(&self, name: &str) -> ResolveResult<Func<Args, Rets, Safe>>
|
||||
pub fn func<Args, Rets>(&self, name: &str) -> ResolveResult<Func<Args, Rets, Wasm>>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
@ -136,20 +184,26 @@ impl Instance {
|
||||
}
|
||||
};
|
||||
|
||||
let func_wasm_inner = self
|
||||
.module
|
||||
.runnable_module
|
||||
.get_trampoline(&self.module.info, sig_index)
|
||||
.unwrap();
|
||||
|
||||
let func_ptr = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(local_func_index) => self
|
||||
.module
|
||||
.func_resolver
|
||||
.get(&self.module, local_func_index)
|
||||
.unwrap()
|
||||
.as_ptr(),
|
||||
LocalOrImport::Import(import_func_index) => {
|
||||
self.inner.import_backing.vm_functions[import_func_index].func
|
||||
}
|
||||
.runnable_module
|
||||
.get_func(&self.module.info, local_func_index)
|
||||
.unwrap(),
|
||||
LocalOrImport::Import(import_func_index) => NonNull::new(
|
||||
self.inner.import_backing.vm_functions[import_func_index].func as *mut _,
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let typed_func: Func<Args, Rets, Safe> =
|
||||
unsafe { Func::new_from_ptr(func_ptr as _, ctx) };
|
||||
let typed_func: Func<Args, Rets, Wasm> =
|
||||
unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, ctx) };
|
||||
|
||||
Ok(typed_func)
|
||||
} else {
|
||||
@ -230,7 +284,7 @@ impl Instance {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call(&self, name: &str, args: &[Value]) -> CallResult<Vec<Value>> {
|
||||
pub fn call(&self, name: &str, params: &[Value]) -> CallResult<Vec<Value>> {
|
||||
let export_index =
|
||||
self.module
|
||||
.info
|
||||
@ -249,7 +303,19 @@ impl Instance {
|
||||
.into());
|
||||
};
|
||||
|
||||
self.call_with_index(func_index, args)
|
||||
let mut results = Vec::new();
|
||||
|
||||
call_func_with_index(
|
||||
&self.module.info,
|
||||
&*self.module.runnable_module,
|
||||
&self.inner.import_backing,
|
||||
self.inner.vmctx,
|
||||
func_index,
|
||||
params,
|
||||
&mut results,
|
||||
)?;
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the
|
||||
@ -270,8 +336,8 @@ impl Instance {
|
||||
|
||||
/// Returns an iterator over all of the items
|
||||
/// exported from this instance.
|
||||
pub fn exports(&mut self) -> ExportIter {
|
||||
ExportIter::new(&self.module, &mut self.inner)
|
||||
pub fn exports(&self) -> ExportIter {
|
||||
ExportIter::new(&self.module, &self.inner)
|
||||
}
|
||||
|
||||
/// The module used to instantiate this Instance.
|
||||
@ -280,45 +346,6 @@ impl Instance {
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
fn call_with_index(&self, func_index: FuncIndex, args: &[Value]) -> CallResult<Vec<Value>> {
|
||||
let sig_index = *self
|
||||
.module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature = &self.module.info.signatures[sig_index];
|
||||
|
||||
if !signature.check_param_value_types(args) {
|
||||
Err(ResolveError::Signature {
|
||||
expected: signature.clone(),
|
||||
found: args.iter().map(|val| val.ty()).collect(),
|
||||
})?
|
||||
}
|
||||
|
||||
let vmctx = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(_) => self.inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
self.inner.import_backing.vm_functions[imported_func_index].vmctx
|
||||
}
|
||||
};
|
||||
|
||||
let token = Token::generate();
|
||||
|
||||
let returns = self.module.protected_caller.call(
|
||||
&self.module,
|
||||
func_index,
|
||||
args,
|
||||
&self.inner.import_backing,
|
||||
vmctx,
|
||||
token,
|
||||
)?;
|
||||
|
||||
Ok(returns)
|
||||
}
|
||||
}
|
||||
|
||||
impl InstanceInner {
|
||||
pub(crate) fn get_export_from_index(
|
||||
&self,
|
||||
@ -367,8 +394,8 @@ impl InstanceInner {
|
||||
let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.func_resolver
|
||||
.get(&module, local_func_index)
|
||||
.runnable_module
|
||||
.get_func(&module.info, local_func_index)
|
||||
.expect("broken invariant, func resolver not synced with module.exports")
|
||||
.cast()
|
||||
.as_ptr() as *const _,
|
||||
@ -427,6 +454,142 @@ impl LikeNamespace for Instance {
|
||||
|
||||
Some(self.inner.get_export_from_index(&self.module, export_index))
|
||||
}
|
||||
|
||||
fn get_exports(&self) -> Vec<(String, Export)> {
|
||||
unimplemented!("Use the exports method instead");
|
||||
}
|
||||
|
||||
fn maybe_insert(&mut self, _name: &str, _export: Export) -> Option<()> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn call_func_with_index(
|
||||
info: &ModuleInfo,
|
||||
runnable: &dyn RunnableModule,
|
||||
import_backing: &ImportBacking,
|
||||
local_ctx: *mut vm::Ctx,
|
||||
func_index: FuncIndex,
|
||||
args: &[Value],
|
||||
rets: &mut Vec<Value>,
|
||||
) -> CallResult<()> {
|
||||
rets.clear();
|
||||
|
||||
let sig_index = *info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
|
||||
let signature = &info.signatures[sig_index];
|
||||
let num_results = signature.returns().len();
|
||||
rets.reserve(num_results);
|
||||
|
||||
if !signature.check_param_value_types(args) {
|
||||
Err(ResolveError::Signature {
|
||||
expected: signature.clone(),
|
||||
found: args.iter().map(|val| val.ty()).collect(),
|
||||
})?
|
||||
}
|
||||
|
||||
let func_ptr = match func_index.local_or_import(info) {
|
||||
LocalOrImport::Local(local_func_index) => {
|
||||
runnable.get_func(info, local_func_index).unwrap()
|
||||
}
|
||||
LocalOrImport::Import(import_func_index) => {
|
||||
NonNull::new(import_backing.vm_functions[import_func_index].func as *mut _).unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
let ctx_ptr = match func_index.local_or_import(info) {
|
||||
LocalOrImport::Local(_) => local_ctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
import_backing.vm_functions[imported_func_index].vmctx
|
||||
}
|
||||
};
|
||||
|
||||
let raw_args: SmallVec<[u64; 8]> = args
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
Value::I32(i) => *i as u64,
|
||||
Value::I64(i) => *i as u64,
|
||||
Value::F32(f) => f.to_bits() as u64,
|
||||
Value::F64(f) => f.to_bits(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let Wasm {
|
||||
trampoline,
|
||||
invoke,
|
||||
invoke_env,
|
||||
} = runnable
|
||||
.get_trampoline(info, sig_index)
|
||||
.expect("wasm trampoline");
|
||||
|
||||
let run_wasm = |result_space: *mut u64| unsafe {
|
||||
let mut trap_info = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
|
||||
let success = invoke(
|
||||
trampoline,
|
||||
ctx_ptr,
|
||||
func_ptr,
|
||||
raw_args.as_ptr(),
|
||||
result_space,
|
||||
&mut trap_info,
|
||||
&mut user_error,
|
||||
invoke_env,
|
||||
);
|
||||
|
||||
if success {
|
||||
Ok(())
|
||||
} else {
|
||||
if let Some(data) = user_error {
|
||||
Err(RuntimeError::Error { data })
|
||||
} else {
|
||||
Err(RuntimeError::Trap {
|
||||
msg: trap_info.to_string().into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let raw_to_value = |raw, ty| match ty {
|
||||
Type::I32 => Value::I32(raw as i32),
|
||||
Type::I64 => Value::I64(raw as i64),
|
||||
Type::F32 => Value::F32(f32::from_bits(raw as u32)),
|
||||
Type::F64 => Value::F64(f64::from_bits(raw)),
|
||||
};
|
||||
|
||||
match signature.returns() {
|
||||
&[] => {
|
||||
run_wasm(0 as *mut u64)?;
|
||||
Ok(())
|
||||
}
|
||||
&[ty] => {
|
||||
let mut result = 0u64;
|
||||
|
||||
run_wasm(&mut result)?;
|
||||
|
||||
rets.push(raw_to_value(result, ty));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
result_tys @ _ => {
|
||||
let mut results: SmallVec<[u64; 8]> = smallvec![0; num_results];
|
||||
|
||||
run_wasm(results.as_mut_ptr())?;
|
||||
|
||||
rets.extend(
|
||||
results
|
||||
.iter()
|
||||
.zip(result_tys.iter())
|
||||
.map(|(&raw, &ty)| raw_to_value(raw, ty)),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A representation of an exported WebAssembly function.
|
||||
@ -461,32 +624,19 @@ impl<'a> DynFunc<'a> {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call(&self, params: &[Value]) -> CallResult<Vec<Value>> {
|
||||
if !self.signature.check_param_value_types(params) {
|
||||
Err(ResolveError::Signature {
|
||||
expected: (*self.signature).clone(),
|
||||
found: params.iter().map(|val| val.ty()).collect(),
|
||||
})?
|
||||
}
|
||||
let mut results = Vec::new();
|
||||
|
||||
let vmctx = match self.func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(_) => self.instance_inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
self.instance_inner.import_backing.vm_functions[imported_func_index].vmctx
|
||||
}
|
||||
};
|
||||
|
||||
let token = Token::generate();
|
||||
|
||||
let returns = self.module.protected_caller.call(
|
||||
&self.module,
|
||||
call_func_with_index(
|
||||
&self.module.info,
|
||||
&*self.module.runnable_module,
|
||||
&self.instance_inner.import_backing,
|
||||
self.instance_inner.vmctx,
|
||||
self.func_index,
|
||||
params,
|
||||
&self.instance_inner.import_backing,
|
||||
vmctx,
|
||||
token,
|
||||
&mut results,
|
||||
)?;
|
||||
|
||||
Ok(returns)
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn signature(&self) -> &FuncSig {
|
||||
@ -497,8 +647,8 @@ impl<'a> DynFunc<'a> {
|
||||
match self.func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(local_func_index) => self
|
||||
.module
|
||||
.func_resolver
|
||||
.get(self.module, local_func_index)
|
||||
.runnable_module
|
||||
.get_func(&self.module.info, local_func_index)
|
||||
.unwrap()
|
||||
.as_ptr(),
|
||||
LocalOrImport::Import(import_func_index) => {
|
||||
@ -507,10 +657,3 @@ impl<'a> DynFunc<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl Instance {
|
||||
pub fn memory_offset_addr(&self, _: u32, _: usize) -> *const u8 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![cfg_attr(nightly, feature(unwind_attributes))]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate field_offset;
|
||||
@ -12,6 +15,7 @@ pub mod backend;
|
||||
mod backing;
|
||||
|
||||
pub mod cache;
|
||||
pub mod codegen;
|
||||
pub mod error;
|
||||
pub mod export;
|
||||
pub mod global;
|
||||
@ -19,11 +23,12 @@ pub mod import;
|
||||
pub mod instance;
|
||||
pub mod memory;
|
||||
pub mod module;
|
||||
pub mod parse;
|
||||
mod sig_registry;
|
||||
pub mod structures;
|
||||
mod sys;
|
||||
pub mod table;
|
||||
mod typed_func;
|
||||
pub mod typed_func;
|
||||
pub mod types;
|
||||
pub mod units;
|
||||
pub mod vm;
|
||||
@ -36,7 +41,7 @@ pub use self::error::Result;
|
||||
#[doc(inline)]
|
||||
pub use self::import::IsExport;
|
||||
#[doc(inline)]
|
||||
pub use self::instance::Instance;
|
||||
pub use self::instance::{DynFunc, Instance};
|
||||
#[doc(inline)]
|
||||
pub use self::module::Module;
|
||||
#[doc(inline)]
|
||||
@ -68,7 +73,24 @@ pub fn compile_with(
|
||||
) -> CompileResult<module::Module> {
|
||||
let token = backend::Token::generate();
|
||||
compiler
|
||||
.compile(wasm, token)
|
||||
.compile(wasm, Default::default(), token)
|
||||
.map(|mut inner| {
|
||||
let inner_info: &mut crate::module::ModuleInfo = &mut inner.info;
|
||||
inner_info.import_custom_sections(wasm).unwrap();
|
||||
module::Module::new(Arc::new(inner))
|
||||
})
|
||||
}
|
||||
|
||||
/// The same as `compile_with` but changes the compiler behavior
|
||||
/// with the values in the `CompilerConfig`
|
||||
pub fn compile_with_config(
|
||||
wasm: &[u8],
|
||||
compiler: &dyn backend::Compiler,
|
||||
compiler_config: backend::CompilerConfig,
|
||||
) -> CompileResult<module::Module> {
|
||||
let token = backend::Token::generate();
|
||||
compiler
|
||||
.compile(wasm, compiler_config, token)
|
||||
.map(|inner| module::Module::new(Arc::new(inner)))
|
||||
}
|
||||
|
||||
@ -76,13 +98,18 @@ pub fn compile_with(
|
||||
/// WebAssembly specification. Returns `true` if validation
|
||||
/// succeeded, `false` if validation failed.
|
||||
pub fn validate(wasm: &[u8]) -> bool {
|
||||
validate_and_report_errors(wasm).is_ok()
|
||||
}
|
||||
|
||||
/// The same as `validate` but with an Error message on failure
|
||||
pub fn validate_and_report_errors(wasm: &[u8]) -> ::std::result::Result<(), String> {
|
||||
use wasmparser::WasmDecoder;
|
||||
let mut parser = wasmparser::ValidatingParser::new(wasm, None);
|
||||
loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
wasmparser::ParserState::EndWasm => break true,
|
||||
wasmparser::ParserState::Error(_) => break false,
|
||||
wasmparser::ParserState::EndWasm => break Ok(()),
|
||||
wasmparser::ParserState::Error(e) => break Err(format!("{}", e)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
#[macro_export]
|
||||
#[cfg(feature = "debug")]
|
||||
macro_rules! debug {
|
||||
($fmt:expr) => (println!(concat!("wasmer-runtime(:{})::", $fmt), line!()));
|
||||
($fmt:expr, $($arg:tt)*) => (println!(concat!("wasmer-runtime(:{})::", $fmt, "\n"), line!(), $($arg)*));
|
||||
($fmt:expr) => (println!(concat!("[{}] wasmer-runtime(:{}) ", $fmt), {
|
||||
let time = ::std::time::SystemTime::now().duration_since(::std::time::UNIX_EPOCH).expect("Can't get time");
|
||||
format!("{}.{:03}", time.as_secs(), time.subsec_millis())
|
||||
}, line!()));
|
||||
($fmt:expr, $($arg:tt)*) => (println!(concat!("[{}] wasmer-runtime(:{}) ", $fmt, "\n"), {
|
||||
let time = ::std::time::SystemTime::now().duration_since(::std::time::UNIX_EPOCH).expect("Can't get time");
|
||||
format!("{}.{:03}", time.as_secs(), time.subsec_millis())
|
||||
}, line!(), $($arg)*));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
@ -38,6 +44,13 @@ macro_rules! func {
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// let imports_with_state = imports! {
|
||||
/// || (0 as _, |_a| {}),
|
||||
/// "env" => {
|
||||
/// "foo" => func!(foo),
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// fn foo(_: &mut Ctx, n: i32) -> i32 {
|
||||
/// n
|
||||
/// }
|
||||
@ -57,6 +70,21 @@ macro_rules! imports {
|
||||
import_object.register($ns_name, ns);
|
||||
})*
|
||||
|
||||
import_object
|
||||
}};
|
||||
($state_gen:expr, $( $ns_name:expr => $ns:tt, )* ) => {{
|
||||
use $crate::{
|
||||
import::{ImportObject, Namespace},
|
||||
};
|
||||
|
||||
let mut import_object = ImportObject::new_with_data($state_gen);
|
||||
|
||||
$({
|
||||
let ns = $crate::__imports_internal!($ns);
|
||||
|
||||
import_object.register($ns_name, ns);
|
||||
})*
|
||||
|
||||
import_object
|
||||
}};
|
||||
}
|
||||
@ -75,3 +103,15 @@ macro_rules! __imports_internal {
|
||||
$ns
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! namespace {
|
||||
( $( $imp_name:expr => $import_item:expr, )* ) => {{
|
||||
let mut ns = $crate::import::Namespace::new();
|
||||
$(
|
||||
ns.insert($imp_name, $import_item);
|
||||
)*
|
||||
ns
|
||||
}};
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ macro_rules! intcast {
|
||||
}
|
||||
intcast! { u8 i8 u16 i16 u32 i32 u64 i64 }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Atomic<T> {
|
||||
v: UnsafeCell<Wrapping<T>>,
|
||||
}
|
||||
|
@ -284,6 +284,7 @@ impl Clone for UnsharedMemory {
|
||||
}
|
||||
|
||||
pub struct SharedMemory {
|
||||
#[allow(dead_code)]
|
||||
desc: MemoryDescriptor,
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
use crate::{
|
||||
backend::{Backend, FuncResolver, ProtectedCaller},
|
||||
backend::{Backend, RunnableModule},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
error,
|
||||
import::ImportObject,
|
||||
structures::{Map, TypedIndex},
|
||||
typed_func::EARLY_TRAPPER,
|
||||
types::{
|
||||
FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
|
||||
ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer,
|
||||
@ -22,9 +21,7 @@ use std::sync::Arc;
|
||||
/// This is used to instantiate a new WebAssembly module.
|
||||
#[doc(hidden)]
|
||||
pub struct ModuleInner {
|
||||
pub func_resolver: Box<dyn FuncResolver>,
|
||||
pub protected_caller: Box<dyn ProtectedCaller>,
|
||||
|
||||
pub runnable_module: Box<dyn RunnableModule>,
|
||||
pub cache_gen: Box<dyn CacheGen>,
|
||||
|
||||
pub info: ModuleInfo,
|
||||
@ -56,6 +53,29 @@ pub struct ModuleInfo {
|
||||
|
||||
pub namespace_table: StringTable<NamespaceIndex>,
|
||||
pub name_table: StringTable<NameIndex>,
|
||||
|
||||
/// Symbol information from emscripten
|
||||
pub em_symbol_map: Option<HashMap<u32, String>>,
|
||||
|
||||
pub custom_sections: HashMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl ModuleInfo {
|
||||
pub fn import_custom_sections(&mut self, wasm: &[u8]) -> crate::error::ParseResult<()> {
|
||||
let mut parser = wasmparser::ModuleReader::new(wasm)?;
|
||||
while !parser.eof() {
|
||||
let section = parser.read()?;
|
||||
if let wasmparser::SectionCode::Custom { name, kind: _ } = section.code {
|
||||
let mut reader = section.get_binary_reader();
|
||||
let len = reader.bytes_remaining();
|
||||
let bytes = reader.read_bytes(len)?;
|
||||
let data = bytes.to_vec();
|
||||
let name = name.to_string();
|
||||
self.custom_sections.insert(name, data);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A compiled WebAssembly module.
|
||||
@ -71,10 +91,6 @@ pub struct Module {
|
||||
|
||||
impl Module {
|
||||
pub(crate) fn new(inner: Arc<ModuleInner>) -> Self {
|
||||
unsafe {
|
||||
EARLY_TRAPPER
|
||||
.with(|ucell| *ucell.get() = Some(inner.protected_caller.get_early_trapper()));
|
||||
}
|
||||
Module { inner }
|
||||
}
|
||||
|
||||
@ -105,8 +121,12 @@ impl Module {
|
||||
}
|
||||
|
||||
pub fn cache(&self) -> Result<Artifact, CacheError> {
|
||||
let (info, backend_metadata, code) = self.inner.cache_gen.generate_cache(&self.inner)?;
|
||||
Ok(Artifact::from_parts(info, backend_metadata, code))
|
||||
let (backend_metadata, code) = self.inner.cache_gen.generate_cache()?;
|
||||
Ok(Artifact::from_parts(
|
||||
Box::new(self.inner.info.clone()),
|
||||
backend_metadata,
|
||||
code,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn info(&self) -> &ModuleInfo {
|
||||
|
426
lib/runtime-core/src/parse.rs
Normal file
426
lib/runtime-core/src/parse.rs
Normal file
@ -0,0 +1,426 @@
|
||||
use crate::codegen::*;
|
||||
use crate::{
|
||||
backend::{Backend, CompilerConfig, RunnableModule},
|
||||
error::CompileError,
|
||||
module::{
|
||||
DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder,
|
||||
TableInitializer,
|
||||
},
|
||||
structures::{Map, TypedIndex},
|
||||
types::{
|
||||
ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit,
|
||||
ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor,
|
||||
TableIndex, Type, Value,
|
||||
},
|
||||
units::Pages,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use wasmparser::{
|
||||
BinaryReaderError, ExternalKind, FuncType, ImportSectionEntryType, Operator, Type as WpType,
|
||||
WasmDecoder,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LoadError {
|
||||
Parse(BinaryReaderError),
|
||||
Codegen(String),
|
||||
}
|
||||
|
||||
impl From<LoadError> for CompileError {
|
||||
fn from(other: LoadError) -> CompileError {
|
||||
CompileError::InternalError {
|
||||
msg: format!("{:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BinaryReaderError> for LoadError {
|
||||
fn from(other: BinaryReaderError) -> LoadError {
|
||||
LoadError::Parse(other)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_module<
|
||||
MCG: ModuleCodeGenerator<FCG, RM, E>,
|
||||
FCG: FunctionCodeGenerator<E>,
|
||||
RM: RunnableModule,
|
||||
E: Debug,
|
||||
>(
|
||||
wasm: &[u8],
|
||||
backend: Backend,
|
||||
mcg: &mut MCG,
|
||||
middlewares: &mut MiddlewareChain,
|
||||
compiler_config: &CompilerConfig,
|
||||
) -> Result<ModuleInfo, LoadError> {
|
||||
let mut info = ModuleInfo {
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
tables: Map::new(),
|
||||
|
||||
imported_functions: Map::new(),
|
||||
imported_memories: Map::new(),
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
|
||||
exports: Default::default(),
|
||||
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
|
||||
start_func: None,
|
||||
|
||||
func_assoc: Map::new(),
|
||||
signatures: Map::new(),
|
||||
backend: backend,
|
||||
|
||||
namespace_table: StringTable::new(),
|
||||
name_table: StringTable::new(),
|
||||
|
||||
em_symbol_map: compiler_config.symbol_map.clone(),
|
||||
|
||||
custom_sections: HashMap::new(),
|
||||
};
|
||||
|
||||
let mut parser = wasmparser::ValidatingParser::new(
|
||||
wasm,
|
||||
Some(wasmparser::ValidatingParserConfig {
|
||||
operator_config: wasmparser::OperatorValidatorConfig {
|
||||
enable_threads: false,
|
||||
enable_reference_types: false,
|
||||
enable_simd: false,
|
||||
enable_bulk_memory: false,
|
||||
},
|
||||
mutable_global_imports: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let mut namespace_builder = Some(StringTableBuilder::new());
|
||||
let mut name_builder = Some(StringTableBuilder::new());
|
||||
let mut func_count: usize = ::std::usize::MAX;
|
||||
|
||||
loop {
|
||||
use wasmparser::ParserState;
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
ParserState::EndWasm => break Ok(info),
|
||||
ParserState::Error(err) => Err(LoadError::Parse(err))?,
|
||||
ParserState::TypeSectionEntry(ref ty) => {
|
||||
info.signatures.push(func_type_to_func_sig(ty)?);
|
||||
}
|
||||
ParserState::ImportSectionEntry { module, field, ty } => {
|
||||
let namespace_index = namespace_builder.as_mut().unwrap().register(module);
|
||||
let name_index = name_builder.as_mut().unwrap().register(field);
|
||||
let import_name = ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
};
|
||||
|
||||
match ty {
|
||||
ImportSectionEntryType::Function(sigindex) => {
|
||||
let sigindex = SigIndex::new(sigindex as usize);
|
||||
info.imported_functions.push(import_name);
|
||||
info.func_assoc.push(sigindex);
|
||||
mcg.feed_import_function()
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
}
|
||||
ImportSectionEntryType::Table(table_ty) => {
|
||||
assert_eq!(table_ty.element_type, WpType::AnyFunc);
|
||||
let table_desc = TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: table_ty.limits.initial,
|
||||
maximum: table_ty.limits.maximum,
|
||||
};
|
||||
|
||||
info.imported_tables.push((import_name, table_desc));
|
||||
}
|
||||
ImportSectionEntryType::Memory(memory_ty) => {
|
||||
let mem_desc = MemoryDescriptor {
|
||||
minimum: Pages(memory_ty.limits.initial),
|
||||
maximum: memory_ty.limits.maximum.map(|max| Pages(max)),
|
||||
shared: memory_ty.shared,
|
||||
};
|
||||
info.imported_memories.push((import_name, mem_desc));
|
||||
}
|
||||
ImportSectionEntryType::Global(global_ty) => {
|
||||
let global_desc = GlobalDescriptor {
|
||||
mutable: global_ty.mutable,
|
||||
ty: wp_type_to_type(global_ty.content_type)?,
|
||||
};
|
||||
info.imported_globals.push((import_name, global_desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
ParserState::FunctionSectionEntry(sigindex) => {
|
||||
let sigindex = SigIndex::new(sigindex as usize);
|
||||
info.func_assoc.push(sigindex);
|
||||
}
|
||||
ParserState::TableSectionEntry(table_ty) => {
|
||||
let table_desc = TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: table_ty.limits.initial,
|
||||
maximum: table_ty.limits.maximum,
|
||||
};
|
||||
|
||||
info.tables.push(table_desc);
|
||||
}
|
||||
ParserState::MemorySectionEntry(memory_ty) => {
|
||||
let mem_desc = MemoryDescriptor {
|
||||
minimum: Pages(memory_ty.limits.initial),
|
||||
maximum: memory_ty.limits.maximum.map(|max| Pages(max)),
|
||||
shared: memory_ty.shared,
|
||||
};
|
||||
|
||||
info.memories.push(mem_desc);
|
||||
}
|
||||
ParserState::ExportSectionEntry { field, kind, index } => {
|
||||
let export_index = match kind {
|
||||
ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)),
|
||||
ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)),
|
||||
ExternalKind::Memory => ExportIndex::Memory(MemoryIndex::new(index as usize)),
|
||||
ExternalKind::Global => ExportIndex::Global(GlobalIndex::new(index as usize)),
|
||||
};
|
||||
|
||||
info.exports.insert(field.to_string(), export_index);
|
||||
}
|
||||
ParserState::StartSectionEntry(start_index) => {
|
||||
info.start_func = Some(FuncIndex::new(start_index as usize));
|
||||
}
|
||||
ParserState::BeginFunctionBody { .. } => {
|
||||
let id = func_count.wrapping_add(1);
|
||||
func_count = id;
|
||||
if func_count == 0 {
|
||||
info.namespace_table = namespace_builder.take().unwrap().finish();
|
||||
info.name_table = name_builder.take().unwrap().finish();
|
||||
mcg.feed_signatures(info.signatures.clone())
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
mcg.feed_function_signatures(info.func_assoc.clone())
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
mcg.check_precondition(&info)
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
}
|
||||
|
||||
let fcg = mcg
|
||||
.next_function()
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
middlewares
|
||||
.run(
|
||||
Some(fcg),
|
||||
Event::Internal(InternalEvent::FunctionBegin(id as u32)),
|
||||
&info,
|
||||
)
|
||||
.map_err(|x| LoadError::Codegen(x))?;
|
||||
|
||||
let sig = info
|
||||
.signatures
|
||||
.get(
|
||||
*info
|
||||
.func_assoc
|
||||
.get(FuncIndex::new(id as usize + info.imported_functions.len()))
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
for ret in sig.returns() {
|
||||
fcg.feed_return(type_to_wp_type(*ret))
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
}
|
||||
for param in sig.params() {
|
||||
fcg.feed_param(type_to_wp_type(*param))
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
}
|
||||
|
||||
let mut body_begun = false;
|
||||
|
||||
loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
ParserState::Error(err) => return Err(LoadError::Parse(err)),
|
||||
ParserState::FunctionBodyLocals { ref locals } => {
|
||||
for &(count, ty) in locals.iter() {
|
||||
fcg.feed_local(ty, count as usize)
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
}
|
||||
}
|
||||
ParserState::CodeOperator(ref op) => {
|
||||
if !body_begun {
|
||||
body_begun = true;
|
||||
fcg.begin_body(&info)
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
}
|
||||
middlewares
|
||||
.run(Some(fcg), Event::Wasm(op), &info)
|
||||
.map_err(|x| LoadError::Codegen(x))?;
|
||||
}
|
||||
ParserState::EndFunctionBody => break,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
middlewares
|
||||
.run(
|
||||
Some(fcg),
|
||||
Event::Internal(InternalEvent::FunctionEnd),
|
||||
&info,
|
||||
)
|
||||
.map_err(|x| LoadError::Codegen(x))?;
|
||||
fcg.finalize()
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
}
|
||||
ParserState::BeginActiveElementSectionEntry(table_index) => {
|
||||
let table_index = TableIndex::new(table_index as usize);
|
||||
let mut elements: Option<Vec<FuncIndex>> = None;
|
||||
let mut base: Option<Initializer> = None;
|
||||
|
||||
loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
ParserState::Error(err) => return Err(LoadError::Parse(err)),
|
||||
ParserState::InitExpressionOperator(ref op) => {
|
||||
base = Some(eval_init_expr(op)?)
|
||||
}
|
||||
ParserState::ElementSectionEntryBody(ref _elements) => {
|
||||
elements = Some(
|
||||
_elements
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|index| FuncIndex::new(index as usize))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
ParserState::BeginInitExpressionBody
|
||||
| ParserState::EndInitExpressionBody => {}
|
||||
ParserState::EndElementSectionEntry => break,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let table_init = TableInitializer {
|
||||
table_index,
|
||||
base: base.unwrap(),
|
||||
elements: elements.unwrap(),
|
||||
};
|
||||
|
||||
info.elem_initializers.push(table_init);
|
||||
}
|
||||
ParserState::BeginActiveDataSectionEntry(memory_index) => {
|
||||
let memory_index = MemoryIndex::new(memory_index as usize);
|
||||
let mut base: Option<Initializer> = None;
|
||||
let mut data: Vec<u8> = vec![];
|
||||
|
||||
loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
ParserState::Error(err) => return Err(LoadError::Parse(err)),
|
||||
ParserState::InitExpressionOperator(ref op) => {
|
||||
base = Some(eval_init_expr(op)?)
|
||||
}
|
||||
ParserState::DataSectionEntryBodyChunk(chunk) => {
|
||||
data.extend_from_slice(chunk);
|
||||
}
|
||||
ParserState::BeginInitExpressionBody
|
||||
| ParserState::EndInitExpressionBody => {}
|
||||
ParserState::BeginDataSectionEntryBody(_)
|
||||
| ParserState::EndDataSectionEntryBody => {}
|
||||
ParserState::EndDataSectionEntry => break,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let data_init = DataInitializer {
|
||||
memory_index,
|
||||
base: base.unwrap(),
|
||||
data,
|
||||
};
|
||||
info.data_initializers.push(data_init);
|
||||
}
|
||||
ParserState::BeginGlobalSectionEntry(ty) => {
|
||||
let init = loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
ParserState::Error(err) => return Err(LoadError::Parse(err)),
|
||||
ParserState::InitExpressionOperator(ref op) => {
|
||||
break eval_init_expr(op)?;
|
||||
}
|
||||
ParserState::BeginInitExpressionBody => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
let desc = GlobalDescriptor {
|
||||
mutable: ty.mutable,
|
||||
ty: wp_type_to_type(ty.content_type)?,
|
||||
};
|
||||
|
||||
let global_init = GlobalInit { desc, init };
|
||||
|
||||
info.globals.push(global_init);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wp_type_to_type(ty: WpType) -> Result<Type, BinaryReaderError> {
|
||||
Ok(match ty {
|
||||
WpType::I32 => Type::I32,
|
||||
WpType::I64 => Type::I64,
|
||||
WpType::F32 => Type::F32,
|
||||
WpType::F64 => Type::F64,
|
||||
WpType::V128 => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "the wasmer llvm backend does not yet support the simd extension",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
_ => panic!("broken invariant, invalid type"),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn type_to_wp_type(ty: Type) -> WpType {
|
||||
match ty {
|
||||
Type::I32 => WpType::I32,
|
||||
Type::I64 => WpType::I64,
|
||||
Type::F32 => WpType::F32,
|
||||
Type::F64 => WpType::F64,
|
||||
}
|
||||
}
|
||||
|
||||
fn func_type_to_func_sig(func_ty: &FuncType) -> Result<FuncSig, BinaryReaderError> {
|
||||
assert_eq!(func_ty.form, WpType::Func);
|
||||
|
||||
Ok(FuncSig::new(
|
||||
func_ty
|
||||
.params
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(wp_type_to_type)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
func_ty
|
||||
.returns
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(wp_type_to_type)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn eval_init_expr(op: &Operator) -> Result<Initializer, BinaryReaderError> {
|
||||
Ok(match *op {
|
||||
Operator::GetGlobal { global_index } => {
|
||||
Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize))
|
||||
}
|
||||
Operator::I32Const { value } => Initializer::Const(Value::I32(value)),
|
||||
Operator::I64Const { value } => Initializer::Const(Value::I64(value)),
|
||||
Operator::F32Const { value } => {
|
||||
Initializer::Const(Value::F32(f32::from_bits(value.bits())))
|
||||
}
|
||||
Operator::F64Const { value } => {
|
||||
Initializer::Const(Value::F64(f64::from_bits(value.bits())))
|
||||
}
|
||||
_ => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "init expr evaluation failed: unsupported opcode",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
@ -33,7 +33,13 @@ impl Memory {
|
||||
|
||||
let protect = protection.to_protect_const();
|
||||
|
||||
let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, MEM_RESERVE, protect) };
|
||||
let flags = if protection == Protect::None {
|
||||
MEM_RESERVE
|
||||
} else {
|
||||
MEM_RESERVE | MEM_COMMIT
|
||||
};
|
||||
|
||||
let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, flags, protect) };
|
||||
|
||||
if ptr.is_null() {
|
||||
Err("unable to allocate memory".to_string())
|
||||
@ -229,3 +235,25 @@ fn round_up_to_page_size(size: usize, page_size: usize) -> usize {
|
||||
fn round_down_to_page_size(size: usize, page_size: usize) -> usize {
|
||||
size & !(page_size - 1)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn clone() {
|
||||
// these should work
|
||||
let _ = Memory::with_size_protect(200_000, Protect::Read)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let _ = Memory::with_size_protect(200_000, Protect::ReadWrite)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let _ = Memory::with_size_protect(200_000, Protect::ReadExec)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
// this would cause segmentation fault as uncommited memory with no access
|
||||
//let _ = Memory::with_size_protect(200_000, Protect::None).unwrap().clone();
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,111 @@
|
||||
use crate::{
|
||||
backend::UserTrapper,
|
||||
error::RuntimeError,
|
||||
export::{Context, Export, FuncPointer},
|
||||
import::IsExport,
|
||||
types::{FuncSig, Type, WasmExternType},
|
||||
vm::Ctx,
|
||||
types::{FuncSig, NativeWasmType, Type, WasmExternType},
|
||||
vm::{self, Ctx},
|
||||
};
|
||||
use std::{
|
||||
any::Any,
|
||||
convert::Infallible,
|
||||
ffi::c_void,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
mem, panic,
|
||||
ptr::{self, NonNull},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{any::Any, cell::UnsafeCell, marker::PhantomData, mem, panic, ptr, sync::Arc};
|
||||
|
||||
thread_local! {
|
||||
pub static EARLY_TRAPPER: UnsafeCell<Option<Box<dyn UserTrapper>>> = UnsafeCell::new(None);
|
||||
#[repr(C)]
|
||||
pub enum WasmTrapInfo {
|
||||
Unreachable = 0,
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
MemoryOutOfBounds = 2,
|
||||
CallIndirectOOB = 3,
|
||||
IllegalArithmetic = 4,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub trait Safeness {}
|
||||
pub struct Safe;
|
||||
pub struct Unsafe;
|
||||
impl Safeness for Safe {}
|
||||
impl Safeness for Unsafe {}
|
||||
impl fmt::Display for WasmTrapInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
WasmTrapInfo::Unreachable => "unreachable",
|
||||
WasmTrapInfo::IncorrectCallIndirectSignature => {
|
||||
"incorrect `call_indirect` signature"
|
||||
}
|
||||
WasmTrapInfo::MemoryOutOfBounds => "memory out-of-bounds access",
|
||||
WasmTrapInfo::CallIndirectOOB => "`call_indirect` out-of-bounds",
|
||||
WasmTrapInfo::IllegalArithmetic => "illegal arithmetic operation",
|
||||
WasmTrapInfo::Unknown => "unknown",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is just an empty trait to constrict that types that
|
||||
/// can be put into the third/fourth (depending if you include lifetimes)
|
||||
/// of the `Func` struct.
|
||||
pub trait Kind {}
|
||||
|
||||
pub type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull<vm::Func>, *const u64, *mut u64);
|
||||
pub type Invoke = unsafe extern "C" fn(
|
||||
Trampoline,
|
||||
*mut Ctx,
|
||||
NonNull<vm::Func>,
|
||||
*const u64,
|
||||
*mut u64,
|
||||
*mut WasmTrapInfo,
|
||||
*mut Option<Box<dyn Any>>,
|
||||
Option<NonNull<c_void>>,
|
||||
) -> bool;
|
||||
|
||||
/// TODO(lachlan): Naming TBD.
|
||||
/// This contains the trampoline and invoke functions for a specific signature,
|
||||
/// as well as the environment that the invoke function may or may not require.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Wasm {
|
||||
pub(crate) trampoline: Trampoline,
|
||||
pub(crate) invoke: Invoke,
|
||||
pub(crate) invoke_env: Option<NonNull<c_void>>,
|
||||
}
|
||||
|
||||
impl Wasm {
|
||||
pub unsafe fn from_raw_parts(
|
||||
trampoline: Trampoline,
|
||||
invoke: Invoke,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
trampoline,
|
||||
invoke,
|
||||
invoke_env,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This type, as part of the `Func` type signature, represents a function that is created
|
||||
/// by the host.
|
||||
pub struct Host(());
|
||||
impl Kind for Wasm {}
|
||||
impl Kind for Host {}
|
||||
|
||||
pub trait WasmTypeList {
|
||||
type CStruct;
|
||||
type RetArray: AsMut<[u64]>;
|
||||
fn from_ret_array(array: Self::RetArray) -> Self;
|
||||
fn empty_ret_array() -> Self::RetArray;
|
||||
fn from_c_struct(c_struct: Self::CStruct) -> Self;
|
||||
fn into_c_struct(self) -> Self::CStruct;
|
||||
fn types() -> &'static [Type];
|
||||
unsafe fn call<Rets>(self, f: *const (), ctx: *mut Ctx) -> Rets
|
||||
unsafe fn call<Rets>(
|
||||
self,
|
||||
f: NonNull<vm::Func>,
|
||||
wasm: Wasm,
|
||||
ctx: *mut Ctx,
|
||||
) -> Result<Rets, RuntimeError>
|
||||
where
|
||||
Rets: WasmTypeList;
|
||||
}
|
||||
@ -33,21 +115,23 @@ where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn to_raw(&self) -> *const ();
|
||||
fn to_raw(&self) -> NonNull<vm::Func>;
|
||||
}
|
||||
|
||||
pub trait TrapEarly<Rets>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn report(self) -> Result<Rets, Box<dyn Any>>;
|
||||
type Error: 'static;
|
||||
fn report(self) -> Result<Rets, Self::Error>;
|
||||
}
|
||||
|
||||
impl<Rets> TrapEarly<Rets> for Rets
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn report(self) -> Result<Rets, Box<dyn Any>> {
|
||||
type Error = Infallible;
|
||||
fn report(self) -> Result<Rets, Infallible> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@ -55,10 +139,11 @@ where
|
||||
impl<Rets, E> TrapEarly<Rets> for Result<Rets, E>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
E: Any,
|
||||
E: 'static,
|
||||
{
|
||||
fn report(self) -> Result<Rets, Box<dyn Any>> {
|
||||
self.map_err(|err| Box::new(err) as Box<dyn Any>)
|
||||
type Error = E;
|
||||
fn report(self) -> Result<Rets, E> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,19 +156,25 @@ where
|
||||
// Func::new(f)
|
||||
// }
|
||||
|
||||
pub struct Func<'a, Args = (), Rets = (), Safety: Safeness = Safe> {
|
||||
f: *const (),
|
||||
pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> {
|
||||
inner: Inner,
|
||||
f: NonNull<vm::Func>,
|
||||
ctx: *mut Ctx,
|
||||
_phantom: PhantomData<(&'a (), Safety, Args, Rets)>,
|
||||
_phantom: PhantomData<(&'a (), Args, Rets)>,
|
||||
}
|
||||
|
||||
impl<'a, Args, Rets> Func<'a, Args, Rets, Safe>
|
||||
impl<'a, Args, Rets> Func<'a, Args, Rets, Wasm>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
pub(crate) unsafe fn new_from_ptr(f: *const (), ctx: *mut Ctx) -> Func<'a, Args, Rets, Safe> {
|
||||
pub(crate) unsafe fn from_raw_parts(
|
||||
inner: Wasm,
|
||||
f: NonNull<vm::Func>,
|
||||
ctx: *mut Ctx,
|
||||
) -> Func<'a, Args, Rets, Wasm> {
|
||||
Func {
|
||||
inner,
|
||||
f,
|
||||
ctx,
|
||||
_phantom: PhantomData,
|
||||
@ -91,16 +182,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Args, Rets> Func<'a, Args, Rets, Unsafe>
|
||||
impl<'a, Args, Rets> Func<'a, Args, Rets, Host>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
pub fn new<F>(f: F) -> Func<'a, Args, Rets, Unsafe>
|
||||
pub fn new<F>(f: F) -> Func<'a, Args, Rets, Host>
|
||||
where
|
||||
F: ExternalFunction<Args, Rets>,
|
||||
{
|
||||
Func {
|
||||
inner: Host(()),
|
||||
f: f.to_raw(),
|
||||
ctx: ptr::null_mut(),
|
||||
_phantom: PhantomData,
|
||||
@ -108,11 +200,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Args, Rets, Safety> Func<'a, Args, Rets, Safety>
|
||||
impl<'a, Args, Rets, Inner> Func<'a, Args, Rets, Inner>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
Safety: Safeness,
|
||||
Inner: Kind,
|
||||
{
|
||||
pub fn params(&self) -> &'static [Type] {
|
||||
Args::types()
|
||||
@ -122,110 +214,205 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmTypeList for Infallible {
|
||||
type CStruct = Infallible;
|
||||
type RetArray = [u64; 0];
|
||||
fn from_ret_array(_: Self::RetArray) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
fn empty_ret_array() -> Self::RetArray {
|
||||
unreachable!()
|
||||
}
|
||||
fn from_c_struct(_: Self::CStruct) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
unreachable!()
|
||||
}
|
||||
fn types() -> &'static [Type] {
|
||||
&[]
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn call<Rets: WasmTypeList>(
|
||||
self,
|
||||
_: NonNull<vm::Func>,
|
||||
_: Wasm,
|
||||
_: *mut Ctx,
|
||||
) -> Result<Rets, RuntimeError> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: WasmExternType> WasmTypeList for (A,) {
|
||||
type CStruct = S1<A>;
|
||||
type RetArray = [u64; 1];
|
||||
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||
(WasmExternType::from_native(NativeWasmType::from_binary(
|
||||
array[0],
|
||||
)),)
|
||||
}
|
||||
fn empty_ret_array() -> Self::RetArray {
|
||||
[0u64]
|
||||
}
|
||||
fn from_c_struct(c_struct: Self::CStruct) -> Self {
|
||||
let S1(a) = c_struct;
|
||||
(a,)
|
||||
(WasmExternType::from_native(a),)
|
||||
}
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
#[allow(unused_parens, non_snake_case)]
|
||||
let (a,) = self;
|
||||
S1(a)
|
||||
S1(WasmExternType::to_native(a))
|
||||
}
|
||||
fn types() -> &'static [Type] {
|
||||
&[A::TYPE]
|
||||
&[A::Native::TYPE]
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn call<Rets: WasmTypeList>(self, f: *const (), ctx: *mut Ctx) -> Rets {
|
||||
let f: extern "C" fn(*mut Ctx, A) -> Rets = mem::transmute(f);
|
||||
unsafe fn call<Rets: WasmTypeList>(
|
||||
self,
|
||||
f: NonNull<vm::Func>,
|
||||
wasm: Wasm,
|
||||
ctx: *mut Ctx,
|
||||
) -> Result<Rets, RuntimeError> {
|
||||
let (a,) = self;
|
||||
f(ctx, a)
|
||||
let args = [a.to_native().to_binary()];
|
||||
let mut rets = Rets::empty_ret_array();
|
||||
let mut trap = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
|
||||
if (wasm.invoke)(
|
||||
wasm.trampoline,
|
||||
ctx,
|
||||
f,
|
||||
args.as_ptr(),
|
||||
rets.as_mut().as_mut_ptr(),
|
||||
&mut trap,
|
||||
&mut user_error,
|
||||
wasm.invoke_env,
|
||||
) {
|
||||
Ok(Rets::from_ret_array(rets))
|
||||
} else {
|
||||
if let Some(data) = user_error {
|
||||
Err(RuntimeError::Error { data })
|
||||
} else {
|
||||
Err(RuntimeError::Trap {
|
||||
msg: trap.to_string().into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Safe>
|
||||
impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Wasm>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
pub fn call(&self, a: A) -> Result<Rets, RuntimeError> {
|
||||
Ok(unsafe { <A as WasmTypeList>::call(a, self.f, self.ctx) })
|
||||
unsafe { <A as WasmTypeList>::call(a, self.f, self.inner, self.ctx) }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_traits {
|
||||
( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => {
|
||||
#[repr($repr)]
|
||||
pub struct $struct_name <$( $x ),*> ( $( $x ),* );
|
||||
pub struct $struct_name <$( $x: WasmExternType ),*> ( $( <$x as WasmExternType>::Native ),* );
|
||||
|
||||
impl< $( $x: WasmExternType, )* > WasmTypeList for ( $( $x ),* ) {
|
||||
type CStruct = $struct_name<$( $x ),*>;
|
||||
type RetArray = [u64; count_idents!( $( $x ),* )];
|
||||
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||
#[allow(non_snake_case)]
|
||||
let [ $( $x ),* ] = array;
|
||||
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
|
||||
}
|
||||
fn empty_ret_array() -> Self::RetArray {
|
||||
[0; count_idents!( $( $x ),* )]
|
||||
}
|
||||
fn from_c_struct(c_struct: Self::CStruct) -> Self {
|
||||
#[allow(non_snake_case)]
|
||||
let $struct_name ( $( $x ),* ) = c_struct;
|
||||
( $( $x ),* )
|
||||
( $( WasmExternType::from_native($x) ),* )
|
||||
}
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
#[allow(unused_parens, non_snake_case)]
|
||||
let ( $( $x ),* ) = self;
|
||||
$struct_name ( $( $x ),* )
|
||||
$struct_name ( $( WasmExternType::to_native($x) ),* )
|
||||
}
|
||||
fn types() -> &'static [Type] {
|
||||
&[$( $x::TYPE, )*]
|
||||
&[$( $x::Native::TYPE, )*]
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn call<Rets: WasmTypeList>(self, f: *const (), ctx: *mut Ctx) -> Rets {
|
||||
let f: extern fn(*mut Ctx $( ,$x )*) -> Rets::CStruct = mem::transmute(f);
|
||||
unsafe fn call<Rets: WasmTypeList>(self, f: NonNull<vm::Func>, wasm: Wasm, ctx: *mut Ctx) -> Result<Rets, RuntimeError> {
|
||||
#[allow(unused_parens)]
|
||||
let ( $( $x ),* ) = self;
|
||||
let c_struct = f(ctx $( ,$x )*);
|
||||
Rets::from_c_struct(c_struct)
|
||||
let args = [ $( $x.to_native().to_binary()),* ];
|
||||
let mut rets = Rets::empty_ret_array();
|
||||
let mut trap = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
|
||||
if (wasm.invoke)(wasm.trampoline, ctx, f, args.as_ptr(), rets.as_mut().as_mut_ptr(), &mut trap, &mut user_error, wasm.invoke_env) {
|
||||
Ok(Rets::from_ret_array(rets))
|
||||
} else {
|
||||
if let Some(data) = user_error {
|
||||
Err(RuntimeError::Error { data })
|
||||
} else {
|
||||
Err(RuntimeError::Trap { msg: trap.to_string().into() })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN {
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(&self) -> *const () {
|
||||
fn to_raw(&self) -> NonNull<vm::Func> {
|
||||
assert_eq!(mem::size_of::<Self>(), 0, "you cannot use a closure that captures state for `Func`.");
|
||||
|
||||
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: $x )* ) -> Rets::CStruct {
|
||||
/// This is required for the llvm backend to be able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct {
|
||||
let f: FN = unsafe { mem::transmute_copy(&()) };
|
||||
|
||||
let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
f( ctx $( ,$x )* ).report()
|
||||
f( ctx $( ,WasmExternType::from_native($x) )* ).report()
|
||||
})) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => err,
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
b as Box<dyn Any>
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
if let Some(early_trapper) = &*EARLY_TRAPPER.with(|ucell| ucell.get()) {
|
||||
early_trapper.do_early_trap(err)
|
||||
} else {
|
||||
eprintln!("panic handling not setup");
|
||||
std::process::exit(1)
|
||||
}
|
||||
(&*ctx.module).runnable_module.do_early_trap(err)
|
||||
}
|
||||
}
|
||||
|
||||
wrap::<$( $x, )* Rets, Trap, Self> as *const ()
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Safe>
|
||||
impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Wasm>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
|
||||
#[allow(unused_parens)]
|
||||
Ok(unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.ctx) })
|
||||
unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.inner, self.ctx) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! count_idents {
|
||||
( $($idents:ident),* ) => {{
|
||||
#[allow(dead_code, non_camel_case_types)]
|
||||
enum Idents { $($idents,)* __CountIdentsLast }
|
||||
const COUNT: usize = Idents::__CountIdentsLast as usize;
|
||||
COUNT
|
||||
}};
|
||||
}
|
||||
|
||||
impl_traits!([C] S0,);
|
||||
impl_traits!([transparent] S1, A);
|
||||
impl_traits!([C] S2, A, B);
|
||||
@ -240,14 +427,14 @@ impl_traits!([C] S10, A, B, C, D, E, F, G, H, I, J);
|
||||
impl_traits!([C] S11, A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_traits!([C] S12, A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
|
||||
impl<'a, Args, Rets, Safety> IsExport for Func<'a, Args, Rets, Safety>
|
||||
impl<'a, Args, Rets, Inner> IsExport for Func<'a, Args, Rets, Inner>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
Safety: Safeness,
|
||||
Inner: Kind,
|
||||
{
|
||||
fn to_export(&self) -> Export {
|
||||
let func = unsafe { FuncPointer::new(self.f as _) };
|
||||
let func = unsafe { FuncPointer::new(self.f.as_ptr()) };
|
||||
let ctx = Context::Internal;
|
||||
let signature = Arc::new(FuncSig::new(Args::types(), Rets::types()));
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, units::Pages};
|
||||
use std::{borrow::Cow, mem};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Represents a WebAssembly type.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -71,29 +71,150 @@ impl From<f64> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe trait WasmExternType: Copy + Clone
|
||||
pub unsafe trait NativeWasmType: Copy + Into<Value>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
const TYPE: Type;
|
||||
fn from_binary(bits: u64) -> Self;
|
||||
fn to_binary(self) -> u64;
|
||||
}
|
||||
|
||||
unsafe impl NativeWasmType for i32 {
|
||||
const TYPE: Type = Type::I32;
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
bits as _
|
||||
}
|
||||
fn to_binary(self) -> u64 {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl NativeWasmType for i64 {
|
||||
const TYPE: Type = Type::I64;
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
bits as _
|
||||
}
|
||||
fn to_binary(self) -> u64 {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl NativeWasmType for f32 {
|
||||
const TYPE: Type = Type::F32;
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
f32::from_bits(bits as u32)
|
||||
}
|
||||
fn to_binary(self) -> u64 {
|
||||
self.to_bits() as _
|
||||
}
|
||||
}
|
||||
unsafe impl NativeWasmType for f64 {
|
||||
const TYPE: Type = Type::F64;
|
||||
fn from_binary(bits: u64) -> Self {
|
||||
f64::from_bits(bits)
|
||||
}
|
||||
fn to_binary(self) -> u64 {
|
||||
self.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe trait WasmExternType: Copy
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
type Native: NativeWasmType;
|
||||
fn from_native(native: Self::Native) -> Self;
|
||||
fn to_native(self) -> Self::Native;
|
||||
}
|
||||
|
||||
unsafe impl WasmExternType for i8 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for u8 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for i16 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for u16 {
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for i32 {
|
||||
const TYPE: Type = Type::I32;
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for u32 {
|
||||
const TYPE: Type = Type::I32;
|
||||
type Native = i32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for i64 {
|
||||
const TYPE: Type = Type::I64;
|
||||
type Native = i64;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for u64 {
|
||||
const TYPE: Type = Type::I64;
|
||||
type Native = i64;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for f32 {
|
||||
const TYPE: Type = Type::F32;
|
||||
type Native = f32;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self
|
||||
}
|
||||
}
|
||||
unsafe impl WasmExternType for f64 {
|
||||
const TYPE: Type = Type::F64;
|
||||
type Native = f64;
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native
|
||||
}
|
||||
fn to_native(self) -> Self::Native {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// pub trait IntegerAtomic
|
||||
@ -113,34 +234,15 @@ unsafe impl WasmExternType for f64 {
|
||||
// fn swap(&self, other: Self::Primitive) -> Self::Primitive;
|
||||
// }
|
||||
|
||||
pub enum ValueError {
|
||||
BufferTooSmall,
|
||||
}
|
||||
|
||||
pub trait ValueType: Copy
|
||||
pub unsafe trait ValueType: Copy
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn into_le(self, buffer: &mut [u8]);
|
||||
fn from_le(buffer: &[u8]) -> Result<Self, ValueError>;
|
||||
}
|
||||
|
||||
macro_rules! convert_value_impl {
|
||||
($t:ty) => {
|
||||
impl ValueType for $t {
|
||||
fn into_le(self, buffer: &mut [u8]) {
|
||||
buffer[..mem::size_of::<Self>()].copy_from_slice(&self.to_le_bytes());
|
||||
}
|
||||
fn from_le(buffer: &[u8]) -> Result<Self, ValueError> {
|
||||
if buffer.len() >= mem::size_of::<Self>() {
|
||||
let mut array = [0u8; mem::size_of::<Self>()];
|
||||
array.copy_from_slice(&buffer[..mem::size_of::<Self>()]);
|
||||
Ok(Self::from_le_bytes(array))
|
||||
} else {
|
||||
Err(ValueError::BufferTooSmall)
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe impl ValueType for $t {}
|
||||
};
|
||||
( $($t:ty),* ) => {
|
||||
$(
|
||||
@ -149,25 +251,7 @@ macro_rules! convert_value_impl {
|
||||
};
|
||||
}
|
||||
|
||||
convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64);
|
||||
|
||||
impl ValueType for f32 {
|
||||
fn into_le(self, buffer: &mut [u8]) {
|
||||
self.to_bits().into_le(buffer);
|
||||
}
|
||||
fn from_le(buffer: &[u8]) -> Result<Self, ValueError> {
|
||||
Ok(f32::from_bits(<u32 as ValueType>::from_le(buffer)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueType for f64 {
|
||||
fn into_le(self, buffer: &mut [u8]) {
|
||||
self.to_bits().into_le(buffer);
|
||||
}
|
||||
fn from_le(buffer: &[u8]) -> Result<Self, ValueError> {
|
||||
Ok(f64::from_bits(<u64 as ValueType>::from_le(buffer)?))
|
||||
}
|
||||
}
|
||||
convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ElementType {
|
||||
@ -207,6 +291,7 @@ pub enum Initializer {
|
||||
GetGlobal(ImportedGlobalIndex),
|
||||
}
|
||||
|
||||
/// Describes the mutability and type of a Global
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct GlobalDescriptor {
|
||||
pub mutable: bool,
|
||||
@ -316,7 +401,7 @@ pub trait LocalImport {
|
||||
macro_rules! define_map_index {
|
||||
($ty:ident) => {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct $ty (u32);
|
||||
impl TypedIndex for $ty {
|
||||
#[doc(hidden)]
|
||||
@ -431,3 +516,58 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::NativeWasmType;
|
||||
use crate::types::WasmExternType;
|
||||
|
||||
#[test]
|
||||
fn test_native_types_round_trip() {
|
||||
assert_eq!(
|
||||
42i32,
|
||||
i32::from_native(i32::from_binary((42i32).to_native().to_binary()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
-42i32,
|
||||
i32::from_native(i32::from_binary((-42i32).to_native().to_binary()))
|
||||
);
|
||||
|
||||
use std::i64;
|
||||
let xi64 = i64::MAX;
|
||||
assert_eq!(
|
||||
xi64,
|
||||
i64::from_native(i64::from_binary((xi64).to_native().to_binary()))
|
||||
);
|
||||
let yi64 = i64::MIN;
|
||||
assert_eq!(
|
||||
yi64,
|
||||
i64::from_native(i64::from_binary((yi64).to_native().to_binary()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
16.5f32,
|
||||
f32::from_native(f32::from_binary((16.5f32).to_native().to_binary()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
-16.5f32,
|
||||
f32::from_native(f32::from_binary((-16.5f32).to_native().to_binary()))
|
||||
);
|
||||
|
||||
use std::f64;
|
||||
let xf64: f64 = f64::MAX;
|
||||
assert_eq!(
|
||||
xf64,
|
||||
f64::from_native(f64::from_binary((xf64).to_native().to_binary()))
|
||||
);
|
||||
|
||||
let yf64: f64 = f64::MIN;
|
||||
assert_eq!(
|
||||
yf64,
|
||||
f64::from_native(f64::from_binary((yf64).to_native().to_binary()))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ use std::{
|
||||
ops::{Add, Sub},
|
||||
};
|
||||
|
||||
const WASM_PAGE_SIZE: usize = 65_536;
|
||||
const WASM_MAX_PAGES: usize = 65_536;
|
||||
pub const WASM_PAGE_SIZE: usize = 65_536;
|
||||
pub const WASM_MAX_PAGES: usize = 65_536;
|
||||
// From emscripten resize_heap implementation
|
||||
pub const WASM_MIN_PAGES: usize = 256;
|
||||
|
||||
/// Units of WebAssembly pages (as specified to be 65,536 bytes).
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
@ -7,6 +7,8 @@ use crate::{
|
||||
};
|
||||
use std::{ffi::c_void, mem, ptr};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
/// The context of the currently running WebAssembly instance.
|
||||
///
|
||||
/// This is implicitly passed to every webassembly function.
|
||||
@ -22,32 +24,8 @@ use std::{ffi::c_void, mem, ptr};
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Ctx {
|
||||
/// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`.
|
||||
pub(crate) memories: *mut *mut LocalMemory,
|
||||
|
||||
/// A pointer to an array of locally-defined tables, indexed by `TableIndex`.
|
||||
pub(crate) tables: *mut *mut LocalTable,
|
||||
|
||||
/// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`.
|
||||
pub(crate) globals: *mut *mut LocalGlobal,
|
||||
|
||||
/// A pointer to an array of imported memories, indexed by `MemoryIndex,
|
||||
pub(crate) imported_memories: *mut *mut LocalMemory,
|
||||
|
||||
/// A pointer to an array of imported tables, indexed by `TableIndex`.
|
||||
pub(crate) imported_tables: *mut *mut LocalTable,
|
||||
|
||||
/// A pointer to an array of imported globals, indexed by `GlobalIndex`.
|
||||
pub(crate) imported_globals: *mut *mut LocalGlobal,
|
||||
|
||||
/// A pointer to an array of imported functions, indexed by `FuncIndex`.
|
||||
pub(crate) imported_funcs: *mut ImportedFunc,
|
||||
|
||||
/// A pointer to an array of signature ids. Conceptually, this maps
|
||||
/// from a static, module-local signature id to a runtime-global
|
||||
/// signature id. This is used to allow call-indirect to other
|
||||
/// modules safely.
|
||||
pub(crate) dynamic_sigindices: *const SigId,
|
||||
// `internal` must be the first field of `Ctx`.
|
||||
pub(crate) internal: InternalCtx,
|
||||
|
||||
pub(crate) local_functions: *const *const Func,
|
||||
|
||||
@ -55,7 +33,7 @@ pub struct Ctx {
|
||||
/// by the owning `Instance`.
|
||||
local_backing: *mut LocalBacking,
|
||||
import_backing: *mut ImportBacking,
|
||||
module: *const ModuleInner,
|
||||
pub module: *const ModuleInner,
|
||||
|
||||
//// This is intended to be user-supplied, per-instance
|
||||
/// contextual data. There are currently some issue with it,
|
||||
@ -70,7 +48,42 @@ pub struct Ctx {
|
||||
/// If there's a function set in this field, it gets called
|
||||
/// when the context is destructed, e.g. when an `Instance`
|
||||
/// is dropped.
|
||||
pub data_finalizer: Option<extern "C" fn(data: *mut c_void)>,
|
||||
pub data_finalizer: Option<fn(data: *mut c_void)>,
|
||||
}
|
||||
|
||||
/// The internal context of the currently running WebAssembly instance.
|
||||
///
|
||||
///
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct InternalCtx {
|
||||
/// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`.
|
||||
pub memories: *mut *mut LocalMemory,
|
||||
|
||||
/// A pointer to an array of locally-defined tables, indexed by `TableIndex`.
|
||||
pub tables: *mut *mut LocalTable,
|
||||
|
||||
/// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`.
|
||||
pub globals: *mut *mut LocalGlobal,
|
||||
|
||||
/// A pointer to an array of imported memories, indexed by `MemoryIndex,
|
||||
pub imported_memories: *mut *mut LocalMemory,
|
||||
|
||||
/// A pointer to an array of imported tables, indexed by `TableIndex`.
|
||||
pub imported_tables: *mut *mut LocalTable,
|
||||
|
||||
/// A pointer to an array of imported globals, indexed by `GlobalIndex`.
|
||||
pub imported_globals: *mut *mut LocalGlobal,
|
||||
|
||||
/// A pointer to an array of imported functions, indexed by `FuncIndex`.
|
||||
pub imported_funcs: *mut ImportedFunc,
|
||||
|
||||
/// A pointer to an array of signature ids. Conceptually, this maps
|
||||
/// from a static, module-local signature id to a runtime-global
|
||||
/// signature id. This is used to allow call-indirect to other
|
||||
/// modules safely.
|
||||
pub dynamic_sigindices: *const SigId,
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
@ -81,16 +94,18 @@ impl Ctx {
|
||||
module: &ModuleInner,
|
||||
) -> Self {
|
||||
Self {
|
||||
memories: local_backing.vm_memories.as_mut_ptr(),
|
||||
tables: local_backing.vm_tables.as_mut_ptr(),
|
||||
globals: local_backing.vm_globals.as_mut_ptr(),
|
||||
internal: InternalCtx {
|
||||
memories: local_backing.vm_memories.as_mut_ptr(),
|
||||
tables: local_backing.vm_tables.as_mut_ptr(),
|
||||
globals: local_backing.vm_globals.as_mut_ptr(),
|
||||
|
||||
imported_memories: import_backing.vm_memories.as_mut_ptr(),
|
||||
imported_tables: import_backing.vm_tables.as_mut_ptr(),
|
||||
imported_globals: import_backing.vm_globals.as_mut_ptr(),
|
||||
imported_funcs: import_backing.vm_functions.as_mut_ptr(),
|
||||
imported_memories: import_backing.vm_memories.as_mut_ptr(),
|
||||
imported_tables: import_backing.vm_tables.as_mut_ptr(),
|
||||
imported_globals: import_backing.vm_globals.as_mut_ptr(),
|
||||
imported_funcs: import_backing.vm_functions.as_mut_ptr(),
|
||||
|
||||
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
|
||||
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
|
||||
},
|
||||
local_functions: local_backing.local_functions.as_ptr(),
|
||||
|
||||
local_backing,
|
||||
@ -108,19 +123,21 @@ impl Ctx {
|
||||
import_backing: &mut ImportBacking,
|
||||
module: &ModuleInner,
|
||||
data: *mut c_void,
|
||||
data_finalizer: extern "C" fn(*mut c_void),
|
||||
data_finalizer: fn(*mut c_void),
|
||||
) -> Self {
|
||||
Self {
|
||||
memories: local_backing.vm_memories.as_mut_ptr(),
|
||||
tables: local_backing.vm_tables.as_mut_ptr(),
|
||||
globals: local_backing.vm_globals.as_mut_ptr(),
|
||||
internal: InternalCtx {
|
||||
memories: local_backing.vm_memories.as_mut_ptr(),
|
||||
tables: local_backing.vm_tables.as_mut_ptr(),
|
||||
globals: local_backing.vm_globals.as_mut_ptr(),
|
||||
|
||||
imported_memories: import_backing.vm_memories.as_mut_ptr(),
|
||||
imported_tables: import_backing.vm_tables.as_mut_ptr(),
|
||||
imported_globals: import_backing.vm_globals.as_mut_ptr(),
|
||||
imported_funcs: import_backing.vm_functions.as_mut_ptr(),
|
||||
imported_memories: import_backing.vm_memories.as_mut_ptr(),
|
||||
imported_tables: import_backing.vm_tables.as_mut_ptr(),
|
||||
imported_globals: import_backing.vm_globals.as_mut_ptr(),
|
||||
imported_funcs: import_backing.vm_functions.as_mut_ptr(),
|
||||
|
||||
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
|
||||
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
|
||||
},
|
||||
local_functions: local_backing.local_functions.as_ptr(),
|
||||
|
||||
local_backing,
|
||||
@ -164,6 +181,11 @@ impl Ctx {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives access to the emscripten symbol map, used for debugging
|
||||
pub unsafe fn borrow_symbol_map(&self) -> &Option<HashMap<u32, String>> {
|
||||
&(*self.module).info.em_symbol_map
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -356,43 +378,45 @@ impl Anyfunc {
|
||||
|
||||
#[cfg(test)]
|
||||
mod vm_offset_tests {
|
||||
use super::{Anyfunc, Ctx, ImportedFunc, LocalGlobal, LocalMemory, LocalTable};
|
||||
use super::{Anyfunc, Ctx, ImportedFunc, InternalCtx, LocalGlobal, LocalMemory, LocalTable};
|
||||
|
||||
#[test]
|
||||
fn vmctx() {
|
||||
assert_eq!(0usize, offset_of!(Ctx => internal).get_byte_offset(),);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_memories() as usize,
|
||||
offset_of!(Ctx => memories).get_byte_offset(),
|
||||
offset_of!(InternalCtx => memories).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_tables() as usize,
|
||||
offset_of!(Ctx => tables).get_byte_offset(),
|
||||
offset_of!(InternalCtx => tables).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_globals() as usize,
|
||||
offset_of!(Ctx => globals).get_byte_offset(),
|
||||
offset_of!(InternalCtx => globals).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_imported_memories() as usize,
|
||||
offset_of!(Ctx => imported_memories).get_byte_offset(),
|
||||
offset_of!(InternalCtx => imported_memories).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_imported_tables() as usize,
|
||||
offset_of!(Ctx => imported_tables).get_byte_offset(),
|
||||
offset_of!(InternalCtx => imported_tables).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_imported_globals() as usize,
|
||||
offset_of!(Ctx => imported_globals).get_byte_offset(),
|
||||
offset_of!(InternalCtx => imported_globals).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_imported_funcs() as usize,
|
||||
offset_of!(Ctx => imported_funcs).get_byte_offset(),
|
||||
offset_of!(InternalCtx => imported_funcs).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@ -480,7 +504,7 @@ mod vm_ctx_tests {
|
||||
str: String,
|
||||
}
|
||||
|
||||
extern "C" fn test_data_finalizer(data: *mut c_void) {
|
||||
fn test_data_finalizer(data: *mut c_void) {
|
||||
let test_data: &mut TestData = unsafe { &mut *(data as *mut TestData) };
|
||||
assert_eq!(test_data.x, 10);
|
||||
assert_eq!(test_data.y, true);
|
||||
@ -543,52 +567,38 @@ mod vm_ctx_tests {
|
||||
|
||||
fn generate_module() -> ModuleInner {
|
||||
use super::Func;
|
||||
use crate::backend::{
|
||||
sys::Memory, Backend, CacheGen, FuncResolver, ProtectedCaller, Token, UserTrapper,
|
||||
};
|
||||
use crate::cache::{Error as CacheError, WasmHash};
|
||||
use crate::error::RuntimeResult;
|
||||
use crate::types::{FuncIndex, LocalFuncIndex, Value};
|
||||
use crate::backend::{sys::Memory, Backend, CacheGen, RunnableModule};
|
||||
use crate::cache::Error as CacheError;
|
||||
use crate::typed_func::Wasm;
|
||||
use crate::types::{LocalFuncIndex, SigIndex};
|
||||
use hashbrown::HashMap;
|
||||
use std::any::Any;
|
||||
use std::ptr::NonNull;
|
||||
struct Placeholder;
|
||||
impl FuncResolver for Placeholder {
|
||||
fn get(
|
||||
impl RunnableModule for Placeholder {
|
||||
fn get_func(
|
||||
&self,
|
||||
_module: &ModuleInner,
|
||||
_module: &ModuleInfo,
|
||||
_local_func_index: LocalFuncIndex,
|
||||
) -> Option<NonNull<Func>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl ProtectedCaller for Placeholder {
|
||||
fn call(
|
||||
&self,
|
||||
_module: &ModuleInner,
|
||||
_func_index: FuncIndex,
|
||||
_params: &[Value],
|
||||
_import_backing: &ImportBacking,
|
||||
_vmctx: *mut Ctx,
|
||||
_: Token,
|
||||
) -> RuntimeResult<Vec<Value>> {
|
||||
Ok(vec![])
|
||||
|
||||
fn get_trampoline(&self, _module: &ModuleInfo, _sig_index: SigIndex) -> Option<Wasm> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
|
||||
unsafe fn do_early_trap(&self, _: Box<dyn Any>) -> ! {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl CacheGen for Placeholder {
|
||||
fn generate_cache(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError> {
|
||||
fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
ModuleInner {
|
||||
func_resolver: Box::new(Placeholder),
|
||||
protected_caller: Box::new(Placeholder),
|
||||
runnable_module: Box::new(Placeholder),
|
||||
cache_gen: Box::new(Placeholder),
|
||||
info: ModuleInfo {
|
||||
memories: Map::new(),
|
||||
@ -614,6 +624,10 @@ mod vm_ctx_tests {
|
||||
|
||||
namespace_table: StringTable::new(),
|
||||
name_table: StringTable::new(),
|
||||
|
||||
em_symbol_map: None,
|
||||
|
||||
custom_sections: HashMap::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ pub unsafe extern "C" fn local_static_memory_grow(
|
||||
memory_index: LocalMemoryIndex,
|
||||
delta: Pages,
|
||||
) -> i32 {
|
||||
let local_memory = *ctx.memories.add(memory_index.index());
|
||||
let local_memory = *ctx.internal.memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut StaticMemory;
|
||||
|
||||
match (*memory).grow(delta, &mut *local_memory) {
|
||||
@ -30,7 +30,7 @@ pub unsafe extern "C" fn local_static_memory_size(
|
||||
ctx: &vm::Ctx,
|
||||
memory_index: LocalMemoryIndex,
|
||||
) -> Pages {
|
||||
let local_memory = *ctx.memories.add(memory_index.index());
|
||||
let local_memory = *ctx.internal.memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut StaticMemory;
|
||||
|
||||
(*memory).size()
|
||||
@ -41,7 +41,7 @@ pub unsafe extern "C" fn local_dynamic_memory_grow(
|
||||
memory_index: LocalMemoryIndex,
|
||||
delta: Pages,
|
||||
) -> i32 {
|
||||
let local_memory = *ctx.memories.add(memory_index.index());
|
||||
let local_memory = *ctx.internal.memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut DynamicMemory;
|
||||
|
||||
match (*memory).grow(delta, &mut *local_memory) {
|
||||
@ -54,7 +54,7 @@ pub unsafe extern "C" fn local_dynamic_memory_size(
|
||||
ctx: &vm::Ctx,
|
||||
memory_index: LocalMemoryIndex,
|
||||
) -> Pages {
|
||||
let local_memory = *ctx.memories.add(memory_index.index());
|
||||
let local_memory = *ctx.internal.memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut DynamicMemory;
|
||||
|
||||
(*memory).size()
|
||||
@ -69,7 +69,10 @@ pub unsafe extern "C" fn imported_static_memory_grow(
|
||||
import_memory_index: ImportedMemoryIndex,
|
||||
delta: Pages,
|
||||
) -> i32 {
|
||||
let local_memory = *ctx.imported_memories.add(import_memory_index.index());
|
||||
let local_memory = *ctx
|
||||
.internal
|
||||
.imported_memories
|
||||
.add(import_memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut StaticMemory;
|
||||
|
||||
match (*memory).grow(delta, &mut *local_memory) {
|
||||
@ -82,7 +85,10 @@ pub unsafe extern "C" fn imported_static_memory_size(
|
||||
ctx: &vm::Ctx,
|
||||
import_memory_index: ImportedMemoryIndex,
|
||||
) -> Pages {
|
||||
let local_memory = *ctx.imported_memories.add(import_memory_index.index());
|
||||
let local_memory = *ctx
|
||||
.internal
|
||||
.imported_memories
|
||||
.add(import_memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut StaticMemory;
|
||||
|
||||
(*memory).size()
|
||||
@ -93,7 +99,7 @@ pub unsafe extern "C" fn imported_dynamic_memory_grow(
|
||||
memory_index: ImportedMemoryIndex,
|
||||
delta: Pages,
|
||||
) -> i32 {
|
||||
let local_memory = *ctx.imported_memories.add(memory_index.index());
|
||||
let local_memory = *ctx.internal.imported_memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut DynamicMemory;
|
||||
|
||||
match (*memory).grow(delta, &mut *local_memory) {
|
||||
@ -106,7 +112,7 @@ pub unsafe extern "C" fn imported_dynamic_memory_size(
|
||||
ctx: &vm::Ctx,
|
||||
memory_index: ImportedMemoryIndex,
|
||||
) -> Pages {
|
||||
let local_memory = *ctx.imported_memories.add(memory_index.index());
|
||||
let local_memory = *ctx.internal.imported_memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut DynamicMemory;
|
||||
|
||||
(*memory).size()
|
||||
|
Reference in New Issue
Block a user