624 lines
17 KiB
Rust
Raw Normal View History

2019-01-21 14:01:25 -08:00
pub use crate::backing::{ImportBacking, LocalBacking};
use crate::{
module::ModuleInner,
2019-01-21 14:05:32 -08:00
structures::TypedIndex,
2019-01-21 14:01:25 -08:00
types::{LocalMemoryIndex, LocalOrImport, MemoryIndex},
2019-01-17 14:13:28 -08:00
};
use std::{ffi::c_void, mem, ptr, slice};
2019-01-08 12:09:47 -05:00
#[derive(Debug)]
#[repr(C)]
pub struct Ctx {
/// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`.
2019-01-12 22:02:19 -05:00
pub(crate) memories: *mut LocalMemory,
2019-01-08 12:09:47 -05:00
/// A pointer to an array of locally-defined tables, indexed by `TableIndex`.
2019-01-12 22:02:19 -05:00
pub(crate) tables: *mut LocalTable,
2019-01-08 12:09:47 -05:00
/// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`.
2019-01-12 22:02:19 -05:00
pub(crate) globals: *mut LocalGlobal,
2019-01-08 12:09:47 -05:00
/// A pointer to an array of imported memories, indexed by `MemoryIndex,
2019-01-12 22:02:19 -05:00
pub(crate) imported_memories: *mut ImportedMemory,
2019-01-08 12:09:47 -05:00
/// A pointer to an array of imported tables, indexed by `TableIndex`.
2019-01-12 22:02:19 -05:00
pub(crate) imported_tables: *mut ImportedTable,
2019-01-08 12:09:47 -05:00
/// A pointer to an array of imported globals, indexed by `GlobalIndex`.
2019-01-12 22:02:19 -05:00
pub(crate) imported_globals: *mut ImportedGlobal,
2019-01-08 12:09:47 -05:00
/// A pointer to an array of imported functions, indexed by `FuncIndex`.
2019-01-12 22:02:19 -05:00
pub(crate) imported_funcs: *mut ImportedFunc,
2019-01-08 12:09:47 -05:00
pub(crate) local_backing: *mut LocalBacking,
pub(crate) import_backing: *mut ImportBacking,
module: *const ModuleInner,
pub data: *mut c_void,
pub data_finalizer: Option<extern "C" fn(data: *mut c_void)>,
2019-01-08 12:09:47 -05:00
}
impl Ctx {
2019-01-12 22:02:19 -05:00
pub unsafe fn new(
local_backing: &mut LocalBacking,
import_backing: &mut ImportBacking,
module: &ModuleInner,
2019-01-12 22:02:19 -05:00
) -> Self {
2019-01-08 12:09:47 -05:00
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(),
imported_memories: import_backing.memories.as_mut_ptr(),
imported_tables: import_backing.tables.as_mut_ptr(),
imported_globals: import_backing.globals.as_mut_ptr(),
imported_funcs: import_backing.functions.as_mut_ptr(),
2019-01-10 22:59:57 -05:00
2019-01-08 12:09:47 -05:00
local_backing,
2019-01-17 13:09:05 -08:00
import_backing,
module,
data: ptr::null_mut(),
data_finalizer: None,
}
}
pub unsafe fn new_with_data(
local_backing: &mut LocalBacking,
import_backing: &mut ImportBacking,
module: &ModuleInner,
data: *mut c_void,
data_finalizer: extern "C" 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(),
imported_memories: import_backing.memories.as_mut_ptr(),
imported_tables: import_backing.tables.as_mut_ptr(),
imported_globals: import_backing.globals.as_mut_ptr(),
imported_funcs: import_backing.functions.as_mut_ptr(),
local_backing,
import_backing,
module,
data,
data_finalizer: Some(data_finalizer),
2019-01-08 12:09:47 -05:00
}
}
2019-01-21 14:05:32 -08:00
pub fn memory<'a>(&'a mut self, mem_index: u32) -> &'a mut [u8] {
let module = unsafe { &*self.module };
2019-01-21 14:05:32 -08:00
let mem_index = MemoryIndex::new(mem_index as usize);
match mem_index.local_or_import(module) {
LocalOrImport::Local(local_mem_index) => {
let local_backing = unsafe { &mut *self.local_backing };
&mut local_backing.memories[local_mem_index][..]
2019-01-21 09:59:53 -08:00
}
LocalOrImport::Import(import_mem_index) => {
let import_backing = unsafe { &mut *self.import_backing };
let vm_memory_import = import_backing.memories[import_mem_index].clone();
unsafe {
let memory = &*vm_memory_import.memory;
slice::from_raw_parts_mut(memory.base, memory.size)
}
2019-01-21 09:59:53 -08:00
}
}
}
}
impl Ctx {
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-08 12:09:47 -05:00
pub fn offset_memories() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
pub fn offset_tables() -> u8 {
1 * (mem::size_of::<usize>() as u8)
}
pub fn offset_globals() -> u8 {
2 * (mem::size_of::<usize>() as u8)
}
pub fn offset_imported_memories() -> u8 {
3 * (mem::size_of::<usize>() as u8)
}
pub fn offset_imported_tables() -> u8 {
4 * (mem::size_of::<usize>() as u8)
}
pub fn offset_imported_globals() -> u8 {
5 * (mem::size_of::<usize>() as u8)
}
pub fn offset_imported_funcs() -> u8 {
6 * (mem::size_of::<usize>() as u8)
}
pub fn offset_signatures() -> u8 {
7 * (mem::size_of::<usize>() as u8)
}
}
/// Used to provide type safety (ish) for passing around function pointers.
/// The typesystem ensures this cannot be dereferenced since an
/// empty enum cannot actually exist.
pub enum Func {}
/// An imported function, which contains the vmctx that owns this function.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct ImportedFunc {
pub func: *const Func,
2019-01-10 22:59:57 -05:00
pub vmctx: *mut Ctx,
2019-01-08 12:09:47 -05:00
}
impl ImportedFunc {
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-08 12:09:47 -05:00
pub fn offset_func() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
2019-01-08 21:57:28 -05:00
2019-01-10 22:59:57 -05:00
pub fn offset_vmctx() -> u8 {
1 * (mem::size_of::<usize>() as u8)
}
2019-01-08 21:57:28 -05:00
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
2019-01-08 12:09:47 -05:00
}
/// Definition of a table used by the VM. (obviously)
#[derive(Debug, Clone)]
#[repr(C)]
pub struct LocalTable {
/// pointer to the elements in the table.
pub base: *mut u8,
/// Number of elements in the table (NOT necessarily the size of the table in bytes!).
pub current_elements: usize,
2019-01-17 13:09:05 -08:00
/// The number of elements that can fit into the memory allocated for this table.
pub capacity: usize,
2019-01-08 12:09:47 -05:00
}
impl LocalTable {
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-08 12:09:47 -05:00
pub fn offset_base() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
pub fn offset_current_elements() -> u8 {
1 * (mem::size_of::<usize>() as u8)
}
2019-01-16 17:59:12 -08:00
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
2019-01-08 12:09:47 -05:00
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct ImportedTable {
/// A pointer to the table definition.
pub table: *mut LocalTable,
/// A pointer to the vmcontext that owns this table definition.
pub vmctx: *mut Ctx,
}
impl ImportedTable {
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-08 12:09:47 -05:00
pub fn offset_table() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
pub fn offset_vmctx() -> u8 {
1 * (mem::size_of::<usize>() as u8)
}
2019-01-16 17:59:12 -08:00
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
2019-01-08 12:09:47 -05:00
}
/// Definition of a memory used by the VM.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct LocalMemory {
/// Pointer to the bottom of this linear memory.
pub base: *mut u8,
/// Current size of this linear memory in bytes.
pub size: usize,
2019-01-17 13:09:05 -08:00
/// The local memory index.
pub index: LocalMemoryIndex,
2019-01-08 12:09:47 -05:00
}
impl LocalMemory {
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-08 12:09:47 -05:00
pub fn offset_base() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
pub fn offset_size() -> u8 {
1 * (mem::size_of::<usize>() as u8)
}
2019-01-16 17:59:12 -08:00
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
2019-01-08 12:09:47 -05:00
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct ImportedMemory {
/// A pointer to the memory definition.
pub memory: *mut LocalMemory,
2019-01-10 22:59:57 -05:00
pub vmctx: *mut Ctx,
2019-01-08 12:09:47 -05:00
}
impl ImportedMemory {
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-08 12:09:47 -05:00
pub fn offset_memory() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
2019-01-10 22:59:57 -05:00
pub fn offset_vmctx() -> u8 {
1 * (mem::size_of::<usize>() as u8)
}
2019-01-16 17:59:12 -08:00
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
2019-01-08 12:09:47 -05:00
}
/// Definition of a global used by the VM.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct LocalGlobal {
pub data: u64,
}
impl LocalGlobal {
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-08 12:09:47 -05:00
pub fn offset_data() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
pub fn null() -> Self {
Self { data: 0 }
}
2019-01-16 10:26:10 -08:00
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
2019-01-08 12:09:47 -05:00
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct ImportedGlobal {
2019-01-10 22:59:57 -05:00
pub global: *mut LocalGlobal,
2019-01-08 12:09:47 -05:00
}
impl ImportedGlobal {
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-10 22:59:57 -05:00
pub fn offset_global() -> u8 {
2019-01-08 12:09:47 -05:00
0 * (mem::size_of::<usize>() as u8)
}
2019-01-16 10:26:10 -08:00
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
2019-01-08 12:09:47 -05:00
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct SigId(pub u32);
/// Caller-checked anyfunc
#[derive(Debug, Clone)]
#[repr(C)]
pub struct Anyfunc {
pub func_data: ImportedFunc,
pub sig_id: SigId,
}
impl Anyfunc {
pub fn null() -> Self {
Self {
2019-01-10 22:59:57 -05:00
func_data: ImportedFunc {
func: ptr::null(),
vmctx: ptr::null_mut(),
},
2019-01-08 12:09:47 -05:00
sig_id: SigId(u32::max_value()),
}
}
2019-01-18 00:33:46 -06:00
#[allow(clippy::erasing_op)] // TODO
2019-01-08 12:09:47 -05:00
pub fn offset_func() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
2019-01-10 22:59:57 -05:00
pub fn offset_vmctx() -> u8 {
1 * (mem::size_of::<usize>() as u8)
}
2019-01-08 12:09:47 -05:00
pub fn offset_sig_id() -> u8 {
2019-01-10 22:59:57 -05:00
2 * (mem::size_of::<usize>() as u8)
2019-01-08 12:09:47 -05:00
}
2019-01-16 17:59:12 -08:00
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
2019-01-08 12:09:47 -05:00
}
#[cfg(test)]
mod vm_offset_tests {
use super::{
Anyfunc, Ctx, ImportedFunc, ImportedGlobal, ImportedMemory, ImportedTable, LocalGlobal,
LocalMemory, LocalTable,
};
#[test]
fn vmctx() {
assert_eq!(
Ctx::offset_memories() as usize,
offset_of!(Ctx => memories).get_byte_offset(),
);
assert_eq!(
Ctx::offset_tables() as usize,
offset_of!(Ctx => tables).get_byte_offset(),
);
assert_eq!(
Ctx::offset_globals() as usize,
offset_of!(Ctx => globals).get_byte_offset(),
);
assert_eq!(
Ctx::offset_imported_memories() as usize,
offset_of!(Ctx => imported_memories).get_byte_offset(),
);
assert_eq!(
Ctx::offset_imported_tables() as usize,
offset_of!(Ctx => imported_tables).get_byte_offset(),
);
assert_eq!(
Ctx::offset_imported_globals() as usize,
offset_of!(Ctx => imported_globals).get_byte_offset(),
);
assert_eq!(
Ctx::offset_imported_funcs() as usize,
offset_of!(Ctx => imported_funcs).get_byte_offset(),
);
}
#[test]
fn imported_func() {
assert_eq!(
ImportedFunc::offset_func() as usize,
offset_of!(ImportedFunc => func).get_byte_offset(),
);
2019-01-10 22:59:57 -05:00
assert_eq!(
ImportedFunc::offset_vmctx() as usize,
offset_of!(ImportedFunc => vmctx).get_byte_offset(),
);
2019-01-08 12:09:47 -05:00
}
#[test]
fn local_table() {
assert_eq!(
LocalTable::offset_base() as usize,
offset_of!(LocalTable => base).get_byte_offset(),
);
assert_eq!(
LocalTable::offset_current_elements() as usize,
offset_of!(LocalTable => current_elements).get_byte_offset(),
);
}
#[test]
fn imported_table() {
assert_eq!(
ImportedTable::offset_table() as usize,
offset_of!(ImportedTable => table).get_byte_offset(),
);
assert_eq!(
ImportedTable::offset_vmctx() as usize,
offset_of!(ImportedTable => vmctx).get_byte_offset(),
);
}
#[test]
fn local_memory() {
assert_eq!(
LocalMemory::offset_base() as usize,
offset_of!(LocalMemory => base).get_byte_offset(),
);
assert_eq!(
LocalMemory::offset_size() as usize,
offset_of!(LocalMemory => size).get_byte_offset(),
);
}
#[test]
fn imported_memory() {
assert_eq!(
ImportedMemory::offset_memory() as usize,
offset_of!(ImportedMemory => memory).get_byte_offset(),
);
2019-01-10 22:59:57 -05:00
assert_eq!(
ImportedMemory::offset_vmctx() as usize,
offset_of!(ImportedMemory => vmctx).get_byte_offset(),
);
2019-01-08 12:09:47 -05:00
}
#[test]
fn local_global() {
assert_eq!(
LocalGlobal::offset_data() as usize,
offset_of!(LocalGlobal => data).get_byte_offset(),
);
}
#[test]
fn imported_global() {
assert_eq!(
2019-01-10 22:59:57 -05:00
ImportedGlobal::offset_global() as usize,
offset_of!(ImportedGlobal => global).get_byte_offset(),
2019-01-08 12:09:47 -05:00
);
}
#[test]
fn cc_anyfunc() {
assert_eq!(
Anyfunc::offset_func() as usize,
offset_of!(Anyfunc => func_data: ImportedFunc => func).get_byte_offset(),
);
2019-01-10 22:59:57 -05:00
assert_eq!(
Anyfunc::offset_vmctx() as usize,
offset_of!(Anyfunc => func_data: ImportedFunc => vmctx).get_byte_offset(),
);
2019-01-08 12:09:47 -05:00
assert_eq!(
Anyfunc::offset_sig_id() as usize,
offset_of!(Anyfunc => sig_id).get_byte_offset(),
);
}
}
#[cfg(test)]
mod vm_ctx_tests {
use super::{Ctx, ImportBacking, LocalBacking};
2019-01-21 10:07:57 -08:00
use crate::module::ModuleInner;
use crate::structures::Map;
use std::ffi::c_void;
struct TestData {
x: u32,
y: bool,
str: String,
}
extern "C" 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);
assert_eq!(test_data.str, "Test".to_string());
println!("hello from finalizer");
drop(test_data);
}
#[test]
fn test_callback_on_drop() {
let mut data = TestData {
x: 10,
y: true,
str: "Test".to_string(),
};
let mut local_backing = LocalBacking {
memories: Map::new().into_boxed_map(),
tables: Map::new().into_boxed_map(),
vm_memories: Map::new().into_boxed_map(),
vm_tables: Map::new().into_boxed_map(),
vm_globals: Map::new().into_boxed_map(),
};
let mut import_backing = ImportBacking {
functions: Map::new().into_boxed_map(),
memories: Map::new().into_boxed_map(),
tables: Map::new().into_boxed_map(),
globals: Map::new().into_boxed_map(),
};
2019-01-21 10:07:57 -08:00
let module = generate_module();
let data = &mut data as *mut _ as *mut c_void;
let ctx = unsafe {
Ctx::new_with_data(
&mut local_backing,
&mut import_backing,
2019-01-21 10:07:57 -08:00
&module,
data,
test_data_finalizer,
)
};
let ctx_test_data = cast_test_data(ctx.data);
assert_eq!(ctx_test_data.x, 10);
assert_eq!(ctx_test_data.y, true);
assert_eq!(ctx_test_data.str, "Test".to_string());
drop(ctx);
}
fn cast_test_data(data: *mut c_void) -> &'static mut TestData {
let test_data: &mut TestData = unsafe { &mut *(data as *mut TestData) };
test_data
}
2019-01-21 10:07:57 -08:00
fn generate_module() -> ModuleInner {
use super::Func;
2019-01-21 14:01:25 -08:00
use crate::backend::{FuncResolver, ProtectedCaller, SigRegistry, Token};
use crate::error::RuntimeResult;
use crate::types::{FuncIndex, LocalFuncIndex, Value};
2019-01-21 10:07:57 -08:00
use hashbrown::HashMap;
use std::ptr::NonNull;
struct Placeholder;
impl FuncResolver for Placeholder {
fn get(
&self,
_module: &ModuleInner,
_local_func_index: LocalFuncIndex,
2019-01-21 10:07:57 -08:00
) -> Option<NonNull<Func>> {
None
}
}
2019-01-21 14:01:25 -08:00
impl ProtectedCaller for Placeholder {
fn call(
&self,
_module: &ModuleInner,
_func_index: FuncIndex,
_params: &[Value],
_returns: &mut [Value],
_import_backing: &ImportBacking,
_vmctx: *mut Ctx,
_: Token,
) -> RuntimeResult<()> {
Ok(())
}
}
2019-01-21 10:07:57 -08:00
ModuleInner {
func_resolver: Box::new(Placeholder),
2019-01-21 14:01:25 -08:00
protected_caller: Box::new(Placeholder),
2019-01-21 10:07:57 -08:00
memories: Map::new(),
globals: Map::new(),
tables: Map::new(),
// These are strictly imported and the typesystem ensures that.
imported_functions: Map::new(),
imported_memories: Map::new(),
imported_tables: Map::new(),
imported_globals: Map::new(),
exports: HashMap::new(),
data_initializers: Vec::new(),
elem_initializers: Vec::new(),
start_func: None,
func_assoc: Map::new(),
sig_registry: SigRegistry::new(),
}
}
}