diff --git a/CHANGELOG.md b/CHANGELOG.md index d0da24a7afc..d71b7046ec3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#942](https://github.com/wasmerio/wasmer/pull/942) Deny missing docs in runtime core and add missing docs - [#936](https://github.com/wasmerio/wasmer/pull/939) Fix bug causing attempts to append to files with WASI to delete the contents of the file - [#940](https://github.com/wasmerio/wasmer/pull/940) Update supported Rust version to 1.38+ - [#921](https://github.com/wasmerio/wasmer/pull/921) In LLVM backend, annotate all memory accesses with TBAA metadata. diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index ff21e9195f1..09c5743b993 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -17,6 +17,7 @@ use crate::{ }; use std::{fmt::Debug, slice}; +/// Size of the array for internal instance usage pub const INTERNALS_SIZE: usize = 256; pub(crate) struct Internals(pub(crate) [u64; INTERNALS_SIZE]); @@ -472,6 +473,8 @@ impl LocalBacking { } } +/// The `ImportBacking` stores references to the imported resources of an Instance. This includes +/// imported memories, tables, globals and functions. #[derive(Debug)] pub struct ImportBacking { pub(crate) memories: BoxedMap, @@ -488,6 +491,7 @@ pub struct ImportBacking { unsafe impl Send for ImportBacking {} impl ImportBacking { + /// Creates a new `ImportBacking` from the given `ModuleInner`, `ImportObject`, and `Ctx`. pub fn new( module: &ModuleInner, imports: &ImportObject, @@ -536,6 +540,7 @@ impl ImportBacking { } } + /// Gets a `ImportedFunc` from the given `ImportedFuncIndex`. pub fn imported_func(&self, index: ImportedFuncIndex) -> vm::ImportedFunc { self.vm_functions[index].clone() } diff --git a/lib/runtime-core/src/cache.rs b/lib/runtime-core/src/cache.rs index 89bbaf79665..e924cd9f5dd 100644 --- a/lib/runtime-core/src/cache.rs +++ b/lib/runtime-core/src/cache.rs @@ -1,3 +1,7 @@ +//! The cache module provides the common data structures used by compiler backends to allow +//! serializing compiled wasm code to a binary format. The binary format can be persisted, +//! and loaded to allow skipping compilation and fast startup. + use crate::{ backend::Backend, module::{Module, ModuleInfo}, @@ -6,20 +10,31 @@ use crate::{ use blake2b_simd::blake2bp; use std::{fmt, io, mem, slice}; +/// Indicates the invalid type of invalid cache file #[derive(Debug)] pub enum InvalidFileType { + /// Given cache header slice does not match the expected size of an `ArtifactHeader` InvalidSize, + /// Given cache header slice does not contain the expected magic bytes InvalidMagic, } +/// Kinds of caching errors #[derive(Debug)] pub enum Error { + /// An IO error while reading/writing a cache binary. IoError(io::Error), + /// An error deserializing bytes into a cache data structure. DeserializeError(String), + /// An error serializing bytes from a cache data structure. SerializeError(String), + /// An undefined caching error with a message. Unknown(String), + /// An invalid cache binary given. InvalidFile(InvalidFileType), + /// The cached binary has been invalidated. InvalidatedCache, + /// The current backend does not support caching. UnsupportedBackend(Backend), } @@ -164,6 +179,8 @@ struct ArtifactInner { compiled_code: Memory, } +/// Artifact are produced by caching, are serialized/deserialized to binaries, and contain +/// module info, backend metadata, and compiled code. pub struct Artifact { inner: ArtifactInner, } @@ -183,6 +200,7 @@ impl Artifact { } } + /// Deserializes an `Artifact` from the given byte slice. pub fn deserialize(bytes: &[u8]) -> Result { let (_, body_slice) = ArtifactHeader::read_from_slice(bytes)?; @@ -192,6 +210,7 @@ impl Artifact { Ok(Artifact { inner }) } + /// A reference to the `Artifact`'s stored `ModuleInfo` pub fn info(&self) -> &ModuleInfo { &self.inner.info } @@ -205,6 +224,7 @@ impl Artifact { ) } + /// Serializes the `Artifact` into a vector of bytes pub fn serialize(&self) -> Result, Error> { let cache_header = ArtifactHeader { magic: WASMER_CACHE_MAGIC, @@ -230,7 +250,9 @@ impl Artifact { /// /// The `wasmer-runtime` supplies a naive `FileSystemCache` api. pub trait Cache { + /// Error type to return when load error occurs type LoadError: fmt::Debug; + /// Error type to return when store error occurs type StoreError: fmt::Debug; /// loads a module using the default `Backend` @@ -238,6 +260,7 @@ pub trait Cache { /// loads a cached module using a specific `Backend` fn load_with_backend(&self, key: WasmHash, backend: Backend) -> Result; + /// Store a module into the cache with the given key fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::StoreError>; } diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 3db1b374368..e96f419e6b4 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -1,3 +1,5 @@ +//! The codegen module provides common functions and data structures used by multiple backends +//! during the code generation process. use crate::{ backend::RunnableModule, backend::{Backend, CacheGen, Compiler, CompilerConfig, Features, Token}, @@ -17,22 +19,35 @@ use std::sync::{Arc, RwLock}; use wasmparser::{self, WasmDecoder}; use wasmparser::{Operator, Type as WpType}; +/// A type that defines a function pointer, which is called when breakpoints occur. pub type BreakpointHandler = Box Result<(), Box> + Send + Sync + 'static>; + +/// Maps instruction pointers to their breakpoint handlers. pub type BreakpointMap = Arc>; +/// An event generated during parsing of a wasm binary #[derive(Debug)] pub enum Event<'a, 'b> { + /// An internal event created by the parser used to provide hooks during code generation. Internal(InternalEvent), + /// An event generated by parsing a wasm operator Wasm(&'b Operator<'a>), + /// An event generated by parsing a wasm operator that contains an owned `Operator` WasmOwned(Operator<'a>), } +/// Kinds of `InternalEvent`s created during parsing. pub enum InternalEvent { + /// A function parse is about to begin. FunctionBegin(u32), + /// A function parsing has just completed. FunctionEnd, + /// A breakpoint emitted during parsing. Breakpoint(BreakpointHandler), + /// Indicates setting an internal field. SetInternal(u32), + /// Indicates getting an internal field. GetInternal(u32), } @@ -48,10 +63,13 @@ impl fmt::Debug for InternalEvent { } } +/// Information for a breakpoint pub struct BreakpointInfo<'a> { + /// Fault. pub fault: Option<&'a dyn Any>, } +/// A trait that represents the functions needed to be implemented to generate code for a module. pub trait ModuleCodeGenerator, RM: RunnableModule, E: Debug> { /// Creates a new module code generator. fn new() -> Self; @@ -65,7 +83,7 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, } /// Adds an import function. fn feed_import_function(&mut self) -> Result<(), E>; - + /// Sets the signatures. fn feed_signatures(&mut self, signatures: Map) -> Result<(), E>; /// Sets function signatures. fn feed_function_signatures(&mut self, assoc: Map) -> Result<(), E>; @@ -80,6 +98,8 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, unsafe fn from_cache(cache: Artifact, _: Token) -> Result; } +/// A streaming compiler which is designed to generated code for a module based on a stream +/// of wasm parser events. pub struct StreamingCompiler< MCG: ModuleCodeGenerator, FCG: FunctionCodeGenerator, @@ -94,6 +114,7 @@ pub struct StreamingCompiler< _phantom_e: PhantomData, } +/// A simple generator for a `StreamingCompiler`. pub struct SimpleStreamingCompilerGen< MCG: ModuleCodeGenerator, FCG: FunctionCodeGenerator, @@ -113,6 +134,7 @@ impl< E: Debug, > SimpleStreamingCompilerGen { + /// Create a new `StreamingCompiler`. pub fn new() -> StreamingCompiler MiddlewareChain> { StreamingCompiler::new(|| MiddlewareChain::new()) } @@ -126,6 +148,7 @@ impl< CGEN: Fn() -> MiddlewareChain, > StreamingCompiler { + /// Create a new `StreamingCompiler` with the given `MiddlewareChain`. pub fn new(chain_gen: CGEN) -> Self { Self { middleware_chain_generator: chain_gen, @@ -137,6 +160,7 @@ impl< } } +/// Create a new `ValidatingParserConfig` with the given features. pub fn validating_parser_config(features: &Features) -> wasmparser::ValidatingParserConfig { wasmparser::ValidatingParserConfig { operator_config: wasmparser::OperatorValidatorConfig { @@ -220,29 +244,35 @@ fn requires_pre_validation(backend: Backend) -> bool { } } +/// A sink for parse events. pub struct EventSink<'a, 'b> { buffer: SmallVec<[Event<'a, 'b>; 2]>, } impl<'a, 'b> EventSink<'a, 'b> { + /// Push a new `Event` to this sink. pub fn push(&mut self, ev: Event<'a, 'b>) { self.buffer.push(ev); } } +/// A container for a chain of middlewares. pub struct MiddlewareChain { chain: Vec>, } impl MiddlewareChain { + /// Create a new empty `MiddlewareChain`. pub fn new() -> MiddlewareChain { MiddlewareChain { chain: vec![] } } + /// Push a new `FunctionMiddleware` to this `MiddlewareChain`. pub fn push(&mut self, m: M) { self.chain.push(Box::new(m)); } + /// Run this chain with the provided function code generator, event and module info. pub(crate) fn run>( &mut self, fcg: Option<&mut FCG>, @@ -270,8 +300,11 @@ impl MiddlewareChain { } } +/// A trait that represents the signature required to implement middleware for a function. pub trait FunctionMiddleware { + /// The error type for this middleware's functions. type Error: Debug; + /// Processes the given event, module info and sink. fn feed_event<'a, 'b: 'a>( &mut self, op: Event<'a, 'b>, diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index 9aac82fabfb..f2fcf7ebd32 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,13 +1,28 @@ +//! The error module contains the data structures and helper functions used to implement errors that +//! are produced and returned from the wasmer runtime core. use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type}; use core::borrow::Borrow; use std::any::Any; +/// Aliases the standard `Result` type as `Result` within this module. pub type Result = std::result::Result; +/// Result of an attempt to compile the provided WebAssembly module into a `Module`. +/// Aliases the standard `Result` with `CompileError` as the default error type. pub type CompileResult = std::result::Result; +/// Result of an attempt to link the provided WebAssembly instance. +/// Aliases the standard `Result` with `Vec` as the default error type. pub type LinkResult = std::result::Result>; +/// Result of an attempt to run the provided WebAssembly instance. +/// Aliases the standard `Result` with `RuntimeError` as the default error type. pub type RuntimeResult = std::result::Result; +/// Result of an attempt to call the provided WebAssembly instance. +/// Aliases the standard `Result` with `CallError` as the default error type. pub type CallResult = std::result::Result; +/// Result of an attempt to resolve a WebAssembly function by name. +/// Aliases the standard `Result` with `ResolveError` as the default error type. pub type ResolveResult = std::result::Result; +/// Result of an attempt to parse bytes into a WebAssembly module. +/// Aliases the standard `Result` with `ParseError` as the default error type. pub type ParseResult = std::result::Result; /// This is returned when the chosen compiler is unable to @@ -17,8 +32,16 @@ pub type ParseResult = std::result::Result; /// Comparing two `CompileError`s always evaluates to false. #[derive(Debug, Clone)] pub enum CompileError { - ValidationError { msg: String }, - InternalError { msg: String }, + /// A validation error containing an error message. + ValidationError { + /// An error message. + msg: String, + }, + /// A internal error containing an error message. + InternalError { + /// An error message. + msg: String, + }, } impl PartialEq for CompileError { @@ -46,41 +69,71 @@ impl std::error::Error for CompileError {} /// Comparing two `LinkError`s always evaluates to false. #[derive(Debug, Clone)] pub enum LinkError { + /// The type of the provided import does not match the expected type. IncorrectImportType { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: String, + /// Found. found: String, }, + /// The signature of the provided import does not match the expected signature. IncorrectImportSignature { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: FuncSig, + /// Found. found: FuncSig, }, + /// An expected import was not provided. ImportNotFound { + /// Namespace. namespace: String, + /// Name. name: String, }, + /// The memory descriptor provided does not match the expected descriptor. IncorrectMemoryDescriptor { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: MemoryDescriptor, + /// Found. found: MemoryDescriptor, }, + /// The table descriptor provided does not match the expected descriptor. IncorrectTableDescriptor { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: TableDescriptor, + /// Found. found: TableDescriptor, }, + /// The global descriptor provided does not match the expected descriptor. IncorrectGlobalDescriptor { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: GlobalDescriptor, + /// Found. found: GlobalDescriptor, }, + /// A generic error with a message. Generic { + /// Error message. message: String, }, } @@ -126,8 +179,16 @@ impl std::error::Error for LinkError {} /// /// Comparing two `RuntimeError`s always evaluates to false. pub enum RuntimeError { - Trap { msg: Box }, - Error { data: Box }, + /// Trap. + Trap { + /// Trap message. + msg: Box, + }, + /// Error. + Error { + /// Error data. + data: Box, + }, } impl PartialEq for RuntimeError { @@ -169,9 +230,23 @@ impl std::error::Error for RuntimeError {} /// Comparing two `ResolveError`s always evaluates to false. #[derive(Debug, Clone)] pub enum ResolveError { - Signature { expected: FuncSig, found: Vec }, - ExportNotFound { name: String }, - ExportWrongType { name: String }, + /// Found signature did not match expected signature. + Signature { + /// Expected `FuncSig`. + expected: FuncSig, + /// Found type. + found: Vec, + }, + /// Export not found. + ExportNotFound { + /// Name. + name: String, + }, + /// Export found with the wrong type. + ExportWrongType { + /// Name. + name: String, + }, } impl PartialEq for ResolveError { @@ -213,7 +288,9 @@ impl std::error::Error for ResolveError {} /// /// Comparing two `CallError`s always evaluates to false. pub enum CallError { + /// An error occured resolving the functions name or types. Resolve(ResolveError), + /// A runtime error occurred during the function call. Runtime(RuntimeError), } @@ -247,8 +324,11 @@ impl std::error::Error for CallError {} /// like a `Memory` or a `Table`. #[derive(Debug, Clone)] pub enum CreationError { + /// Unable to create memory error. UnableToCreateMemory, + /// Unable to create table error. UnableToCreateTable, + /// Invalid descriptor error with message. InvalidDescriptor(String), } @@ -281,11 +361,17 @@ impl std::error::Error for CreationError {} /// Comparing two `Error`s always evaluates to false. #[derive(Debug)] pub enum Error { + /// Compile error. CompileError(CompileError), + /// Link errors. LinkError(Vec), + /// Runtime error. RuntimeError(RuntimeError), + /// Resolve error. ResolveError(ResolveError), + /// Call error. CallError(CallError), + /// Creation error. CreationError(CreationError), } @@ -368,13 +454,20 @@ impl std::fmt::Display for Error { impl std::error::Error for Error {} +/// An error occurred while growing a memory or table. #[derive(Debug)] pub enum GrowError { + /// Error growing memory. MemoryGrowError, + /// Error growing table. TableGrowError, + /// Max pages were exceeded. ExceededMaxPages(PageError), + /// Max pages for memory were exceeded. ExceededMaxPagesForMemory(usize, usize), + /// Error protecting memory. CouldNotProtectMemory(MemoryProtectionError), + /// Error creating memory. CouldNotCreateMemory(MemoryCreationError), } @@ -393,9 +486,11 @@ impl std::fmt::Display for GrowError { impl std::error::Error for GrowError {} +/// A kind of page error. #[derive(Debug)] pub enum PageError { // left, right, added + /// Max pages were exceeded error. ExceededMaxPages(usize, usize, usize), } @@ -414,9 +509,12 @@ impl Into for PageError { } } +/// Error occured while creating memory. #[derive(Debug)] pub enum MemoryCreationError { + /// Allocation of virtual memory failed error. VirtualMemoryAllocationFailed(usize, String), + /// Error creating memory from file. CouldNotCreateMemoryFromFile(std::io::Error), } @@ -446,8 +544,10 @@ impl From for MemoryCreationError { } } +/// Error protecting memory. #[derive(Debug)] pub enum MemoryProtectionError { + /// Protection failed error. ProtectionFailed(usize, usize, String), } @@ -470,8 +570,10 @@ impl Into for MemoryProtectionError { } } +/// Parse Error. #[derive(Debug)] pub enum ParseError { + /// Error reading binary. BinaryReadError, } diff --git a/lib/runtime-core/src/export.rs b/lib/runtime-core/src/export.rs index 7960d76e699..213ea06b82f 100644 --- a/lib/runtime-core/src/export.rs +++ b/lib/runtime-core/src/export.rs @@ -1,3 +1,6 @@ +//! The export module contains the implementation data structures and helper functions used to +//! manipulate and access a wasm module's exports including memories, tables, globals, and +//! functions. use crate::{ global::Global, instance::InstanceInner, memory::Memory, module::ExportIndex, module::ModuleInner, table::Table, types::FuncSig, vm, @@ -5,27 +8,39 @@ use crate::{ use indexmap::map::Iter as IndexMapIter; use std::sync::Arc; +/// A kind of Context. #[derive(Debug, Copy, Clone)] pub enum Context { + /// External context include a mutable pointer to `Ctx`. External(*mut vm::Ctx), + /// Internal context. Internal, } // Manually implemented because context contains a raw pointer to Ctx unsafe impl Send for Context {} +/// Kind of WebAssembly export. #[derive(Debug, Clone)] pub enum Export { + /// Function export. Function { + /// A pointer to a function. func: FuncPointer, + /// A kind of context. ctx: Context, + /// The signature of the function. signature: Arc, }, + /// Memory export. Memory(Memory), + /// Table export. Table(Table), + /// Global export. Global(Global), } +/// Const pointer to a `Func`. #[derive(Debug, Clone)] pub struct FuncPointer(*const vm::Func); @@ -45,6 +60,7 @@ impl FuncPointer { } } +/// An iterator to an instance's exports. pub struct ExportIter<'a> { inner: &'a InstanceInner, iter: IndexMapIter<'a, String, ExportIndex>, diff --git a/lib/runtime-core/src/fault.rs b/lib/runtime-core/src/fault.rs index 5aaf81af94a..a2dd8f5e1fb 100644 --- a/lib/runtime-core/src/fault.rs +++ b/lib/runtime-core/src/fault.rs @@ -1,4 +1,8 @@ +//! The fault module contains the implementation for handling breakpoints, traps, and signals +//! for wasm code. + pub mod raw { + //! The raw module contains required externed function interfaces for the fault module. use std::ffi::c_void; extern "C" { @@ -40,13 +44,19 @@ struct UnwindInfo { payload: Option>, // out } +/// A store for boundary register preservation. #[repr(packed)] #[derive(Default, Copy, Clone)] pub struct BoundaryRegisterPreservation { + /// R15. pub r15: u64, + /// R14. pub r14: u64, + /// R13. pub r13: u64, + /// R12. pub r12: u64, + /// RBX. pub rbx: u64, } @@ -58,6 +68,7 @@ thread_local! { static BOUNDARY_REGISTER_PRESERVATION: UnsafeCell = UnsafeCell::new(BoundaryRegisterPreservation::default()); } +/// Gets a mutable pointer to the `BoundaryRegisterPreservation`. #[no_mangle] pub unsafe extern "C" fn get_boundary_register_preservation() -> *mut BoundaryRegisterPreservation { BOUNDARY_REGISTER_PRESERVATION.with(|x| x.get()) @@ -89,10 +100,12 @@ lazy_static! { } static INTERRUPT_SIGNAL_DELIVERED: AtomicBool = AtomicBool::new(false); +/// Returns a boolean indicating if SIGINT triggered the fault. pub fn was_sigint_triggered_fault() -> bool { WAS_SIGINT_TRIGGERED.with(|x| x.get()) } +/// Runs a callback function with the given `Ctx`. pub unsafe fn with_ctx R>(ctx: *mut vm::Ctx, cb: F) -> R { let addr = CURRENT_CTX.with(|x| x.get()); let old = *addr; @@ -102,18 +115,22 @@ pub unsafe fn with_ctx R>(ctx: *mut vm::Ctx, cb: F) -> R { ret } +/// Pushes a new `CodeVersion` to the current code versions. pub fn push_code_version(version: CodeVersion) { CURRENT_CODE_VERSIONS.with(|x| x.borrow_mut().push(version)); } +/// Pops a `CodeVersion` from the current code versions. pub fn pop_code_version() -> Option { CURRENT_CODE_VERSIONS.with(|x| x.borrow_mut().pop()) } +/// Gets the wasm interrupt signal mem. pub unsafe fn get_wasm_interrupt_signal_mem() -> *mut u8 { INTERRUPT_SIGNAL_MEM.0 } +/// Sets the wasm interrupt on the given `Ctx`. pub unsafe fn set_wasm_interrupt_on_ctx(ctx: *mut vm::Ctx) { if mprotect( (&*ctx).internal.interrupt_signal_mem as _, @@ -125,6 +142,7 @@ pub unsafe fn set_wasm_interrupt_on_ctx(ctx: *mut vm::Ctx) { } } +/// Sets a wasm interrupt. pub unsafe fn set_wasm_interrupt() { let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0; if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_NONE) < 0 { @@ -132,6 +150,7 @@ pub unsafe fn set_wasm_interrupt() { } } +/// Clears the wasm interrupt. pub unsafe fn clear_wasm_interrupt() { let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0; if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_READ | PROT_WRITE) < 0 { @@ -139,6 +158,7 @@ pub unsafe fn clear_wasm_interrupt() { } } +/// Catches an unsafe unwind with the given functions and breakpoints. pub unsafe fn catch_unsafe_unwind R>( f: F, breakpoints: Option, @@ -164,6 +184,7 @@ pub unsafe fn catch_unsafe_unwind R>( } } +/// Begins an unsafe unwind. pub unsafe fn begin_unsafe_unwind(e: Box) -> ! { let unwind = UNWIND.with(|x| x.get()); let inner = (*unwind) @@ -181,6 +202,7 @@ unsafe fn with_breakpoint_map) -> R>(f: F) - f(inner.breakpoints.as_ref()) } +/// Allocates and runs with the given stack size and closure. pub fn allocate_and_run R>(size: usize, f: F) -> R { struct Context R, R> { f: Option, @@ -316,6 +338,7 @@ extern "C" fn sigint_handler( } } +/// Ensure the signal handler is installed. pub fn ensure_sighandler() { INSTALL_SIGHANDLER.call_once(|| unsafe { install_sighandler(); @@ -344,12 +367,17 @@ unsafe fn install_sighandler() { sigaction(SIGINT, &sa_interrupt).unwrap(); } +/// Info about the fault pub struct FaultInfo { + /// Faulting address. pub faulting_addr: *const c_void, + /// Instruction pointer. pub ip: *const c_void, + /// Known registers. pub known_registers: [Option; 32], } +/// Gets fault info for the given siginfo and context pointers. #[cfg(all(target_os = "linux", target_arch = "x86_64"))] pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> FaultInfo { use libc::{ @@ -421,6 +449,7 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> } } +/// Get fault info from siginfo and ucontext. #[cfg(all(target_os = "macos", target_arch = "x86_64"))] pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> FaultInfo { #[allow(dead_code)] diff --git a/lib/runtime-core/src/global.rs b/lib/runtime-core/src/global.rs index b59d1599b8e..be9eaf6ae91 100644 --- a/lib/runtime-core/src/global.rs +++ b/lib/runtime-core/src/global.rs @@ -1,3 +1,5 @@ +//! The global module contains the implementation data structures and helper functions used to +//! manipulate and access a wasm globals. use crate::{ export::Export, import::IsExport, @@ -9,6 +11,7 @@ use std::{ sync::{Arc, Mutex}, }; +/// Container with a descriptor and a reference to a global value. pub struct Global { desc: GlobalDescriptor, storage: Arc>, diff --git a/lib/runtime-core/src/import.rs b/lib/runtime-core/src/import.rs index 9d86205b156..284ff1725a5 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -1,3 +1,6 @@ +//! The import module contains the implementation data structures and helper functions used to +//! manipulate and access a wasm module's imports including memories, tables, globals, and +//! functions. use crate::export::Export; use std::collections::VecDeque; use std::collections::{hash_map::Entry, HashMap}; @@ -7,13 +10,20 @@ use std::{ sync::{Arc, Mutex}, }; +/// This trait represents objects that act as a namespace for imports. For example, an `Instance` +/// or `ImportObject` could be considered namespaces that could provide imports to an instance. pub trait LikeNamespace { + /// Gets an export by name. fn get_export(&self, name: &str) -> Option; + /// Gets all exports in the namespace. fn get_exports(&self) -> Vec<(String, Export)>; + /// Maybe insert an `Export` by name into the namespace. fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()>; } +/// A trait that represents `Export` values. pub trait IsExport { + /// Gets self as `Export`. fn to_export(&self) -> Export; } @@ -48,6 +58,8 @@ pub struct ImportObject { map: Arc>>>, pub(crate) state_creator: Option (*mut c_void, fn(*mut c_void)) + Send + Sync + 'static>>, + /// Allow missing functions to be generated and instantiation to continue when required + /// functions are not provided. pub allow_missing_functions: bool, } @@ -61,6 +73,7 @@ impl ImportObject { } } + /// Create a new `ImportObject` which generates data from the provided state creator. pub fn new_with_data(state_creator: F) -> Self where F: Fn() -> (*mut c_void, fn(*mut c_void)) + 'static + Send + Sync, @@ -145,6 +158,7 @@ impl ImportObject { .and_then(|ns| f(ns)) } + /// Create a clone ref of this namespace. pub fn clone_ref(&self) -> Self { Self { map: Arc::clone(&self.map), @@ -166,6 +180,7 @@ impl ImportObject { } } +/// Iterator for an `ImportObject`'s exports. pub struct ImportObjectIterator { elements: VecDeque<(String, String, Export)>, } @@ -204,17 +219,20 @@ impl Extend<(String, String, Export)> for ImportObject { } } +/// The top-level container for the two-level wasm imports pub struct Namespace { map: HashMap>, } impl Namespace { + /// Create a new empty `Namespace`. pub fn new() -> Self { Self { map: HashMap::new(), } } + /// Insert a new `Export` into the namespace with the given name. pub fn insert(&mut self, name: S, export: E) -> Option> where S: Into, @@ -223,6 +241,7 @@ impl Namespace { self.map.insert(name.into(), Box::new(export)) } + /// Returns true if the `Namespace` contains the given name. pub fn contains_key(&mut self, key: S) -> bool where S: Into, diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 85e9f8f768b..8a8ea457d57 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -1,3 +1,5 @@ +//! The instance module contains the implementation data structures and helper functions used to +//! manipulate and access wasm instances. use crate::{ backend::RunnableModule, backing::{ImportBacking, LocalBacking}, @@ -48,6 +50,7 @@ impl Drop for InstanceInner { /// /// [`ImportObject`]: struct.ImportObject.html pub struct Instance { + /// Reference to the module used to instantiate this instance. pub module: Arc, inner: Pin>, #[allow(dead_code)] @@ -137,6 +140,7 @@ impl Instance { Ok(instance) } + /// Load an `Instance` using the given loader. pub fn load(&self, loader: T) -> ::std::result::Result { loader.load(&*self.module.runnable_module, &self.module.info, unsafe { &*self.inner.vmctx @@ -230,6 +234,7 @@ impl Instance { } } + /// Resolve a function by name. pub fn resolve_func(&self, name: &str) -> ResolveResult { let export_index = self.module @@ -381,10 +386,12 @@ impl Instance { Module::new(Arc::clone(&self.module)) } + /// Get the value of an internal field pub fn get_internal(&self, field: &InternalField) -> u64 { self.inner.backing.internals.0[field.index()] } + /// Set the value of an internal field. pub fn set_internal(&mut self, field: &InternalField, value: u64) { self.inner.backing.internals.0[field.index()] = value; } @@ -774,10 +781,12 @@ impl<'a> DynFunc<'a> { Ok(results) } + /// Gets the signature of this `Dynfunc`. pub fn signature(&self) -> &FuncSig { &*self.signature } + /// Gets a const pointer to the function represent by this `DynFunc`. pub fn raw(&self) -> *const vm::Func { match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 26a76f7dbf6..d8b558ebc07 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -1,5 +1,20 @@ +//! Wasmer Runtime Core Library +//! +//! The runtime core library provides common data structures which are shared by compiler backends +//! to implement a Web Assembly runtime. +//! +//! The runtime core also provides an API for users who use wasmer as an embedded wasm runtime which +//! allows operations like compiling, instantiating, providing imports, access exports, memories, +//! and tables for example. +//! +//! The runtime core library is recommended to be used by only power users who wish to customize the +//! wasmer runtime. Most wasmer users should prefer the API which is re-exported by the wasmer +//! runtime library which provides common defaults and a friendly API. +//! + #![deny( dead_code, + missing_docs, nonstandard_style, unused_imports, unused_mut, @@ -77,6 +92,9 @@ pub use wasmparser; use self::cache::{Artifact, Error as CacheError}; pub mod prelude { + //! The prelude module is a helper module used to bring commonly used runtime core imports into + //! scope. + pub use crate::import::{ImportObject, Namespace}; pub use crate::types::{ FuncIndex, GlobalIndex, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, @@ -158,6 +176,7 @@ pub fn validate_and_report_errors_with_features( } } +/// Creates a new module from the given cache `Artifact` for the specified compiler backend pub unsafe fn load_cache_with( cache: Artifact, compiler: &dyn backend::Compiler, diff --git a/lib/runtime-core/src/loader.rs b/lib/runtime-core/src/loader.rs index f50d3a7a032..84d2bbbfb83 100644 --- a/lib/runtime-core/src/loader.rs +++ b/lib/runtime-core/src/loader.rs @@ -1,3 +1,4 @@ +//! The loader module functions are used to load an instance. use crate::{backend::RunnableModule, module::ModuleInfo, types::Type, types::Value, vm::Ctx}; #[cfg(unix)] use libc::{mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE}; @@ -6,10 +7,14 @@ use std::{ ops::{Deref, DerefMut}, }; +/// The loader trait represents the functions used to load an instance. pub trait Loader { + /// The type of `Instance` for the loader. type Instance: Instance; + /// The error type returned by the loader. type Error: Debug; + /// Loads the given module and context into an instance. fn load( &self, rm: &dyn RunnableModule, @@ -18,18 +23,23 @@ pub trait Loader { ) -> Result; } +/// This trait represents an instance used by the loader. pub trait Instance { + /// The error type returned by this instance. type Error: Debug; + /// Call a function by id with the given args. fn call(&mut self, id: usize, args: &[Value]) -> Result; + /// Read memory at the given offset and length. fn read_memory(&mut self, _offset: u32, _len: u32) -> Result, Self::Error> { unimplemented!("Instance::read_memory") } - + /// Write memory at the given offset and length. fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> { unimplemented!("Instance::write_memory") } } +/// A local implementation for `Loader`. pub struct LocalLoader; impl Loader for LocalLoader { @@ -54,6 +64,7 @@ impl Loader for LocalLoader { } } +/// A local instance. pub struct LocalInstance { code: CodeMemory, offsets: Vec, @@ -111,6 +122,7 @@ impl Instance for LocalInstance { } } +/// A pointer to code in memory. pub struct CodeMemory { ptr: *mut u8, size: usize, @@ -121,14 +133,17 @@ unsafe impl Sync for CodeMemory {} #[cfg(not(unix))] impl CodeMemory { + /// Creates a new code memory with the given size. pub fn new(_size: usize) -> CodeMemory { unimplemented!("CodeMemory::new"); } + /// Makes this code memory executable. pub fn make_executable(&self) { unimplemented!("CodeMemory::make_executable"); } + /// Makes this code memory writable. pub fn make_writable(&self) { unimplemented!("CodeMemory::make_writable"); } @@ -136,6 +151,7 @@ impl CodeMemory { #[cfg(unix)] impl CodeMemory { + /// Creates a new code memory with the given size. pub fn new(size: usize) -> CodeMemory { if size == 0 { return CodeMemory { @@ -167,12 +183,14 @@ impl CodeMemory { } } + /// Makes this code memory executable. pub fn make_executable(&self) { if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 { panic!("cannot set code memory to executable"); } } + /// Makes this code memory writable. pub fn make_writable(&self) { if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE) } != 0 { panic!("cannot set code memory to writable"); diff --git a/lib/runtime-core/src/macros.rs b/lib/runtime-core/src/macros.rs index 6dbb93ca09c..a9dc6721cae 100644 --- a/lib/runtime-core/src/macros.rs +++ b/lib/runtime-core/src/macros.rs @@ -1,3 +1,5 @@ +/// Prints a log message with args, similar to println, when the debug feature is enabled. +/// If the debug feature is disabled, arguments are not evaluated or printed. #[macro_export] #[cfg(feature = "debug")] macro_rules! debug { @@ -11,6 +13,8 @@ macro_rules! debug { }, line!(), $($arg)*)); } +/// Prints a log message with args, similar to println, when the debug feature is enabled. +/// If the debug feature is disabled, arguments are not evaluated or printed. #[macro_export] #[cfg(not(feature = "debug"))] macro_rules! debug { @@ -18,6 +22,8 @@ macro_rules! debug { ($fmt:expr, $($arg:tt)*) => {}; } +/// Prints a log message with args, similar to println, when the trace feature is enabled. +/// If the trace feature is disabled, arguments are not evaluated or printed. #[macro_export] #[cfg(feature = "trace")] macro_rules! trace { @@ -29,6 +35,8 @@ macro_rules! trace { } } +/// Prints a log message with args, similar to println, when the trace feature is enabled. +/// If the trace feature is disabled, arguments are not evaluated or printed. #[macro_export] #[cfg(not(feature = "trace"))] macro_rules! trace { @@ -36,6 +44,7 @@ macro_rules! trace { ($fmt:expr, $($arg:tt)*) => {}; } +/// Helper macro to create a new `Func` object using the provided function pointer. #[macro_export] macro_rules! func { ($func:path) => {{ diff --git a/lib/runtime-core/src/memory/dynamic.rs b/lib/runtime-core/src/memory/dynamic.rs index 332a37acb3d..885c47dbee6 100644 --- a/lib/runtime-core/src/memory/dynamic.rs +++ b/lib/runtime-core/src/memory/dynamic.rs @@ -62,10 +62,12 @@ impl DynamicMemory { Ok(storage) } + /// The size of this memory in `Pages`. pub fn size(&self) -> Pages { self.current } + /// Try to grow self by the given number of delta pages. pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Result { if delta == Pages(0) { return Ok(self.current); @@ -104,10 +106,12 @@ impl DynamicMemory { Ok(old_pages) } + /// Get this memory represented as a slice of bytes. pub fn as_slice(&self) -> &[u8] { unsafe { &self.memory.as_slice()[0..self.current.bytes().0] } } + /// Get this memory represented as a mutable slice of bytes pub fn as_slice_mut(&mut self) -> &mut [u8] { unsafe { &mut self.memory.as_slice_mut()[0..self.current.bytes().0] } } diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 60fb87c4dd3..75a02e2276a 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -1,3 +1,5 @@ +//! The memory module contains the implementation data structures and helper functions used to +//! manipulate and access wasm memory. use crate::{ error::{CreationError, GrowError}, export::Export, @@ -170,10 +172,14 @@ impl fmt::Debug for Memory { } } +/// A kind a memory. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MemoryType { + /// A dynamic memory. Dynamic, + /// A static memory. Static, + /// A shared static memory. SharedStatic, } @@ -200,6 +206,7 @@ enum UnsharedMemoryStorage { Static(Box), } +/// A reference to an unshared memory. pub struct UnsharedMemory { internal: Arc, } @@ -214,6 +221,7 @@ struct UnsharedMemoryInternal { unsafe impl Sync for UnsharedMemoryInternal {} impl UnsharedMemory { + /// Create a new `UnsharedMemory` from the given memory descriptor. pub fn new(desc: MemoryDescriptor) -> Result { let mut local = vm::LocalMemory { base: std::ptr::null_mut(), @@ -243,6 +251,7 @@ impl UnsharedMemory { }) } + /// Try to grow this memory by the given number of delta pages. pub fn grow(&self, delta: Pages) -> Result { let mut storage = self.internal.storage.lock().unwrap(); @@ -260,6 +269,7 @@ impl UnsharedMemory { pages } + /// Size of this memory in pages. pub fn size(&self) -> Pages { let storage = self.internal.storage.lock().unwrap(); @@ -282,10 +292,12 @@ impl Clone for UnsharedMemory { } } +/// A reference to a shared memory. pub struct SharedMemory { internal: Arc, } +/// Data structure for a shared internal memory. pub struct SharedMemoryInternal { memory: StdMutex>, local: Cell, @@ -315,6 +327,7 @@ impl SharedMemory { }) } + /// Try to grow this memory by the given number of delta pages. pub fn grow(&self, delta: Pages) -> Result { let _guard = self.internal.lock.lock(); let mut local = self.internal.local.get(); @@ -323,12 +336,14 @@ impl SharedMemory { pages } + /// Size of this memory in pages. pub fn size(&self) -> Pages { let _guard = self.internal.lock.lock(); let memory = self.internal.memory.lock().unwrap(); memory.size() } + /// Gets a mutable pointer to the `LocalMemory`. // This function is scary, because the mutex is not locked here pub(crate) fn vm_local_memory(&self) -> *mut vm::LocalMemory { self.internal.local.as_ptr() diff --git a/lib/runtime-core/src/memory/ptr.rs b/lib/runtime-core/src/memory/ptr.rs index abed45bfb67..5e31627c20f 100644 --- a/lib/runtime-core/src/memory/ptr.rs +++ b/lib/runtime-core/src/memory/ptr.rs @@ -12,9 +12,12 @@ use crate::{ }; use std::{cell::Cell, fmt, marker::PhantomData, mem}; +/// Array. pub struct Array; +/// Item. pub struct Item; +/// A pointer to a Wasm item. #[repr(transparent)] pub struct WasmPtr { offset: u32, @@ -22,6 +25,7 @@ pub struct WasmPtr { } impl WasmPtr { + /// Create a new `WasmPtr` at the given offset. #[inline] pub fn new(offset: u32) -> Self { Self { @@ -30,6 +34,7 @@ impl WasmPtr { } } + /// Get the offset for this `WasmPtr`. #[inline] pub fn offset(self) -> u32 { self.offset @@ -44,6 +49,7 @@ fn align_pointer(ptr: usize, align: usize) -> usize { } impl WasmPtr { + /// Dereference this `WasmPtr`. #[inline] pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell> { if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { @@ -58,6 +64,7 @@ impl WasmPtr { } } + /// Mutable dereference this `WasmPtr`. #[inline] pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell> { if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { @@ -72,6 +79,7 @@ impl WasmPtr { } impl WasmPtr { + /// Dereference this `WasmPtr`. #[inline] pub fn deref<'a>(self, memory: &'a Memory, index: u32, length: u32) -> Option<&'a [Cell]> { // gets the size of the item in the array with padding added such that @@ -94,6 +102,7 @@ impl WasmPtr { } } + /// Mutable dereference this `WasmPtr`. #[inline] pub unsafe fn deref_mut<'a>( self, @@ -119,6 +128,7 @@ impl WasmPtr { Some(cell_ptrs) } + /// Get a UTF-8 string representation of this `WasmPtr` with the given length. pub fn get_utf8_string<'a>(self, memory: &'a Memory, str_len: u32) -> Option<&'a str> { if self.offset as usize + str_len as usize > memory.size().bytes().0 { return None; diff --git a/lib/runtime-core/src/memory/static_.rs b/lib/runtime-core/src/memory/static_.rs index 957e53eafd7..c6355b2a2ee 100644 --- a/lib/runtime-core/src/memory/static_.rs +++ b/lib/runtime-core/src/memory/static_.rs @@ -56,10 +56,12 @@ impl StaticMemory { Ok(storage) } + /// The size of this memory in `Pages`. pub fn size(&self) -> Pages { self.current } + /// Try to grow this memory by the given number of delta pages. pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Result { if delta == Pages(0) { return Ok(self.current); @@ -94,10 +96,12 @@ impl StaticMemory { Ok(old_pages) } + /// Get this memory represented as a slice of bytes. pub fn as_slice(&self) -> &[u8] { unsafe { &self.memory.as_slice()[0..self.current.bytes().0] } } + /// Get this memory represented as a mutable slice of bytes. pub fn as_slice_mut(&mut self) -> &mut [u8] { unsafe { &mut self.memory.as_slice_mut()[0..self.current.bytes().0] } } diff --git a/lib/runtime-core/src/memory/view.rs b/lib/runtime-core/src/memory/view.rs index 4dbaa5bd52d..762e1a614b9 100644 --- a/lib/runtime-core/src/memory/view.rs +++ b/lib/runtime-core/src/memory/view.rs @@ -39,12 +39,16 @@ impl Atomic for f64 { type Output = AtomicU64; } +/// A trait that represants an atomic type. pub trait Atomicity {} +/// Atomically. pub struct Atomically; impl Atomicity for Atomically {} +/// Non-atomically. pub struct NonAtomically; impl Atomicity for NonAtomically {} +/// A view into a memory. pub struct MemoryView<'a, T: 'a, A = NonAtomically> { ptr: *mut T, length: usize, @@ -65,6 +69,7 @@ where } impl<'a, T: Atomic> MemoryView<'a, T> { + /// Get atomic access to a memory view. pub fn atomically(&self) -> MemoryView<'a, T::Output, Atomically> { MemoryView { ptr: self.ptr as *mut T::Output, diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 1bf914a463d..eb87b85d3b9 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -1,3 +1,5 @@ +//! The module module contains the implementation data structures and helper functions used to +//! manipulate and access wasm modules. use crate::{ backend::{Backend, RunnableModule}, cache::{Artifact, Error as CacheError}, @@ -27,40 +29,59 @@ pub struct ModuleInner { pub info: ModuleInfo, } +/// Container for module data including memories, globals, tables, imports, and exports. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ModuleInfo { + /// Map of memory index to memory descriptors. // This are strictly local and the typesystem ensures that. pub memories: Map, + /// Map of global index to global descriptors. pub globals: Map, + /// Map of table index to table descriptors. pub tables: Map, + /// Map of imported function index to import name. // These are strictly imported and the typesystem ensures that. pub imported_functions: Map, + /// Map of imported memory index to import name and memory descriptor. pub imported_memories: Map, + /// Map of imported table index to import name and table descriptor. pub imported_tables: Map, + /// Map of imported global index to import name and global descriptor. pub imported_globals: Map, + /// Map of string to export index. pub exports: IndexMap, + /// Vector of data initializers. pub data_initializers: Vec, + /// Vector of table initializers. pub elem_initializers: Vec, + /// Index of optional start function. pub start_func: Option, + /// Map function index to signature index. pub func_assoc: Map, + /// Map signature index to function signature. pub signatures: Map, + /// Backend. pub backend: Backend, + /// Table of namespace indexes. pub namespace_table: StringTable, + /// Table of name indexes. pub name_table: StringTable, - /// Symbol information from emscripten + /// Symbol information from emscripten. pub em_symbol_map: Option>, + /// Custom sections. pub custom_sections: HashMap>, } impl ModuleInfo { + /// Creates custom section info from the given wasm file. pub fn import_custom_sections(&mut self, wasm: &[u8]) -> crate::error::ParseResult<()> { let mut parser = wasmparser::ModuleReader::new(wasm)?; while !parser.eof() { @@ -120,6 +141,7 @@ impl Module { Instance::new(Arc::clone(&self.inner), import_object) } + /// Create a cache artifact from this module. pub fn cache(&self) -> Result { let (backend_metadata, code) = self.inner.cache_gen.generate_cache()?; Ok(Artifact::from_parts( @@ -129,6 +151,7 @@ impl Module { )) } + /// Get the module data for this module. pub fn info(&self) -> &ModuleInfo { &self.inner.info } @@ -151,11 +174,16 @@ pub struct ImportName { pub name_index: NameIndex, } +/// Kinds of export indexes. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum ExportIndex { + /// Function export index. Func(FuncIndex), + /// Memory export index. Memory(MemoryIndex), + /// Global export index. Global(GlobalIndex), + /// Table export index. Table(TableIndex), } @@ -182,6 +210,7 @@ pub struct TableInitializer { pub elements: Vec, } +/// String table builder. pub struct StringTableBuilder { map: IndexMap, buffer: String, @@ -189,6 +218,7 @@ pub struct StringTableBuilder { } impl StringTableBuilder { + /// Creates a new `StringTableBuilder`. pub fn new() -> Self { Self { map: IndexMap::new(), @@ -197,6 +227,7 @@ impl StringTableBuilder { } } + /// Register a new string into table. pub fn register(&mut self, s: S) -> K where S: Into + AsRef, @@ -219,6 +250,7 @@ impl StringTableBuilder { } } + /// Finish building the `StringTable`. pub fn finish(self) -> StringTable { let table = self .map @@ -233,6 +265,7 @@ impl StringTableBuilder { } } +/// A map of index to string. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct StringTable { table: Map, @@ -240,6 +273,7 @@ pub struct StringTable { } impl StringTable { + /// Creates a `StringTable`. pub fn new() -> Self { Self { table: Map::new(), @@ -247,6 +281,7 @@ impl StringTable { } } + /// Gets a reference to a string at the given index. pub fn get(&self, index: K) -> &str { let (offset, length) = self.table[index]; let offset = offset as usize; @@ -256,6 +291,7 @@ impl StringTable { } } +/// Namespace index. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NamespaceIndex(u32); @@ -271,6 +307,7 @@ impl TypedIndex for NamespaceIndex { } } +/// Name index. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NameIndex(u32); diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index e3e2c162e1d..ab6c79cbc0d 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -1,3 +1,6 @@ +//! The parse module contains common data structures and functions using to parse wasm files into +//! runtime data structures. + use crate::codegen::*; use crate::{ backend::{Backend, CompilerConfig, RunnableModule}, @@ -22,9 +25,12 @@ use wasmparser::{ WasmDecoder, }; +/// Kind of load error. #[derive(Debug)] pub enum LoadError { + /// Parse error. Parse(BinaryReaderError), + /// Code generation error. Codegen(String), } @@ -42,6 +48,8 @@ impl From for LoadError { } } +/// Read wasm binary into module data using the given backend, module code generator, middlewares, +/// and compiler configuration. pub fn read_module< MCG: ModuleCodeGenerator, FCG: FunctionCodeGenerator, @@ -394,6 +402,7 @@ pub fn read_module< Ok(info) } +/// Convert given `WpType` to `Type`. pub fn wp_type_to_type(ty: WpType) -> Result { match ty { WpType::I32 => Ok(Type::I32), @@ -410,6 +419,7 @@ pub fn wp_type_to_type(ty: WpType) -> Result { } } +/// Convert given `Type` to `WpType`. pub fn type_to_wp_type(ty: Type) -> WpType { match ty { Type::I32 => WpType::I32, diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index 6025abe609f..317976db500 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -1,119 +1,191 @@ +//! The state module is used to track state of a running web assembly instances so that +//! state could read or updated at runtime. Use cases include generating stack traces, switching +//! generated code from one tier to another, or serializing state of a running instace. + use std::collections::BTreeMap; use std::ops::Bound::{Included, Unbounded}; +/// An index to a register #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct RegisterIndex(pub usize); +/// A kind of wasm or constant value #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum WasmAbstractValue { + /// A wasm runtime value Runtime, + /// A wasm constant value Const(u64), } +/// A container for the state of a running wasm instance. #[derive(Clone, Debug)] pub struct MachineState { + /// Stack values. pub stack_values: Vec, + /// Register values. pub register_values: Vec, - + /// Previous frame. pub prev_frame: BTreeMap, - + /// Wasm stack. pub wasm_stack: Vec, + /// Private depth of the wasm stack. pub wasm_stack_private_depth: usize, - + /// Wasm instruction offset. pub wasm_inst_offset: usize, } +/// A diff of two `MachineState`s. #[derive(Clone, Debug, Default)] pub struct MachineStateDiff { + /// Last. pub last: Option, + /// Stack push. pub stack_push: Vec, + /// Stack pop. pub stack_pop: usize, + + /// Register diff. pub reg_diff: Vec<(RegisterIndex, MachineValue)>, + /// Previous frame diff. pub prev_frame_diff: BTreeMap>, // None for removal + /// Wasm stack push. pub wasm_stack_push: Vec, + /// Wasm stack pop. pub wasm_stack_pop: usize, + /// Private depth of the wasm stack. pub wasm_stack_private_depth: usize, // absolute value; not a diff. - + /// Wasm instruction offset. pub wasm_inst_offset: usize, // absolute value; not a diff. } +/// A kind of machine value. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum MachineValue { + /// Undefined. Undefined, + /// Vmctx. Vmctx, + /// Vmctx Deref. VmctxDeref(Vec), + /// Preserve Register. PreserveRegister(RegisterIndex), + /// Copy Stack BP Relative. CopyStackBPRelative(i32), // relative to Base Pointer, in byte offset - ExplicitShadow, // indicates that all values above this are above the shadow region + /// Explicit Shadow. + ExplicitShadow, // indicates that all values above this are above the shadow region + /// Wasm Stack. WasmStack(usize), + /// Wasm Local. WasmLocal(usize), + /// Two Halves. TwoHalves(Box<(MachineValue, MachineValue)>), // 32-bit values. TODO: optimize: add another type for inner "half" value to avoid boxing? } +/// A map of function states. #[derive(Clone, Debug)] pub struct FunctionStateMap { + /// Initial. pub initial: MachineState, + /// Local Function Id. pub local_function_id: usize, + /// Locals. pub locals: Vec, + /// Shadow size. pub shadow_size: usize, // for single-pass backend, 32 bytes on x86-64 + /// Diffs. pub diffs: Vec, + /// Wasm Function Header target offset. pub wasm_function_header_target_offset: Option, + /// Wasm offset to target offset pub wasm_offset_to_target_offset: BTreeMap, + /// Loop offsets. pub loop_offsets: BTreeMap, /* suspend_offset -> info */ + /// Call offsets. pub call_offsets: BTreeMap, /* suspend_offset -> info */ + /// Trappable offsets. pub trappable_offsets: BTreeMap, /* suspend_offset -> info */ } +/// A kind of suspend offset. #[derive(Clone, Copy, Debug)] pub enum SuspendOffset { + /// A loop. Loop(usize), + /// A call. Call(usize), + /// A trappable. Trappable(usize), } +/// Info for an offset. #[derive(Clone, Debug)] pub struct OffsetInfo { + /// End offset. pub end_offset: usize, // excluded bound + /// Diff Id. pub diff_id: usize, + /// Activate offset. pub activate_offset: usize, } +/// A map of module state. #[derive(Clone, Debug)] pub struct ModuleStateMap { + /// Local functions. pub local_functions: BTreeMap, + /// Total size. pub total_size: usize, } +/// State dump of a wasm function. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct WasmFunctionStateDump { + /// Local function id. pub local_function_id: usize, + /// Wasm instruction offset. pub wasm_inst_offset: usize, + /// Stack. pub stack: Vec>, + /// Locals. pub locals: Vec>, } +/// An image of the execution state. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ExecutionStateImage { + /// Frames. pub frames: Vec, } +/// Represents an image of an `Instance` including its memory, globals, and execution state. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InstanceImage { + /// Memory for this `InstanceImage` pub memory: Option>, + /// Stored globals for this `InstanceImage` pub globals: Vec, + /// `ExecutionStateImage` for this `InstanceImage` pub execution_state: ExecutionStateImage, } +/// A `CodeVersion` is a container for a unit of generated code for a module. #[derive(Debug, Clone)] pub struct CodeVersion { + /// Indicates if this code version is the baseline version. pub baseline: bool, + + /// `ModuleStateMap` for this code version. pub msm: ModuleStateMap, + + /// A pointer to the machine code for this module. pub base: usize, } impl ModuleStateMap { + /// Looks up an ip from self using the given ip, base, and offset table provider. pub fn lookup_ip &BTreeMap>( &self, ip: usize, @@ -146,6 +218,7 @@ impl ModuleStateMap { } } } + /// Looks up a call ip from self using the given ip and base values. pub fn lookup_call_ip( &self, ip: usize, @@ -154,6 +227,7 @@ impl ModuleStateMap { self.lookup_ip(ip, base, |fsm| &fsm.call_offsets) } + /// Looks up a trappable ip from self using the given ip and base values. pub fn lookup_trappable_ip( &self, ip: usize, @@ -162,6 +236,7 @@ impl ModuleStateMap { self.lookup_ip(ip, base, |fsm| &fsm.trappable_offsets) } + /// Looks up a loop ip from self using the given ip and base values. pub fn lookup_loop_ip( &self, ip: usize, @@ -172,6 +247,7 @@ impl ModuleStateMap { } impl FunctionStateMap { + /// Creates a new `FunctionStateMap` with the given parameters. pub fn new( initial: MachineState, local_function_id: usize, @@ -194,6 +270,7 @@ impl FunctionStateMap { } impl MachineState { + /// Creates a `MachineStateDiff` from self and the given `&MachineState`. pub fn diff(&self, old: &MachineState) -> MachineStateDiff { let first_diff_stack_depth: usize = self .stack_values @@ -256,6 +333,7 @@ impl MachineState { } impl MachineStateDiff { + /// Creates a `MachineState` from the given `&FunctionStateMap`. pub fn build_state(&self, m: &FunctionStateMap) -> MachineState { let mut chain: Vec<&MachineStateDiff> = vec![]; chain.push(self); @@ -298,6 +376,7 @@ impl MachineStateDiff { } impl ExecutionStateImage { + /// Prints a backtrace if the `WASMER_BACKTRACE` environment variable is 1. pub fn print_backtrace_if_needed(&self) { use std::env; @@ -311,6 +390,7 @@ impl ExecutionStateImage { eprintln!("Run with `WASMER_BACKTRACE=1` environment variable to display a backtrace."); } + /// Converts self into a `String`, used for display purposes. pub fn output(&self) -> String { fn join_strings(x: impl Iterator, sep: &str) -> String { let mut ret = String::new(); @@ -376,6 +456,7 @@ impl ExecutionStateImage { } impl InstanceImage { + /// Converts a slice of bytes into an `Option` pub fn from_bytes(input: &[u8]) -> Option { use bincode::deserialize; match deserialize(input) { @@ -384,6 +465,7 @@ impl InstanceImage { } } + /// Converts self into a vector of bytes. pub fn to_bytes(&self) -> Vec { use bincode::serialize; serialize(self).unwrap() @@ -392,6 +474,7 @@ impl InstanceImage { #[cfg(all(unix, target_arch = "x86_64"))] pub mod x64 { + //! The x64 state module contains functions to generate state and code for x64 targets. use super::*; use crate::codegen::BreakpointMap; use crate::fault::{ @@ -410,6 +493,7 @@ pub mod x64 { ptr as usize as u64 } + /// Create a new `MachineState` with default values. pub fn new_machine_state() -> MachineState { MachineState { stack_values: vec![], @@ -421,6 +505,8 @@ pub mod x64 { } } + /// Invokes a call return on the stack for the given module state map, code base, instance + /// image and context. #[warn(unused_variables)] pub unsafe fn invoke_call_return_on_stack( msm: &ModuleStateMap, @@ -772,6 +858,7 @@ pub mod x64 { ) } + /// Builds an `InstanceImage` for the given `Ctx` and `ExecutionStateImage`. pub fn build_instance_image( vmctx: &mut Ctx, execution_state: ExecutionStateImage, @@ -807,6 +894,8 @@ pub mod x64 { } } + /// Returns a `ExecutionStateImage` for the given versions, stack, initial registers and + /// initial address. #[warn(unused_variables)] pub unsafe fn read_stack<'a, I: Iterator, F: Fn() -> I + 'a>( versions: F, @@ -1022,55 +1111,93 @@ pub mod x64 { unreachable!(); } + /// A kind of GPR register #[repr(u8)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum GPR { + /// RAX Register RAX, + /// RCX Register RCX, + /// RDX Register RDX, + /// RBX Register RBX, + /// RSP Register RSP, + /// RBP Register RBP, + /// RSI Register RSI, + /// RDI Register RDI, + /// R8 Register R8, + /// R9 Register R9, + /// R10 Register R10, + /// R11 Register R11, + /// R12 Register R12, + /// R13 Register R13, + /// R14 Register R14, + /// R15 Register R15, } + /// A kind of XMM register #[repr(u8)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum XMM { + /// XMM0 Register XMM0, + /// XMM1 Register XMM1, + /// XMM2 Register XMM2, + /// XMM3 Register XMM3, + /// XMM4 Register XMM4, + /// XMM5 Register XMM5, + /// XMM6 Register XMM6, + /// XMM7 Register XMM7, + /// XMM8 Register XMM8, + /// XMM9 Register XMM9, + /// XMM10 Register XMM10, + /// XMM11 Register XMM11, + /// XMM12 Register XMM12, + /// XMM13 Register XMM13, + /// XMM14 Register XMM14, + /// XMM15 Register XMM15, } + /// A kind of register belonging to the x64 register set #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum X64Register { + /// A register belonging to the GPR register set GPR(GPR), + /// A register belonging to the XMM register set XMM(XMM), } impl X64Register { + /// Returns a `RegisterIndex` for the current `X64Register`. pub fn to_index(&self) -> RegisterIndex { match *self { X64Register::GPR(x) => RegisterIndex(x as usize), @@ -1078,6 +1205,7 @@ pub mod x64 { } } + /// Returns an `Option` for the given DWARF register integer number. pub fn from_dwarf_regnum(x: u16) -> Option { Some(match x { 0 => X64Register::GPR(GPR::RAX), diff --git a/lib/runtime-core/src/structures/boxed.rs b/lib/runtime-core/src/structures/boxed.rs index 710dc70a6a4..439bad68075 100644 --- a/lib/runtime-core/src/structures/boxed.rs +++ b/lib/runtime-core/src/structures/boxed.rs @@ -5,6 +5,7 @@ use std::{ ops::{Deref, DerefMut}, }; +/// Boxed map. #[derive(Debug, Clone)] pub struct BoxedMap where diff --git a/lib/runtime-core/src/structures/map.rs b/lib/runtime-core/src/structures/map.rs index 2d4f3323edd..b3f20b5cb44 100644 --- a/lib/runtime-core/src/structures/map.rs +++ b/lib/runtime-core/src/structures/map.rs @@ -21,6 +21,7 @@ impl Map where K: TypedIndex, { + /// Creates a new `Map`. pub fn new() -> Self { Self { elems: Vec::new(), @@ -28,6 +29,7 @@ where } } + /// Creates a new empty `Map` with the given capacity. pub fn with_capacity(capacity: usize) -> Self { Self { elems: Vec::with_capacity(capacity), @@ -35,32 +37,39 @@ where } } + /// Returns the size of this map. pub fn len(&self) -> usize { self.elems.len() } + /// Returns true if this map is empty. pub fn is_empty(&self) -> bool { self.elems.is_empty() } + /// Adds a new value to this map. pub fn push(&mut self, value: V) -> K { let len = self.len(); self.elems.push(value); K::new(len) } + /// Returns the next index into the map. pub fn next_index(&self) -> K { K::new(self.len()) } + /// Reserves the given size. pub fn reserve_exact(&mut self, size: usize) { self.elems.reserve_exact(size); } + /// Convert this into a `BoxedMap`. pub fn into_boxed_map(self) -> BoxedMap { BoxedMap::new(self.elems.into_boxed_slice()) } + /// Convert this into a `Vec`. pub fn into_vec(self) -> Vec { self.elems } @@ -71,6 +80,7 @@ where K: TypedIndex, V: Clone, { + /// Resize this map to the given new length and value. pub fn resize(&mut self, new_len: usize, value: V) { self.elems.resize(new_len, value); } @@ -184,6 +194,7 @@ where } } +/// Iterator for a `Map`. pub struct Iter<'a, K: TypedIndex, V: 'a> { enumerated: iter::Enumerate>, _marker: PhantomData, @@ -206,6 +217,7 @@ impl<'a, K: TypedIndex, V: 'a> Iterator for Iter<'a, K, V> { } } +/// Mutable iterator for a `Map`. pub struct IterMut<'a, K: TypedIndex, V: 'a> { enumerated: iter::Enumerate>, _marker: PhantomData, diff --git a/lib/runtime-core/src/structures/mod.rs b/lib/runtime-core/src/structures/mod.rs index ca7a449dfdc..42af0d2c07a 100644 --- a/lib/runtime-core/src/structures/mod.rs +++ b/lib/runtime-core/src/structures/mod.rs @@ -1,3 +1,4 @@ +//! The structures module contains commonly used data structures. mod boxed; mod map; mod slice; @@ -6,6 +7,7 @@ pub use self::boxed::BoxedMap; pub use self::map::{Iter, IterMut, Map}; pub use self::slice::SliceMap; +/// Represents a typed index. pub trait TypedIndex: Copy + Clone { #[doc(hidden)] fn new(index: usize) -> Self; diff --git a/lib/runtime-core/src/structures/slice.rs b/lib/runtime-core/src/structures/slice.rs index 8574b2d5bc0..5ac0d6190a5 100644 --- a/lib/runtime-core/src/structures/slice.rs +++ b/lib/runtime-core/src/structures/slice.rs @@ -20,30 +20,37 @@ impl SliceMap where K: TypedIndex, { + /// Gets a reference to the value at the given index. pub fn get(&self, index: K) -> Option<&V> { self.slice.get(index.index()) } + /// Gets a mutable reference to the value at the given index. pub fn get_mut(&mut self, index: K) -> Option<&mut V> { self.slice.get_mut(index.index()) } + /// Gets the length of this slice map. pub fn len(&self) -> usize { self.slice.len() } + /// Returns an iterator for this slice map. pub fn iter(&self) -> Iter { Iter::new(self.slice.iter()) } + /// Returns a mutable iterator for this slice map. pub fn iter_mut(&mut self) -> IterMut { IterMut::new(self.slice.iter_mut()) } + /// Gets a pointer to the `SliceMap`. pub fn as_ptr(&self) -> *const V { self as *const SliceMap as *const V } + /// Gets a mutable pointer to the `SliceMap`. pub fn as_mut_ptr(&mut self) -> *mut V { self as *mut SliceMap as *mut V } diff --git a/lib/runtime-core/src/sys/unix/memory.rs b/lib/runtime-core/src/sys/unix/memory.rs index fc60d732b6f..051dc1f0ebf 100644 --- a/lib/runtime-core/src/sys/unix/memory.rs +++ b/lib/runtime-core/src/sys/unix/memory.rs @@ -9,6 +9,7 @@ use std::{fs::File, os::unix::io::IntoRawFd, path::Path, ptr, slice, sync::Arc}; unsafe impl Send for Memory {} unsafe impl Sync for Memory {} +/// Data for a sized and protected region of memory. #[derive(Debug)] pub struct Memory { ptr: *mut u8, @@ -18,6 +19,7 @@ pub struct Memory { } impl Memory { + /// Create a new memory from the given path value and protection. pub fn from_file_path

