extern crate alloc;
use alloc::boxed::Box;
use core::alloc::Layout;
use core::ffi::c_void;
use crate::platform::{ConsoleLevel, ConsoleProvider, RandomProvider, TimeProvider};
use crate::prelude::Rc;
use crate::ffi::{TsRunContext, console::FfiConsoleProvider};
mod regexp;
pub use regexp::WasmRegExpProvider;
use dlmalloc::GlobalDlmalloc;
#[global_allocator]
static ALLOCATOR: GlobalDlmalloc = GlobalDlmalloc;
#[cfg(all(not(feature = "std"), not(test)))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
core::arch::wasm32::unreachable()
}
#[link(wasm_import_module = "tsrun_host")]
unsafe extern "C" {
fn host_time_now() -> i64;
fn host_time_start_timer() -> u64;
fn host_time_elapsed(start: u64) -> u64;
fn host_random() -> f64;
fn host_console_write(level: u32, ptr: *const u8, len: u32);
fn host_console_clear();
pub fn host_regex_compile(
pattern_ptr: *const u8,
pattern_len: u32,
flags_ptr: *const u8,
flags_len: u32,
error_ptr_out: *mut u32,
error_len_out: *mut u32,
) -> u32;
pub fn host_regex_free(handle: u32);
pub fn host_regex_test(handle: u32, input_ptr: *const u8, input_len: u32) -> i32;
pub fn host_regex_exec(
handle: u32,
input_ptr: *const u8,
input_len: u32,
start_pos: u32,
match_start_out: *mut u32,
match_end_out: *mut u32,
captures_ptr_out: *mut u32,
captures_count_out: *mut u32,
) -> i32;
pub fn host_free_captures(ptr: u32, count: u32);
pub fn host_regex_replace(
handle: u32,
input_ptr: *const u8,
input_len: u32,
replacement_ptr: *const u8,
replacement_len: u32,
global: u32,
result_ptr_out: *mut u32,
result_len_out: *mut u32,
) -> i32;
pub fn host_regex_split(
handle: u32,
input_ptr: *const u8,
input_len: u32,
parts_ptr_out: *mut u32,
parts_count_out: *mut u32,
) -> i32;
pub fn host_free_split_result(parts_ptr: u32, parts_count: u32);
pub fn host_free_string(ptr: u32, len: u32);
}
pub struct WasmRawTimeProvider;
impl TimeProvider for WasmRawTimeProvider {
fn now_millis(&self) -> i64 {
unsafe { host_time_now() }
}
fn start_timer(&self) -> u64 {
unsafe { host_time_start_timer() }
}
fn elapsed_millis(&self, start: u64) -> u64 {
unsafe { host_time_elapsed(start) }
}
}
pub struct WasmRawRandomProvider;
impl RandomProvider for WasmRawRandomProvider {
fn random(&mut self) -> f64 {
unsafe { host_random() }
}
}
pub struct WasmRawConsoleProvider;
impl ConsoleProvider for WasmRawConsoleProvider {
fn write(&self, level: ConsoleLevel, message: &str) {
let level_num = match level {
ConsoleLevel::Log => 0,
ConsoleLevel::Info => 1,
ConsoleLevel::Debug => 2,
ConsoleLevel::Warn => 3,
ConsoleLevel::Error => 4,
};
unsafe {
host_console_write(level_num, message.as_ptr(), message.len() as u32);
}
}
fn clear(&self) {
unsafe {
host_console_clear();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn tsrun_alloc(size: u32) -> u32 {
if size == 0 {
return 0;
}
let Ok(layout) = Layout::from_size_align(size as usize, 8) else {
return 0;
};
let ptr = unsafe { alloc::alloc::alloc(layout) };
if ptr.is_null() { 0 } else { ptr as u32 }
}
#[unsafe(no_mangle)]
pub extern "C" fn tsrun_dealloc(ptr: u32, size: u32) {
if ptr == 0 || size == 0 {
return;
}
let Ok(layout) = Layout::from_size_align(size as usize, 8) else {
return;
};
unsafe {
alloc::alloc::dealloc(ptr as *mut u8, layout);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn tsrun_wasm_new() -> *mut TsRunContext {
let ctx = Box::new(TsRunContext::new());
let ctx_ptr = Box::into_raw(ctx);
let ctx_ref = unsafe { &mut *ctx_ptr };
ctx_ref
.interp
.set_time_provider(Box::new(WasmRawTimeProvider));
ctx_ref
.interp
.set_random_provider(Box::new(WasmRawRandomProvider));
ctx_ref.interp.set_console(Box::new(WasmRawConsoleProvider));
ctx_ref
.interp
.set_regexp_provider(Rc::new(WasmRegExpProvider));
ctx_ptr
}
#[unsafe(no_mangle)]
pub extern "C" fn tsrun_wasm_new_with_callback_console() -> *mut TsRunContext {
let ctx = Box::new(TsRunContext::new());
let ctx_ptr = Box::into_raw(ctx);
let ctx_ref = unsafe { &mut *ctx_ptr };
ctx_ref
.interp
.set_time_provider(Box::new(WasmRawTimeProvider));
ctx_ref
.interp
.set_random_provider(Box::new(WasmRawRandomProvider));
let provider = FfiConsoleProvider::new(ctx_ptr as *mut c_void);
ctx_ref.interp.set_console(Box::new(provider));
ctx_ptr
}