use crate::{context::Ctx, Error};
use rquickjs_sys as qjs;
use std::{ffi::CStr, ptr};
mod module;
pub use module::Module;
mod string;
pub use string::String;
mod object;
pub use object::Object;
mod array;
pub use array::Array;
mod convert;
pub use convert::*;
mod rf;
#[derive(Debug, Clone, PartialEq)]
pub enum Value<'js> {
Symbol,
String(String<'js>),
Object(Object<'js>),
Array(Array<'js>),
Int(i32),
Bool(bool),
Null,
Undefined,
Uninitialized,
Float(f64),
}
pub(crate) unsafe fn handle_exception<'js>(
ctx: Ctx<'js>,
js_val: qjs::JSValue,
) -> Result<qjs::JSValue, Error> {
if js_val.tag != qjs::JS_TAG_EXCEPTION as i64 {
return Ok(js_val);
}
Err(get_exception(ctx))
}
pub(crate) unsafe fn get_exception<'js>(ctx: Ctx<'js>) -> Error {
let exception_val = qjs::JS_GetException(ctx.ctx);
let is_error = qjs::JS_IsError(ctx.ctx, exception_val);
let s = qjs::JS_ToCString(ctx.ctx, exception_val);
if s == ptr::null_mut() {
return Error::Unknown;
}
let mut exception_text = CStr::from_ptr(s).to_string_lossy().into_owned();
qjs::JS_FreeCString(ctx.ctx, s);
if is_error == 1 {
let s_stack = CStr::from_bytes_with_nul(b"stack\0").unwrap();
let val = qjs::JS_GetPropertyStr(ctx.ctx, exception_val, s_stack.as_ptr());
if !qjs::JS_IsUndefined(val) {
let stack = qjs::JS_ToCString(ctx.ctx, val);
let text = match CStr::from_ptr(stack).to_str() {
Err(e) => return e.into(),
Ok(x) => x,
};
exception_text = format!("{}\n{}", exception_text, text);
qjs::JS_FreeCString(ctx.ctx, stack);
}
}
qjs::JS_FreeValue(ctx.ctx, exception_val);
Error::Exception(exception_text)
}
impl<'js> Value<'js> {
pub(crate) unsafe fn from_js_value(ctx: Ctx<'js>, v: qjs::JSValue) -> Result<Self, Error> {
let v = handle_exception(ctx, v)?;
match v.tag as i32 {
qjs::JS_TAG_INT => Ok(Value::Int(qjs::JS_VALUE_GET_INT!(v))),
qjs::JS_TAG_BOOL => Ok(Value::Bool(qjs::JS_VALUE_GET_BOOL!(v) != 0)),
qjs::JS_TAG_NULL => Ok(Value::Null),
qjs::JS_TAG_UNDEFINED => Ok(Value::Undefined),
qjs::JS_TAG_UNINITIALIZED => Ok(Value::Uninitialized),
qjs::JS_TAG_FLOAT64 => Ok(Value::Float(qjs::JS_VALUE_GET_FLOAT64!(v))),
qjs::JS_TAG_STRING => Ok(Value::String(String::new(ctx, v))),
qjs::JS_TAG_SYMBOL => {
println!("{:?}", v.u.ptr);
Ok(Value::Symbol)
}
qjs::JS_TAG_OBJECT => {
if qjs::JS_IsArray(ctx.ctx, v) == 1 {
Ok(Value::Array(Array::new(ctx, v)))
} else {
Ok(Value::Object(Object::new(ctx, v)))
}
}
_ => panic!("got unmatched js value type tag"),
}
}
pub(crate) fn to_js_value(&self) -> qjs::JSValue {
match *self {
Value::Int(ref x) => qjs::JSValue {
u: qjs::JSValueUnion { int32: *x },
tag: qjs::JS_TAG_INT as i64,
},
Value::Bool(ref x) => qjs::JSValue {
u: qjs::JSValueUnion {
int32: if *x { 1 } else { 0 },
},
tag: qjs::JS_TAG_BOOL as i64,
},
Value::Null => qjs::JSValue {
u: qjs::JSValueUnion { int32: 0 },
tag: qjs::JS_TAG_NULL as i64,
},
Value::Undefined => qjs::JSValue {
u: qjs::JSValueUnion { int32: 0 },
tag: qjs::JS_TAG_UNDEFINED as i64,
},
Value::Uninitialized => qjs::JSValue {
u: qjs::JSValueUnion { int32: 0 },
tag: qjs::JS_TAG_UNINITIALIZED as i64,
},
Value::Float(ref x) => qjs::JSValue {
u: qjs::JSValueUnion { float64: *x },
tag: qjs::JS_TAG_FLOAT64 as i64,
},
Value::Symbol => panic!(),
Value::String(ref x) => x.to_js_value(),
Value::Object(ref x) => x.to_js_value(),
Value::Array(ref x) => x.to_js_value(),
}
}
pub(crate) fn type_name(&self) -> &'static str {
match *self {
Value::Int(_) => "integer",
Value::Bool(_) => "bool",
Value::Null => "null",
Value::Undefined => "undefined",
Value::Uninitialized => "uninitialized",
Value::Float(_) => "float",
Value::Symbol => "symbol",
Value::String(_) => "string",
Value::Object(_) => "object",
Value::Array(_) => "array",
}
}
}