extern crate alloc;
pub(crate) mod console;
mod context;
mod module;
mod native;
mod order;
mod regexp;
mod value;
use alloc::boxed::Box;
use alloc::ffi::CString;
use alloc::string::String;
use alloc::vec::Vec;
use core::ffi::{CStr, c_char, c_void};
use core::ptr;
use crate::prelude::FxHashMap;
use crate::value::CheapClone;
use crate::{Interpreter, JsValue, RuntimeValue};
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
#[unsafe(no_mangle)]
pub extern "C" fn tsrun_version() -> *const c_char {
VERSION.as_ptr() as *const c_char
}
pub struct TsRunContext {
pub(crate) interp: Interpreter,
pub(crate) last_error: Option<CString>,
pub(crate) native_callbacks: FxHashMap<usize, NativeCallbackWrapper>,
pub(crate) next_ffi_id: usize,
pub(crate) console_callback: Option<ConsoleCallbackWrapper>,
}
impl TsRunContext {
pub(crate) fn new() -> Self {
Self {
interp: Interpreter::new(),
last_error: None,
native_callbacks: FxHashMap::default(),
next_ffi_id: 1, console_callback: None,
}
}
pub(crate) fn set_error(&mut self, error: String) -> *const c_char {
match CString::new(error) {
Ok(c_str) => {
self.last_error = Some(c_str);
self.last_error.as_ref().map_or(ptr::null(), |s| s.as_ptr())
}
Err(_) => {
self.last_error = Some(unsafe {
CString::from_vec_unchecked(b"Error: null byte in message".to_vec())
});
self.last_error.as_ref().map_or(ptr::null(), |s| s.as_ptr())
}
}
}
pub(crate) fn clear_error(&mut self) {
self.last_error = None;
}
}
pub struct TsRunValue {
pub(crate) inner: RuntimeValue,
}
impl TsRunValue {
pub(crate) fn from_runtime_value(rv: RuntimeValue) -> Box<Self> {
Box::new(Self { inner: rv })
}
pub(crate) fn from_js_value(interp: &mut Interpreter, value: JsValue) -> Box<Self> {
if let JsValue::Object(ref obj) = value {
let guard = interp.heap.create_guard();
guard.guard(obj.cheap_clone());
Box::new(Self {
inner: RuntimeValue::with_guard(value, guard),
})
} else {
Box::new(Self {
inner: RuntimeValue::unguarded(value),
})
}
}
pub(crate) fn value(&self) -> &JsValue {
self.inner.value()
}
}
pub(crate) struct NativeCallbackWrapper {
pub callback: TsRunNativeFn,
pub userdata: *mut c_void,
}
#[repr(C)]
pub struct TsRunValueResult {
pub value: *mut TsRunValue,
pub error: *const c_char,
}
impl TsRunValueResult {
pub(crate) fn ok(value: Box<TsRunValue>) -> Self {
Self {
value: Box::into_raw(value),
error: ptr::null(),
}
}
pub(crate) fn err(ctx: &mut TsRunContext, error: String) -> Self {
Self {
value: ptr::null_mut(),
error: ctx.set_error(error),
}
}
}
#[repr(C)]
pub struct TsRunResult {
pub ok: bool,
pub error: *const c_char,
}
impl TsRunResult {
pub(crate) fn success() -> Self {
Self {
ok: true,
error: ptr::null(),
}
}
pub(crate) fn err(ctx: &mut TsRunContext, error: String) -> Self {
Self {
ok: false,
error: ctx.set_error(error),
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TsRunType {
Undefined = 0,
Null = 1,
Boolean = 2,
Number = 3,
String = 4,
Object = 5,
Symbol = 6,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TsRunStepStatus {
Continue = 0,
Complete = 1,
NeedImports = 2,
Suspended = 3,
Done = 4,
Error = 5,
}
#[repr(C)]
pub struct TsRunImportRequest {
pub specifier: *const c_char,
pub resolved_path: *const c_char,
pub importer: *const c_char,
}
#[repr(C)]
pub struct TsRunOrder {
pub id: u64,
pub payload: *mut TsRunValue,
}
#[repr(C)]
pub struct TsRunStepResult {
pub status: TsRunStepStatus,
pub value: *mut TsRunValue,
pub imports: *mut TsRunImportRequest,
pub import_count: usize,
pub pending_orders: *mut TsRunOrder,
pub pending_count: usize,
pub cancelled_orders: *mut u64,
pub cancelled_count: usize,
pub error: *const c_char,
}
impl Default for TsRunStepResult {
fn default() -> Self {
Self {
status: TsRunStepStatus::Done,
value: ptr::null_mut(),
imports: ptr::null_mut(),
import_count: 0,
pending_orders: ptr::null_mut(),
pending_count: 0,
cancelled_orders: ptr::null_mut(),
cancelled_count: 0,
error: ptr::null(),
}
}
}
#[repr(C)]
pub struct TsRunOrderResponse {
pub id: u64,
pub value: *mut TsRunValue,
pub error: *const c_char,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TsRunConsoleLevel {
Log = 0,
Info = 1,
Debug = 2,
Warn = 3,
Error = 4,
Clear = 5,
}
pub type TsRunConsoleFn = extern "C" fn(
level: TsRunConsoleLevel,
message: *const c_char,
message_len: usize,
userdata: *mut c_void,
);
pub(crate) struct ConsoleCallbackWrapper {
pub callback: TsRunConsoleFn,
pub userdata: *mut c_void,
}
pub type TsRunNativeFn = extern "C" fn(
ctx: *mut TsRunContext,
this_arg: *mut TsRunValue,
args: *mut *mut TsRunValue,
argc: usize,
userdata: *mut c_void,
error_out: *mut *const c_char,
) -> *mut TsRunValue;
#[repr(C)]
pub struct TsRunGcStats {
pub total_objects: usize,
pub pooled_objects: usize,
pub live_objects: usize,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tsrun_free_string(s: *mut c_char) {
if !s.is_null() {
unsafe { drop(CString::from_raw(s)) };
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tsrun_free_strings(strings: *mut *mut c_char, count: usize) {
if strings.is_null() {
return;
}
unsafe {
for i in 0..count {
let s = *strings.add(i);
if !s.is_null() {
drop(CString::from_raw(s));
}
}
drop(Vec::from_raw_parts(strings, count, count));
}
}
pub(crate) unsafe fn c_str_to_str<'a>(s: *const c_char) -> Option<&'a str> {
if s.is_null() {
None
} else {
unsafe { CStr::from_ptr(s) }.to_str().ok()
}
}
pub(crate) fn str_to_c_string(s: &str) -> *mut c_char {
match CString::new(s) {
Ok(c_str) => c_str.into_raw(),
Err(_) => ptr::null_mut(),
}
}