use crate::externref::VMExternRef;
use crate::instance::Instance;
use crate::table::{Table, TableElementType};
use crate::traphandlers::{raise_lib_trap, resume_panic, Trap};
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMContext};
use backtrace::Backtrace;
use std::mem;
use std::ptr::{self, NonNull};
use wasmtime_environ::{DataIndex, ElemIndex, GlobalIndex, MemoryIndex, TableIndex, TrapCode};
const TOINT_32: f32 = 1.0 / f32::EPSILON;
const TOINT_64: f64 = 1.0 / f64::EPSILON;
pub extern "C" fn wasmtime_f32_ceil(x: f32) -> f32 {
x.ceil()
}
pub extern "C" fn wasmtime_f32_floor(x: f32) -> f32 {
x.floor()
}
pub extern "C" fn wasmtime_f32_trunc(x: f32) -> f32 {
x.trunc()
}
#[allow(clippy::float_arithmetic, clippy::float_cmp)]
pub extern "C" fn wasmtime_f32_nearest(x: f32) -> f32 {
let i = x.to_bits();
let e = i >> 23 & 0xff;
if e >= 0x7f_u32 + 23 {
if e == 0xff {
if i & 0x7fffff != 0 {
return f32::from_bits(i | (1 << 22));
}
}
x
} else {
(x.abs() + TOINT_32 - TOINT_32).copysign(x)
}
}
pub extern "C" fn wasmtime_i64_udiv(x: u64, y: u64) -> u64 {
x / y
}
pub extern "C" fn wasmtime_i64_sdiv(x: i64, y: i64) -> i64 {
x / y
}
pub extern "C" fn wasmtime_i64_urem(x: u64, y: u64) -> u64 {
x % y
}
pub extern "C" fn wasmtime_i64_srem(x: i64, y: i64) -> i64 {
x % y
}
pub extern "C" fn wasmtime_i64_ishl(x: i64, y: i64) -> i64 {
x << y
}
pub extern "C" fn wasmtime_i64_ushr(x: u64, y: i64) -> u64 {
x >> y
}
pub extern "C" fn wasmtime_i64_sshr(x: i64, y: i64) -> i64 {
x >> y
}
pub extern "C" fn wasmtime_f64_ceil(x: f64) -> f64 {
x.ceil()
}
pub extern "C" fn wasmtime_f64_floor(x: f64) -> f64 {
x.floor()
}
pub extern "C" fn wasmtime_f64_trunc(x: f64) -> f64 {
x.trunc()
}
#[allow(clippy::float_arithmetic, clippy::float_cmp)]
pub extern "C" fn wasmtime_f64_nearest(x: f64) -> f64 {
let i = x.to_bits();
let e = i >> 52 & 0x7ff;
if e >= 0x3ff_u64 + 52 {
if e == 0x7ff {
if i & 0xfffffffffffff != 0 {
return f64::from_bits(i | (1 << 51));
}
}
x
} else {
(x.abs() + TOINT_64 - TOINT_64).copysign(x)
}
}
pub unsafe extern "C" fn wasmtime_memory32_grow(
vmctx: *mut VMContext,
delta: u64,
memory_index: u32,
) -> usize {
match std::panic::catch_unwind(|| {
let instance = (*vmctx).instance_mut();
let memory_index = MemoryIndex::from_u32(memory_index);
instance.memory_grow(memory_index, delta)
}) {
Ok(Ok(Some(size_in_bytes))) => size_in_bytes / (wasmtime_environ::WASM_PAGE_SIZE as usize),
Ok(Ok(None)) => usize::max_value(),
Ok(Err(err)) => crate::traphandlers::raise_user_trap(err),
Err(p) => resume_panic(p),
}
}
pub unsafe extern "C" fn wasmtime_table_grow(
vmctx: *mut VMContext,
table_index: u32,
delta: u32,
init_value: *mut u8,
) -> u32 {
match std::panic::catch_unwind(|| {
let instance = (*vmctx).instance_mut();
let table_index = TableIndex::from_u32(table_index);
let element = match instance.table_element_type(table_index) {
TableElementType::Func => (init_value as *mut VMCallerCheckedAnyfunc).into(),
TableElementType::Extern => {
let init_value = if init_value.is_null() {
None
} else {
Some(VMExternRef::clone_from_raw(init_value))
};
init_value.into()
}
};
instance.table_grow(table_index, delta, element)
}) {
Ok(Ok(Some(r))) => r,
Ok(Ok(None)) => -1_i32 as u32,
Ok(Err(err)) => crate::traphandlers::raise_user_trap(err),
Err(p) => resume_panic(p),
}
}
pub unsafe extern "C" fn wasmtime_table_fill(
vmctx: *mut VMContext,
table_index: u32,
dst: u32,
val: *mut u8,
len: u32,
) {
let result = {
let instance = (*vmctx).instance_mut();
let table_index = TableIndex::from_u32(table_index);
let table = &mut *instance.get_table(table_index);
match table.element_type() {
TableElementType::Func => {
let val = val as *mut VMCallerCheckedAnyfunc;
table.fill(dst, val.into(), len)
}
TableElementType::Extern => {
let val = if val.is_null() {
None
} else {
Some(VMExternRef::clone_from_raw(val))
};
table.fill(dst, val.into(), len)
}
}
};
if let Err(trap) = result {
raise_lib_trap(trap);
}
}
pub unsafe extern "C" fn wasmtime_table_copy(
vmctx: *mut VMContext,
dst_table_index: u32,
src_table_index: u32,
dst: u32,
src: u32,
len: u32,
) {
let result = {
let dst_table_index = TableIndex::from_u32(dst_table_index);
let src_table_index = TableIndex::from_u32(src_table_index);
let instance = (*vmctx).instance_mut();
let dst_table = instance.get_table(dst_table_index);
let src_table = instance.get_table(src_table_index);
Table::copy(dst_table, src_table, dst, src, len)
};
if let Err(trap) = result {
raise_lib_trap(trap);
}
}
pub unsafe extern "C" fn wasmtime_table_init(
vmctx: *mut VMContext,
table_index: u32,
elem_index: u32,
dst: u32,
src: u32,
len: u32,
) {
let result = {
let table_index = TableIndex::from_u32(table_index);
let elem_index = ElemIndex::from_u32(elem_index);
let instance = (*vmctx).instance_mut();
instance.table_init(table_index, elem_index, dst, src, len)
};
if let Err(trap) = result {
raise_lib_trap(trap);
}
}
pub unsafe extern "C" fn wasmtime_elem_drop(vmctx: *mut VMContext, elem_index: u32) {
let elem_index = ElemIndex::from_u32(elem_index);
let instance = (*vmctx).instance_mut();
instance.elem_drop(elem_index);
}
pub unsafe extern "C" fn wasmtime_memory_copy(
vmctx: *mut VMContext,
dst_index: u32,
dst: u64,
src_index: u32,
src: u64,
len: u64,
) {
let result = {
let src_index = MemoryIndex::from_u32(src_index);
let dst_index = MemoryIndex::from_u32(dst_index);
let instance = (*vmctx).instance_mut();
instance.memory_copy(dst_index, dst, src_index, src, len)
};
if let Err(trap) = result {
raise_lib_trap(trap);
}
}
pub unsafe extern "C" fn wasmtime_memory_fill(
vmctx: *mut VMContext,
memory_index: u32,
dst: u64,
val: u32,
len: u64,
) {
let result = {
let memory_index = MemoryIndex::from_u32(memory_index);
let instance = (*vmctx).instance_mut();
instance.memory_fill(memory_index, dst, val as u8, len)
};
if let Err(trap) = result {
raise_lib_trap(trap);
}
}
pub unsafe extern "C" fn wasmtime_memory_init(
vmctx: *mut VMContext,
memory_index: u32,
data_index: u32,
dst: u64,
src: u32,
len: u32,
) {
let result = {
let memory_index = MemoryIndex::from_u32(memory_index);
let data_index = DataIndex::from_u32(data_index);
let instance = (*vmctx).instance_mut();
instance.memory_init(memory_index, data_index, dst, src, len)
};
if let Err(trap) = result {
raise_lib_trap(trap);
}
}
pub unsafe extern "C" fn wasmtime_data_drop(vmctx: *mut VMContext, data_index: u32) {
let data_index = DataIndex::from_u32(data_index);
let instance = (*vmctx).instance_mut();
instance.data_drop(data_index)
}
pub unsafe extern "C" fn wasmtime_drop_externref(externref: *mut u8) {
let externref = externref as *mut crate::externref::VMExternData;
let externref = NonNull::new(externref).unwrap();
crate::externref::VMExternData::drop_and_dealloc(externref);
}
pub unsafe extern "C" fn wasmtime_activations_table_insert_with_gc(
vmctx: *mut VMContext,
externref: *mut u8,
) {
let externref = VMExternRef::clone_from_raw(externref);
let instance = (*vmctx).instance();
let (activations_table, module_info_lookup) = (*instance.store()).externref_activations_table();
activations_table.insert_with_gc(externref, module_info_lookup);
}
pub unsafe extern "C" fn wasmtime_externref_global_get(
vmctx: *mut VMContext,
index: u32,
) -> *mut u8 {
let index = GlobalIndex::from_u32(index);
let instance = (*vmctx).instance();
let global = instance.defined_or_imported_global_ptr(index);
match (*global).as_externref().clone() {
None => ptr::null_mut(),
Some(externref) => {
let raw = externref.as_raw();
let (activations_table, module_info_lookup) =
(*instance.store()).externref_activations_table();
activations_table.insert_with_gc(externref, module_info_lookup);
raw
}
}
}
pub unsafe extern "C" fn wasmtime_externref_global_set(
vmctx: *mut VMContext,
index: u32,
externref: *mut u8,
) {
let externref = if externref.is_null() {
None
} else {
Some(VMExternRef::clone_from_raw(externref))
};
let index = GlobalIndex::from_u32(index);
let instance = (*vmctx).instance();
let global = instance.defined_or_imported_global_ptr(index);
let old = mem::replace((*global).as_externref_mut(), externref);
drop(old);
}
pub unsafe extern "C" fn wasmtime_memory_atomic_notify(
vmctx: *mut VMContext,
memory_index: u32,
addr: usize,
_count: u32,
) -> u32 {
let result = {
let memory = MemoryIndex::from_u32(memory_index);
let instance = (*vmctx).instance();
let addr_to_check = addr.checked_add(4).unwrap();
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
Err(Trap::User(anyhow::anyhow!(
"unimplemented: wasm atomics (fn wasmtime_memory_atomic_notify) unsupported",
)))
})
};
match result {
Ok(n) => n,
Err(e) => raise_lib_trap(e),
}
}
pub unsafe extern "C" fn wasmtime_memory_atomic_wait32(
vmctx: *mut VMContext,
memory_index: u32,
addr: usize,
_expected: u32,
_timeout: u64,
) -> u32 {
let result = {
let memory = MemoryIndex::from_u32(memory_index);
let instance = (*vmctx).instance();
let addr_to_check = addr.checked_add(4).unwrap();
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
Err(Trap::User(anyhow::anyhow!(
"unimplemented: wasm atomics (fn wasmtime_memory_atomic_wait32) unsupported",
)))
})
};
match result {
Ok(n) => n,
Err(e) => raise_lib_trap(e),
}
}
pub unsafe extern "C" fn wasmtime_memory_atomic_wait64(
vmctx: *mut VMContext,
memory_index: u32,
addr: usize,
_expected: u64,
_timeout: u64,
) -> u32 {
let result = {
let memory = MemoryIndex::from_u32(memory_index);
let instance = (*vmctx).instance();
let addr_to_check = addr.checked_add(8).unwrap();
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
Err(Trap::User(anyhow::anyhow!(
"unimplemented: wasm atomics (fn wasmtime_memory_atomic_wait64) unsupported",
)))
})
};
match result {
Ok(n) => n,
Err(e) => raise_lib_trap(e),
}
}
unsafe fn validate_atomic_addr(
instance: &Instance,
memory: MemoryIndex,
addr: usize,
) -> Result<(), Trap> {
if addr > instance.get_memory(memory).current_length {
return Err(Trap::Wasm {
trap_code: TrapCode::HeapOutOfBounds,
backtrace: Backtrace::new_unresolved(),
});
}
Ok(())
}
pub unsafe extern "C" fn wasmtime_out_of_gas(vmctx: *mut VMContext) {
match (*(*vmctx).instance().store()).out_of_gas() {
Ok(()) => {}
Err(err) => crate::traphandlers::raise_user_trap(err),
}
}