mod internal;
mod closure;
pub use crate::internal::JSString;
pub use rusty_jsc_macros::callback;
pub use rusty_jsc_sys::JSObjectCallAsFunctionCallback;
use rusty_jsc_sys::*;
use std::fmt;
pub mod private {
pub use rusty_jsc_sys::*;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JSValue {
inner: JSValueRef,
}
impl Drop for JSValue {
fn drop(&mut self) {
}
}
impl JSValue {
fn from(inner: JSValueRef) -> Self {
Self { inner }
}
pub fn undefined(context: &JSContext) -> JSValue {
JSValue::from(unsafe { JSValueMakeUndefined(context.inner) })
}
pub fn null(context: &JSContext) -> JSValue {
JSValue::from(unsafe { JSValueMakeNull(context.inner) })
}
pub fn boolean(context: &JSContext, value: bool) -> JSValue {
JSValue::from(unsafe { JSValueMakeBoolean(context.inner, value) })
}
pub fn number(context: &JSContext, value: f64) -> JSValue {
JSValue::from(unsafe { JSValueMakeNumber(context.inner, value) })
}
pub fn string(context: &JSContext, value: impl Into<JSString>) -> JSValue {
let value = value.into();
JSValue::from(unsafe { JSValueMakeString(context.inner, value.inner) })
}
pub fn callback(context: &JSContext, callback: JSObjectCallAsFunctionCallback) -> JSValue {
let name = JSString::from_utf8("".to_string());
let func = unsafe { JSObjectMakeFunctionWithCallback(context.inner, name.inner, callback) };
JSValue::from(func)
}
pub fn is_undefined(&self, context: &JSContext) -> bool {
unsafe { JSValueIsUndefined(context.inner, self.inner) }
}
pub fn is_null(&self, context: &JSContext) -> bool {
unsafe { JSValueIsNull(context.inner, self.inner) }
}
pub fn is_boolean(&self, context: &JSContext) -> bool {
unsafe { JSValueIsBoolean(context.inner, self.inner) }
}
pub fn is_array(&self, context: &JSContext) -> bool {
unsafe { JSValueIsArray(context.inner, self.inner) }
}
pub fn is_number(&self, context: &JSContext) -> bool {
unsafe { JSValueIsNumber(context.inner, self.inner) }
}
pub fn is_string(&self, context: &JSContext) -> bool {
unsafe { JSValueIsString(context.inner, self.inner) }
}
pub fn to_bool(&self, context: &JSContext) -> bool {
unsafe { JSValueToBoolean(context.inner, self.inner) }
}
pub fn to_string(&self, context: &JSContext) -> Result<JSString, JSValue> {
let mut exception: JSValueRef = std::ptr::null_mut();
let string = unsafe { JSValueToStringCopy(context.inner, self.inner, &mut exception) };
if !exception.is_null() {
return Err(JSValue::from(exception));
}
Ok(JSString::from(string))
}
pub fn to_number(&self, context: &JSContext) -> Result<f64, JSValue> {
let mut exception: JSValueRef = std::ptr::null_mut();
let num = unsafe { JSValueToNumber(context.inner, self.inner, &mut exception) };
if !exception.is_null() {
return Err(JSValue::from(exception));
}
Ok(num)
}
pub fn to_object(&self, context: &JSContext) -> Result<JSObject, JSValue> {
let mut exception: JSValueRef = std::ptr::null_mut();
let object_ref = unsafe { JSValueToObject(context.inner, self.inner, &mut exception) };
if !exception.is_null() {
return Err(JSValue::from(exception));
}
let obj = JSObject::from(object_ref);
Ok(obj)
}
}
unsafe impl Send for JSValue {}
unsafe impl Sync for JSValue {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JSObject {
inner: JSObjectRef,
}
unsafe impl Send for JSObject {}
unsafe impl Sync for JSObject {}
impl Drop for JSObject {
fn drop(&mut self) {
}
}
impl JSObject {
fn from(inner: JSObjectRef) -> Self {
Self { inner }
}
pub fn new(context: &JSContext) -> Self {
let null = std::ptr::null_mut();
let o_ref = unsafe { JSObjectMake(context.inner, null, null as _) };
Self::from(o_ref)
}
pub fn new_array(context: &JSContext, args: &[JSValue]) -> Result<Self, JSValue> {
let args_refs = args.iter().map(|arg| arg.inner).collect::<Vec<_>>();
let mut exception: JSValueRef = std::ptr::null_mut();
let o_ref = unsafe {
JSObjectMakeArray(
context.inner,
args.len() as _,
args_refs.as_slice().as_ptr(),
&mut exception,
)
};
if !exception.is_null() {
return Err(JSValue::from(exception));
}
Ok(Self::from(o_ref))
}
pub fn new_function_with_callback(
context: &JSContext,
name: impl Into<JSString>,
callback: JSObjectCallAsFunctionCallback,
) -> Self {
let name = name.into();
let o_ref =
unsafe { JSObjectMakeFunctionWithCallback(context.inner, name.inner, callback) };
Self::from(o_ref)
}
pub fn construct(&self, context: &JSContext, args: &[JSValue]) -> Result<Self, JSValue> {
let args_refs = args.iter().map(|arg| arg.inner).collect::<Vec<_>>();
let mut exception: JSValueRef = std::ptr::null_mut();
let result = unsafe {
JSObjectCallAsConstructor(
context.inner,
self.inner,
args.len() as _,
args_refs.as_slice().as_ptr(),
&mut exception,
)
};
if !exception.is_null() {
return Err(JSValue::from(exception));
}
if result.is_null() {
return Err(JSValue::string(
context,
format!(
"Can't call constructor for {:?}: not a valid constructor",
self.to_jsvalue().to_string(context)
),
));
}
Ok(Self::from(result))
}
pub fn call(
&self,
context: &JSContext,
this: Option<&JSObject>,
args: &[JSValue],
) -> Result<JSValue, JSValue> {
let args_refs = args.iter().map(|arg| arg.inner).collect::<Vec<_>>();
let mut exception: JSValueRef = std::ptr::null_mut();
let result = unsafe {
JSObjectCallAsFunction(
context.inner,
self.inner,
this.map(|t| t.inner)
.unwrap_or_else(|| std::ptr::null_mut()),
args.len() as _,
args_refs.as_slice().as_ptr(),
&mut exception,
)
};
if !exception.is_null() {
return Err(JSValue::from(exception));
}
if result.is_null() {
return Err(JSValue::string(
context,
format!(
"Can't call the object {:?}: not a valid function",
self.to_jsvalue().to_string(context)
),
));
}
Ok(JSValue::from(result))
}
pub fn to_jsvalue(&self) -> JSValue {
JSValue::from(self.inner)
}
pub fn create_typed_array_with_bytes(
context: &JSContext,
bytes: &mut [u8],
) -> Result<Self, JSValue> {
let deallocator_ctx = std::ptr::null_mut();
let mut exception: JSValueRef = std::ptr::null_mut();
let result = unsafe {
JSObjectMakeTypedArrayWithBytesNoCopy(
context.inner,
JSTypedArrayType_kJSTypedArrayTypeUint8Array,
bytes.as_mut_ptr() as _,
bytes.len() as _,
None,
deallocator_ctx,
&mut exception,
)
};
if !exception.is_null() {
return Err(JSValue::from(exception));
}
if result.is_null() {
return Err(JSValue::string(
context,
"Can't create a type array".to_string(),
));
}
Ok(Self::from(result))
}
pub fn create_typed_array_from_buffer(
context: &JSContext,
buffer: JSObject,
) -> Result<Self, JSValue> {
let mut exception: JSValueRef = std::ptr::null_mut();
let result = unsafe {
JSObjectMakeTypedArrayWithArrayBuffer(
context.inner,
JSTypedArrayType_kJSTypedArrayTypeUint8Array,
buffer.inner,
&mut exception,
)
};
if !exception.is_null() {
return Err(JSValue::from(exception));
}
if result.is_null() {
return Err(JSValue::string(
context,
"Can't create a typed array from the provided buffer".to_string(),
));
}
Ok(Self::from(result))
}
pub fn get_typed_array_buffer(&self, context: &JSContext) -> Result<&mut [u8], JSValue> {
let mut exception: JSValueRef = std::ptr::null_mut();
let arr_ptr =
unsafe { JSObjectGetTypedArrayBytesPtr(context.inner, self.inner, &mut exception) };
let arr_len =
unsafe { JSObjectGetTypedArrayLength(context.inner, self.inner, &mut exception) };
if !exception.is_null() {
return Err(JSValue::from(exception));
}
let slice = unsafe { std::slice::from_raw_parts_mut(arr_ptr as _, arr_len as usize) };
Ok(slice)
}
pub fn get_property(&self, context: &JSContext, property_name: impl Into<JSString>) -> JSValue {
let property_name = property_name.into();
let mut exception: JSValueRef = std::ptr::null_mut();
let jsvalue_ref = unsafe {
JSObjectGetProperty(
context.inner,
self.inner,
property_name.inner,
&mut exception,
)
};
JSValue::from(jsvalue_ref)
}
pub fn get_property_at_index(
&self,
context: &JSContext,
property_index: u32,
) -> Result<JSValue, JSValue> {
let mut exception: JSValueRef = std::ptr::null_mut();
let property = unsafe {
JSObjectGetPropertyAtIndex(context.inner, self.inner, property_index, &mut exception)
};
if !exception.is_null() {
return Err(JSValue::from(exception));
}
Ok(JSValue::from(property))
}
pub fn get_property_names(&mut self, context: &JSContext) -> Vec<String> {
let property_name_array = unsafe { JSObjectCopyPropertyNames(context.inner, self.inner) };
let num_properties = unsafe { JSPropertyNameArrayGetCount(property_name_array) };
(0..num_properties)
.map(|property_index| {
JSString::from(unsafe {
JSPropertyNameArrayGetNameAtIndex(property_name_array, property_index)
})
.to_string()
})
.collect::<Vec<_>>()
}
pub fn get_array_buffer(&mut self, context: &JSContext) -> Result<&mut [u8], JSValue> {
let mut exception: JSValueRef = std::ptr::null_mut();
let arr_ptr =
unsafe { JSObjectGetArrayBufferBytesPtr(context.inner, self.inner, &mut exception) };
if !exception.is_null() {
return Err(JSValue::from(exception));
}
let arr_len =
unsafe { JSObjectGetArrayBufferByteLength(context.inner, self.inner, &mut exception) };
if !exception.is_null() {
return Err(JSValue::from(exception));
}
let slice = unsafe { std::slice::from_raw_parts_mut(arr_ptr as _, arr_len as usize) };
Ok(slice)
}
pub fn set_property(
&self,
context: &JSContext,
property_name: impl Into<JSString>,
value: JSValue,
) -> Result<(), JSValue> {
let property_name = property_name.into();
let attributes = 0; let mut exception: JSValueRef = std::ptr::null_mut();
unsafe {
JSObjectSetProperty(
context.inner,
self.inner,
property_name.inner,
value.inner,
attributes,
&mut exception,
)
}
if !exception.is_null() {
return Err(JSValue::from(exception));
}
Ok(())
}
pub fn set_property_at_index(
&self,
context: &JSContext,
index: u32,
value: JSValue,
) -> Result<(), JSValue> {
let mut exception: JSValueRef = std::ptr::null_mut();
unsafe {
JSObjectSetPropertyAtIndex(
context.inner,
self.inner,
index,
value.inner,
&mut exception,
)
}
if !exception.is_null() {
return Err(JSValue::from(exception));
}
Ok(())
}
}
impl From<JSObjectRef> for JSObject {
fn from(obj: JSObjectRef) -> Self {
JSObject::from(obj)
}
}
impl From<JSValueRef> for JSValue {
fn from(val: JSValueRef) -> Self {
JSValue::from(val)
}
}
impl From<JSValue> for JSValueRef {
fn from(val: JSValue) -> Self {
val.inner
}
}
impl From<JSObject> for JSObjectRef {
fn from(val: JSObject) -> JSObjectRef {
val.inner
}
}
pub struct JSVirtualMachine {
context_group: JSContextGroupRef,
global_context: JSGlobalContextRef,
}
impl Drop for JSVirtualMachine {
fn drop(&mut self) {
unsafe {
JSGlobalContextRelease(self.global_context);
JSContextGroupRelease(self.context_group);
}
}
}
impl JSVirtualMachine {
fn from(context: JSContextRef) -> Self {
let global_context = unsafe { JSContextGetGlobalContext(context) };
unsafe {
JSGlobalContextRetain(global_context);
}
let context_group = unsafe { JSContextGetGroup(global_context) };
unsafe {
JSContextGroupRetain(context_group);
}
Self {
context_group,
global_context,
}
}
fn new() -> Self {
let context_group = unsafe { JSContextGroupCreate() };
let global_context =
unsafe { JSGlobalContextCreateInGroup(context_group, std::ptr::null_mut()) };
Self {
context_group,
global_context,
}
}
}
pub struct JSContext {
inner: JSContextRef,
vm: JSVirtualMachine,
}
impl fmt::Debug for JSContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("JSContext").finish()
}
}
impl Default for JSContext {
fn default() -> Self {
JSContext::new()
}
}
impl JSContext {
pub fn from(ctx: JSContextRef) -> Self {
let vm = JSVirtualMachine::from(ctx);
Self { inner: ctx, vm }
}
pub fn new() -> Self {
let vm = JSVirtualMachine::new();
Self {
inner: vm.global_context,
vm,
}
}
pub fn with_virtual_machine(vm: JSVirtualMachine) -> Self {
Self {
inner: vm.global_context,
vm,
}
}
pub fn get_global_object(&self) -> JSObject {
JSObject::from(unsafe { JSContextGetGlobalObject(self.inner) })
}
pub fn evaluate_script(
&mut self,
script: &str,
starting_line_number: i32,
) -> Result<JSValue, JSValue> {
let script = JSString::from_utf8(script.to_string());
let this_object = std::ptr::null_mut();
let source_url = std::ptr::null_mut();
let mut exception: JSValueRef = std::ptr::null_mut();
let value = unsafe {
JSEvaluateScript(
self.vm.global_context,
script.inner,
this_object,
source_url,
starting_line_number,
&mut exception,
)
};
if !exception.is_null() {
return Err(JSValue::from(exception));
}
Ok(JSValue::from(value))
}
}