(path: P, protection: Protect) -> Result where P: AsRef, @@ -54,6 +56,7 @@ impl Memory { } } + /// Create a new memory with the given size and protection. pub fn with_size_protect(size: usize, protection: Protect) -> Result { if size == 0 { return Ok(Self { @@ -89,6 +92,7 @@ impl Memory { } } + /// Create a new memory with the given size. pub fn with_size(size: usize) -> Result { if size == 0 { return Ok(Self { @@ -127,6 +131,7 @@ impl Memory { } } + /// Protect this memory with the given range bounds and protection. pub unsafe fn protect( &mut self, range: impl RangeBounds, @@ -166,6 +171,7 @@ impl Memory { } } + /// Split this memory into multiple memories by the given offset. pub fn split_at(mut self, offset: usize) -> (Memory, Memory) { let page_size = page_size::get(); if offset % page_size == 0 { @@ -187,22 +193,27 @@ impl Memory { } } + /// Gets the size of this memory. pub fn size(&self) -> usize { self.size } + /// Gets a slice for this memory. pub unsafe fn as_slice(&self) -> &[u8] { slice::from_raw_parts(self.ptr, self.size) } + /// Gets a mutable slice for this memory. pub unsafe fn as_slice_mut(&mut self) -> &mut [u8] { slice::from_raw_parts_mut(self.ptr, self.size) } + /// Gets the protect kind of this memory. pub fn protection(&self) -> Protect { self.protection } + /// Gets mutable pointer to the memory. pub fn as_ptr(&self) -> *mut u8 { self.ptr } @@ -238,13 +249,19 @@ impl Clone for Memory { } } +/// Kinds of memory protection. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[allow(dead_code)] pub enum Protect { + /// Read/write/exec allowed. None, + /// Read only. Read, + /// Read/write only. ReadWrite, + /// Read/exec only. ReadExec, + /// Read/write/exec only. ReadWriteExec, } @@ -259,6 +276,7 @@ impl Protect { } } + /// Returns true if this memory is readable. pub fn is_readable(self) -> bool { match self { Protect::Read | Protect::ReadWrite | Protect::ReadExec | Protect::ReadWriteExec => true, @@ -266,6 +284,7 @@ impl Protect { } } + /// Returns true if this memory is writable. pub fn is_writable(self) -> bool { match self { Protect::ReadWrite | Protect::ReadWriteExec => true, diff --git a/lib/runtime-core/src/sys/windows/memory.rs b/lib/runtime-core/src/sys/windows/memory.rs index 888aeff4dfb..411dee5b779 100644 --- a/lib/runtime-core/src/sys/windows/memory.rs +++ b/lib/runtime-core/src/sys/windows/memory.rs @@ -12,6 +12,7 @@ use winapi::um::winnt::{ unsafe impl Send for Memory {} unsafe impl Sync for Memory {} +/// Data for a sized and protected region of memory. #[derive(Debug)] pub struct Memory { ptr: *mut u8, @@ -20,6 +21,7 @@ pub struct Memory { } impl Memory { + /// Create a new memory from the given path value and protection. pub fn with_size_protect(size: usize, protection: Protect) -> Result { if size == 0 { return Ok(Self { @@ -52,6 +54,7 @@ impl Memory { } } + /// Create a new memory with the given size. pub fn with_size(size: usize) -> Result { if size == 0 { return Ok(Self { @@ -79,6 +82,7 @@ impl Memory { } } + /// Protect this memory with the given range bounds and protection. pub unsafe fn protect( &mut self, range: impl RangeBounds, @@ -120,6 +124,7 @@ impl Memory { } } + /// Split this memory into multiple memories by the given offset. pub fn split_at(mut self, offset: usize) -> (Memory, Memory) { let page_size = page_size::get(); if offset % page_size == 0 { @@ -140,22 +145,27 @@ impl Memory { } } + /// Gets the size of this memory. pub fn size(&self) -> usize { self.size } + /// Gets a slice for this memory. pub unsafe fn as_slice(&self) -> &[u8] { slice::from_raw_parts(self.ptr, self.size) } + /// Gets a mutable slice for this memory. pub unsafe fn as_slice_mut(&mut self) -> &mut [u8] { slice::from_raw_parts_mut(self.ptr, self.size) } + /// Gets the protect kind of this memory. pub fn protection(&self) -> Protect { self.protection } + /// Gets mutable pointer to the memory. pub fn as_ptr(&self) -> *mut u8 { self.ptr } @@ -192,12 +202,17 @@ impl Clone for Memory { } } +/// Kinds of memory protection. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[allow(dead_code)] pub enum Protect { + /// Read/write/exec allowed. None, + /// Read only. Read, + /// Read/write only. ReadWrite, + /// Read/exec only. ReadExec, } @@ -211,6 +226,7 @@ impl Protect { } } + /// Returns true if this memory is readable. pub fn is_readable(self) -> bool { match self { Protect::Read | Protect::ReadWrite | Protect::ReadExec => true, @@ -218,6 +234,7 @@ impl Protect { } } + /// Returns true if this memory is writable. pub fn is_writable(self) -> bool { match self { Protect::ReadWrite => true, diff --git a/lib/runtime-core/src/table/anyfunc.rs b/lib/runtime-core/src/table/anyfunc.rs index 4a63c8f6249..38cb578f27a 100644 --- a/lib/runtime-core/src/table/anyfunc.rs +++ b/lib/runtime-core/src/table/anyfunc.rs @@ -17,11 +17,13 @@ enum AnyfuncInner<'a> { Managed(DynFunc<'a>), } +/// Anyfunc data type. pub struct Anyfunc<'a> { inner: AnyfuncInner<'a>, } impl<'a> Anyfunc<'a> { + /// Create a new `Anyfunc`. pub unsafe fn new(func: *const vm::Func, signature: Sig) -> Self where Sig: Into>, diff --git a/lib/runtime-core/src/table/mod.rs b/lib/runtime-core/src/table/mod.rs index 8e5c0e03323..997ce5eb1e4 100644 --- a/lib/runtime-core/src/table/mod.rs +++ b/lib/runtime-core/src/table/mod.rs @@ -1,3 +1,5 @@ +//! The runtime table module contains data structures and functions used to create and update wasm +//! tables. use crate::{ error::CreationError, export::Export, @@ -16,16 +18,20 @@ pub use self::anyfunc::Anyfunc; pub(crate) use self::anyfunc::AnyfuncTable; use crate::error::GrowError; +/// Kind of table element. pub enum Element<'a> { + /// Anyfunc. Anyfunc(Anyfunc<'a>), } +/// Kind of table storage. // #[derive(Debug)] pub enum TableStorage { /// This is intended to be a caller-checked Anyfunc. Anyfunc(Box), } +/// Container with a descriptor and a reference to a table storage. pub struct Table { desc: TableDescriptor, storage: Arc>, @@ -128,6 +134,7 @@ impl Table { } } + /// Get a mutable pointer to underlying table storage. pub fn vm_local_table(&mut self) -> *mut vm::LocalTable { let mut storage = self.storage.lock().unwrap(); &mut storage.1 diff --git a/lib/runtime-core/src/tiering.rs b/lib/runtime-core/src/tiering.rs index 21a9ea2d377..bb4426b125d 100644 --- a/lib/runtime-core/src/tiering.rs +++ b/lib/runtime-core/src/tiering.rs @@ -1,3 +1,5 @@ +//! The tiering module supports switching between code compiled with different optimization levels +//! as runtime. use crate::backend::{Compiler, CompilerConfig}; use crate::compile_with_config; use crate::fault::{ @@ -22,12 +24,17 @@ impl Drop for Defer { } } +/// Kind of shell exit operation. pub enum ShellExitOperation { + /// Operation to continue with an instance image. ContinueWith(InstanceImage), } +/// Context for an interactive shell. pub struct InteractiveShellContext { + /// Optional instance image. pub image: Option, + /// Flag to indicate patching. pub patched: bool, } @@ -70,6 +77,7 @@ unsafe fn do_optimize( } } +/// Runs an instance with tiering. pub unsafe fn run_tiering ShellExitOperation>( module_info: &ModuleInfo, wasm_binary: &[u8], diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 80de3e0d6bc..3d07484c715 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -62,6 +62,7 @@ pub fn get_context() -> *const CallContext { } impl TrampolineBufferBuilder { + /// Creates a new empty `TrampolineBufferBuilder`. pub fn new() -> TrampolineBufferBuilder { TrampolineBufferBuilder { code: vec![], @@ -100,6 +101,7 @@ impl TrampolineBufferBuilder { idx } + /// Adds context RSP state preserving trampoline to the buffer. pub fn add_context_rsp_state_preserving_trampoline( &mut self, target: unsafe extern "C" fn(&mut Ctx, *const CallContext, *const u64), diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 2a38beb1e97..7904cb0b6d0 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,3 +1,5 @@ +//! The typed func module implements a way of representing a wasm function +//! with the correct types from rust. Function calls using a typed func have a low overhead. use crate::{ error::RuntimeError, export::{Context, Export, FuncPointer}, @@ -16,14 +18,22 @@ use std::{ sync::Arc, }; +/// Wasm trap info. #[repr(C)] pub enum WasmTrapInfo { + /// Unreachable trap. Unreachable = 0, + /// Call indirect incorrect signature trap. IncorrectCallIndirectSignature = 1, + /// Memory out of bounds trap. MemoryOutOfBounds = 2, + /// Call indirect out of bounds trap. CallIndirectOOB = 3, + /// Illegal arithmetic trap. IllegalArithmetic = 4, + /// Misaligned atomic access trap. MisalignedAtomicAccess = 5, + /// Unknown trap. Unknown, } @@ -52,12 +62,15 @@ impl fmt::Display for WasmTrapInfo { /// of the `Func` struct. pub trait Kind {} +/// Aliases to an extern "C" type used as a trampoline to a function. pub type Trampoline = unsafe extern "C" fn( vmctx: *mut vm::Ctx, func: NonNull, args: *const u64, rets: *mut u64, ); + +/// Aliases to an extern "C" type used to invoke a function. pub type Invoke = unsafe extern "C" fn( trampoline: Trampoline, vmctx: *mut vm::Ctx, @@ -80,6 +93,7 @@ pub struct Wasm { } impl Wasm { + /// Create new `Wasm` from given parts. pub unsafe fn from_raw_parts( trampoline: Trampoline, invoke: Invoke, @@ -102,8 +116,10 @@ impl Kind for Host {} /// Represents a list of WebAssembly values. pub trait WasmTypeList { + /// CStruct type. type CStruct; + /// Array of return values. type RetArray: AsMut<[u64]>; /// Construct `Self` based on an array of returned values. @@ -175,14 +191,18 @@ where Args: WasmTypeList, Rets: WasmTypeList, { + /// Conver to function pointer. fn to_raw(&self) -> NonNull; } +/// Represents a TrapEarly type. pub trait TrapEarly where Rets: WasmTypeList, { + /// The error type for this trait. type Error: 'static; + /// Get returns or error result. fn report(self) -> Result; } @@ -236,6 +256,7 @@ where } } + /// Get the underlying func pointer. pub fn get_vm_func(&self) -> NonNull { self.f } @@ -246,6 +267,7 @@ where Args: WasmTypeList, Rets: WasmTypeList, { + /// Creates a new `Func`. pub fn new(f: F) -> Func<'a, Args, Rets, Host> where Kind: ExternalFunctionKind, @@ -390,6 +412,7 @@ impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Wasm> where Rets: WasmTypeList, { + /// Call wasm function and return results. pub fn call(&self, a: A) -> Result { unsafe { ::call(a, self.f, self.inner, self.ctx) } } @@ -397,6 +420,7 @@ where macro_rules! impl_traits { ( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => { + /// Struct for typed funcs. #[repr($repr)] pub struct $struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* ) where @@ -598,6 +622,7 @@ macro_rules! impl_traits { $( $x: WasmExternType, )* Rets: WasmTypeList, { + /// Call the typed func and return results. #[allow(non_snake_case)] pub fn call(&self, $( $x: $x, )* ) -> Result { #[allow(unused_parens)] diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index bea89ffe39d..1b19bbc3d91 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -1,3 +1,6 @@ +//! The runtime types modules represent type used within the wasm runtime and helper functions to +//! convert to other represenations. + use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, units::Pages}; use std::borrow::Cow; @@ -41,6 +44,7 @@ pub enum Value { } impl Value { + /// The `Type` of this `Value`. pub fn ty(&self) -> Type { match self { Value::I32(_) => Type::I32, @@ -51,6 +55,7 @@ impl Value { } } + /// Convert this `Value` to a u128 binary representation. pub fn to_u128(&self) -> u128 { match *self { Value::I32(x) => x as u128, @@ -92,12 +97,16 @@ impl From for Value { } } +/// Represents a native wasm type. pub unsafe trait NativeWasmType: Copy + Into where Self: Sized, { + /// Type for this `NativeWasmType`. const TYPE: Type; + /// Convert from u64 bites to self. fn from_binary(bits: u64) -> Self; + /// Convert self to u64 binary representation. fn to_binary(self) -> u64; } @@ -138,12 +147,16 @@ unsafe impl NativeWasmType for f64 { } } +/// A trait to represent a wasm extern type. pub unsafe trait WasmExternType: Copy where Self: Sized, { + /// Native wasm type for this `WasmExternType`. type Native: NativeWasmType; + /// Convert from given `Native` type to self. fn from_native(native: Self::Native) -> Self; + /// Convert self to `Native` type. fn to_native(self) -> Self::Native; } @@ -255,6 +268,7 @@ unsafe impl WasmExternType for f64 { // fn swap(&self, other: Self::Primitive) -> Self::Primitive; // } +/// Trait for a Value type. pub unsafe trait ValueType: Copy where Self: Sized, @@ -274,12 +288,15 @@ macro_rules! convert_value_impl { convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); +/// Kinds of element types. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum ElementType { /// Any wasm function. Anyfunc, } +/// Describes the properties of a table including the element types, minimum and optional maximum, +/// number of elements in the table. #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct TableDescriptor { /// Type of data stored in this table. @@ -315,14 +332,18 @@ pub enum Initializer { /// Describes the mutability and type of a Global #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct GlobalDescriptor { + /// Mutable flag. pub mutable: bool, + /// Wasm type. pub ty: Type, } /// A wasm global. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct GlobalInit { + /// Global descriptor. pub desc: GlobalDescriptor, + /// Global initializer. pub init: Initializer, } @@ -340,6 +361,7 @@ pub struct MemoryDescriptor { } impl MemoryDescriptor { + /// Create a new memory descriptor with the given min/max pages and shared flag. pub fn new(minimum: Pages, maximum: Option, shared: bool) -> Result { let memory_type = match (maximum.is_some(), shared) { (true, true) => MemoryType::SharedStatic, @@ -357,6 +379,7 @@ impl MemoryDescriptor { }) } + /// Returns the `MemoryType` for this descriptor. pub fn memory_type(&self) -> MemoryType { self.memory_type } @@ -380,6 +403,7 @@ pub struct FuncSig { } impl FuncSig { + /// Creates a new function signatures with the given parameter and return types. pub fn new(params: Params, returns: Returns) -> Self where Params: Into>, @@ -391,14 +415,17 @@ impl FuncSig { } } + /// Parameter types. pub fn params(&self) -> &[Type] { &self.params } + /// Return types. pub fn returns(&self) -> &[Type] { &self.returns } + /// Returns true if parameter types match the function signature. pub fn check_param_value_types(&self, params: &[Value]) -> bool { self.params.len() == params.len() && self @@ -427,14 +454,18 @@ impl std::fmt::Display for FuncSig { } } +/// Trait that represents Local or Import. pub trait LocalImport { + /// Local type. type Local: TypedIndex; + /// Import type. type Import: TypedIndex; } #[rustfmt::skip] macro_rules! define_map_index { ($ty:ident) => { + /// Typed Index #[derive(Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct $ty (u32); @@ -475,6 +506,7 @@ define_map_index![ macro_rules! define_local_or_import { ($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => { impl $ty { + /// Converts self into `LocalOrImport`. pub fn local_or_import(self, info: &ModuleInfo) -> LocalOrImport<$ty> { if self.index() < info.$imports.len() { LocalOrImport::Import(::Import::new(self.index())) @@ -485,12 +517,14 @@ macro_rules! define_local_or_import { } impl $local_ty { + /// Convert up. pub fn convert_up(self, info: &ModuleInfo) -> $ty { $ty ((self.index() + info.$imports.len()) as u32) } } impl $imported_ty { + /// Convert up. pub fn convert_up(self, _info: &ModuleInfo) -> $ty { $ty (self.index() as u32) } @@ -511,6 +545,7 @@ define_local_or_import![ (GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals), ]; +/// Index for signature. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct SigIndex(u32); impl TypedIndex for SigIndex { @@ -525,11 +560,14 @@ impl TypedIndex for SigIndex { } } +/// Kind of local or import type. pub enum LocalOrImport where T: LocalImport, { + /// Local. Local(T::Local), + /// Import. Import(T::Import), } @@ -537,6 +575,7 @@ impl LocalOrImport where T: LocalImport, { + /// Returns `Some` if self is local, `None` if self is an import. pub fn local(self) -> Option { match self { LocalOrImport::Local(local) => Some(local), @@ -544,6 +583,7 @@ where } } + /// Returns `Some` if self is an import, `None` if self is local. pub fn import(self) -> Option { match self { LocalOrImport::Import(import) => Some(import), diff --git a/lib/runtime-core/src/units.rs b/lib/runtime-core/src/units.rs index e8232efc8aa..2e8f2c0a595 100644 --- a/lib/runtime-core/src/units.rs +++ b/lib/runtime-core/src/units.rs @@ -1,12 +1,17 @@ +//! The units module provides common WebAssembly units like `Pages` and conversion functions into +//! other units. use crate::error::PageError; use std::{ fmt, ops::{Add, Sub}, }; +/// The page size in bytes of a wasm page. pub const WASM_PAGE_SIZE: usize = 65_536; +/// Tbe max number of wasm pages allowed. pub const WASM_MAX_PAGES: usize = 65_536; // From emscripten resize_heap implementation +/// The minimum number of wasm pages allowed. pub const WASM_MIN_PAGES: usize = 256; /// Units of WebAssembly pages (as specified to be 65,536 bytes). @@ -14,6 +19,7 @@ pub const WASM_MIN_PAGES: usize = 256; pub struct Pages(pub u32); impl Pages { + /// Checked add of Pages to Pages. pub fn checked_add(self, rhs: Pages) -> Result { let added = (self.0 as usize) + (rhs.0 as usize); if added <= WASM_MAX_PAGES { @@ -27,6 +33,7 @@ impl Pages { } } + /// Calculate number of bytes from pages. pub fn bytes(self) -> Bytes { self.into() } diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 2bb734a04d8..0584633dac8 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -1,3 +1,5 @@ +//! The runtime vm module contains data structures and helper functions used during runtime to +//! execute wasm instance functions. pub use crate::backing::{ImportBacking, LocalBacking, INTERNALS_SIZE}; use crate::{ error::CallResult, @@ -36,6 +38,7 @@ use std::collections::HashMap; #[repr(C)] pub struct Ctx { // `internal` must be the first field of `Ctx`. + /// InternalCtx data field pub internal: InternalCtx, pub(crate) local_functions: *const *const Func, @@ -43,7 +46,9 @@ pub struct Ctx { /// These are pointers to things that are known to be owned /// by the owning `Instance`. pub local_backing: *mut LocalBacking, + /// Mutable pointer to import data pub import_backing: *mut ImportBacking, + /// Const pointer to module inner data pub module: *const ModuleInner, /// This is intended to be user-supplied, per-instance @@ -110,22 +115,31 @@ pub struct InternalCtx { /// modules safely. pub dynamic_sigindices: *const SigId, + /// Const pointer to Intrinsics. pub intrinsics: *const Intrinsics, + /// Stack lower bound. pub stack_lower_bound: *mut u8, + /// Mutable pointer to memory base. pub memory_base: *mut u8, + /// Memory bound. pub memory_bound: usize, + /// Mutable pointer to internal fields. pub internals: *mut [u64; INTERNALS_SIZE], // TODO: Make this dynamic? + /// Interrupt signal mem. pub interrupt_signal_mem: *mut u8, } static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0); +/// An internal field. pub struct InternalField { + /// Init once field. init: Once, + /// Inner field. inner: UnsafeCell, } @@ -133,6 +147,7 @@ unsafe impl Send for InternalField {} unsafe impl Sync for InternalField {} impl InternalField { + /// Allocate and return an `InternalField`. pub const fn allocate() -> InternalField { InternalField { init: Once::new(), @@ -140,6 +155,7 @@ impl InternalField { } } + /// Get the index of this `InternalField`. pub fn index(&self) -> usize { let inner: *mut usize = self.inner.get(); self.init.call_once(|| { @@ -157,9 +173,12 @@ impl InternalField { } } +/// A container for VM instrinsic functions #[repr(C)] pub struct Intrinsics { + /// Const pointer to memory grow `Func`. pub memory_grow: *const Func, + /// Const pointer to memory size `Func`. pub memory_size: *const Func, /*pub memory_grow: unsafe extern "C" fn( ctx: &mut Ctx, @@ -176,27 +195,33 @@ unsafe impl Send for Intrinsics {} unsafe impl Sync for Intrinsics {} impl Intrinsics { + /// Memory grow offset #[allow(clippy::erasing_op)] pub fn offset_memory_grow() -> u8 { (0 * ::std::mem::size_of::()) as u8 } + /// Memory size offset pub fn offset_memory_size() -> u8 { (1 * ::std::mem::size_of::()) as u8 } } +/// Local static memory intrinsics pub static INTRINSICS_LOCAL_STATIC_MEMORY: Intrinsics = Intrinsics { memory_grow: vmcalls::local_static_memory_grow as _, memory_size: vmcalls::local_static_memory_size as _, }; +/// Local dynamic memory intrinsics pub static INTRINSICS_LOCAL_DYNAMIC_MEMORY: Intrinsics = Intrinsics { memory_grow: vmcalls::local_dynamic_memory_grow as _, memory_size: vmcalls::local_dynamic_memory_size as _, }; +/// Imported static memory intrinsics pub static INTRINSICS_IMPORTED_STATIC_MEMORY: Intrinsics = Intrinsics { memory_grow: vmcalls::imported_static_memory_grow as _, memory_size: vmcalls::imported_static_memory_size as _, }; +/// Imported dynamic memory intrinsics pub static INTRINSICS_IMPORTED_DYNAMIC_MEMORY: Intrinsics = Intrinsics { memory_grow: vmcalls::imported_dynamic_memory_grow as _, memory_size: vmcalls::imported_dynamic_memory_size as _, @@ -509,7 +534,9 @@ pub struct Func(InnerFunc); #[derive(Debug, Clone)] #[repr(C)] pub struct ImportedFunc { + /// Const pointer to `Func`. pub func: *const Func, + /// Mutable pointer to `Ctx`. pub vmctx: *mut Ctx, } @@ -517,15 +544,18 @@ pub struct ImportedFunc { unsafe impl Send for ImportedFunc {} impl ImportedFunc { + /// Offset to func. #[allow(clippy::erasing_op)] // TODO pub fn offset_func() -> u8 { 0 * (mem::size_of::() as u8) } + /// Offset to vmctx. pub fn offset_vmctx() -> u8 { 1 * (mem::size_of::() as u8) } + /// Size of an `ImportedFunc`. pub fn size() -> u8 { mem::size_of::() as u8 } @@ -547,15 +577,18 @@ pub struct LocalTable { unsafe impl Send for LocalTable {} impl LocalTable { + /// Offset to base. #[allow(clippy::erasing_op)] // TODO pub fn offset_base() -> u8 { 0 * (mem::size_of::() as u8) } + /// Offset count. pub fn offset_count() -> u8 { 1 * (mem::size_of::() as u8) } + /// Size of a `LocalTable`. pub fn size() -> u8 { mem::size_of::() as u8 } @@ -579,15 +612,18 @@ pub struct LocalMemory { unsafe impl Send for LocalMemory {} impl LocalMemory { + /// Offset base. #[allow(clippy::erasing_op)] // TODO pub fn offset_base() -> u8 { 0 * (mem::size_of::() as u8) } + /// Offset bound. pub fn offset_bound() -> u8 { 1 * (mem::size_of::() as u8) } + /// Size of a `LocalMemory`. pub fn size() -> u8 { mem::size_of::() as u8 } @@ -597,24 +633,29 @@ impl LocalMemory { #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct LocalGlobal { + /// Data. pub data: u128, } impl LocalGlobal { + /// Offset data. #[allow(clippy::erasing_op)] // TODO pub fn offset_data() -> u8 { 0 * (mem::size_of::() as u8) } + /// A null `LocalGlobal`. pub fn null() -> Self { Self { data: 0 } } + /// Size of a `LocalGlobal`. pub fn size() -> u8 { mem::size_of::() as u8 } } +/// Identifier for a function signature. #[derive(Debug, Clone, Copy)] #[repr(transparent)] pub struct SigId(pub u32); @@ -623,8 +664,11 @@ pub struct SigId(pub u32); #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Anyfunc { + /// Const pointer to `Func`. pub func: *const Func, + /// Mutable pointer to `Ctx`. pub ctx: *mut Ctx, + /// Sig id of this function pub sig_id: SigId, } @@ -632,6 +676,7 @@ pub struct Anyfunc { unsafe impl Send for Anyfunc {} impl Anyfunc { + /// A null `Anyfunc` value. pub fn null() -> Self { Self { func: ptr::null(), @@ -640,19 +685,23 @@ impl Anyfunc { } } + /// The offset for this func. #[allow(clippy::erasing_op)] // TODO pub fn offset_func() -> u8 { 0 * (mem::size_of::() as u8) } + /// The offset of the vmctx. pub fn offset_vmctx() -> u8 { 1 * (mem::size_of::() as u8) } + /// The offset of the sig id. pub fn offset_sig_id() -> u8 { 2 * (mem::size_of::() as u8) } + /// The size of `Anyfunc`. pub fn size() -> u8 { mem::size_of::() as u8 }