Skip to content

Commit 05f39f3

Browse files
andrieshiemstratheduke
authored andcommitted
Implement bytecode compilation.
1 parent e6e6fd6 commit 05f39f3

File tree

4 files changed

+299
-37
lines changed

4 files changed

+299
-37
lines changed

src/bindings.rs

+65-37
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ use crate::{
1515
droppable_value::DroppableValue,
1616
ContextError, ExecutionError, JsValue, ValueError,
1717
};
18+
use libquickjs_sys::JS_DupValue;
1819

1920
// JS_TAG_* constants from quickjs.
2021
// For some reason bindgen does not pick them up.
2122
#[cfg(feature = "bigint")]
2223
const TAG_BIG_INT: i64 = -10;
2324
const TAG_STRING: i64 = -7;
25+
const TAG_FUNCTION_BYTECODE: i64 = -2;
2426
const TAG_OBJECT: i64 = -1;
2527
const TAG_INT: i64 = 0;
2628
const TAG_BOOL: i64 = 1;
@@ -29,23 +31,6 @@ const TAG_UNDEFINED: i64 = 3;
2931
const TAG_EXCEPTION: i64 = 6;
3032
const TAG_FLOAT64: i64 = 7;
3133

32-
/// Free a JSValue.
33-
/// This function is the equivalent of JS_FreeValue from quickjs, which can not
34-
/// be used due to being `static inline`.
35-
unsafe fn free_value(context: *mut q::JSContext, value: q::JSValue) {
36-
// All tags < 0 are garbage collected and need to be freed.
37-
if value.tag < 0 {
38-
// This transmute is OK since if tag < 0, the union will be a refcount
39-
// pointer.
40-
let ptr = value.u.ptr as *mut q::JSRefCountHeader;
41-
let pref: &mut q::JSRefCountHeader = &mut *ptr;
42-
pref.ref_count -= 1;
43-
if pref.ref_count <= 0 {
44-
q::__JS_FreeValue(context, value);
45-
}
46-
}
47-
}
48-
4934
#[cfg(feature = "chrono")]
5035
fn js_date_constructor(context: *mut q::JSContext) -> q::JSValue {
5136
let global = unsafe { q::JS_GetGlobalObject(context) };
@@ -61,7 +46,7 @@ fn js_date_constructor(context: *mut q::JSContext) -> q::JSValue {
6146
)
6247
};
6348
assert_eq!(date_constructor.tag, TAG_OBJECT);
64-
unsafe { free_value(context, global) };
49+
unsafe { q::JS_FreeValue(context, global) };
6550
date_constructor
6651
}
6752

@@ -80,7 +65,7 @@ fn js_create_bigint_function(context: *mut q::JSContext) -> q::JSValue {
8065
)
8166
};
8267
assert_eq!(bigint_function.tag, TAG_OBJECT);
83-
unsafe { free_value(context, global) };
68+
unsafe { q::JS_FreeValue(context, global) };
8469
bigint_function
8570
}
8671

@@ -137,9 +122,11 @@ fn serialize_value(context: *mut q::JSContext, value: JsValue) -> Result<q::JSVa
137122
Err(e) => {
138123
// Make sure to free the array if a individual element
139124
// fails.
125+
140126
unsafe {
141-
free_value(context, arr);
127+
q::JS_FreeValue(context, arr);
142128
}
129+
143130
return Err(e);
144131
}
145132
};
@@ -157,7 +144,7 @@ fn serialize_value(context: *mut q::JSContext, value: JsValue) -> Result<q::JSVa
157144
// Make sure to free the array if a individual
158145
// element fails.
159146
unsafe {
160-
free_value(context, arr);
147+
q::JS_FreeValue(context, arr);
161148
}
162149
return Err(ValueError::Internal(
163150
"Could not append element to array".into(),
@@ -178,7 +165,7 @@ fn serialize_value(context: *mut q::JSContext, value: JsValue) -> Result<q::JSVa
178165
let qvalue = serialize_value(context, value).map_err(|e| {
179166
// Free the object if a property failed.
180167
unsafe {
181-
free_value(context, obj);
168+
q::JS_FreeValue(context, obj);
182169
}
183170
e
184171
})?;
@@ -195,7 +182,7 @@ fn serialize_value(context: *mut q::JSContext, value: JsValue) -> Result<q::JSVa
195182
if ret < 0 {
196183
// Free the object if a property failed.
197184
unsafe {
198-
free_value(context, obj);
185+
q::JS_FreeValue(context, obj);
199186
}
200187
return Err(ValueError::Internal(
201188
"Could not add add property to object".into(),
@@ -227,7 +214,7 @@ fn serialize_value(context: *mut q::JSContext, value: JsValue) -> Result<q::JSVa
227214
)
228215
};
229216
unsafe {
230-
free_value(context, date_constructor);
217+
q::JS_FreeValue(context, date_constructor);
231218
}
232219

233220
if value.tag != TAG_OBJECT {
@@ -250,7 +237,7 @@ fn serialize_value(context: *mut q::JSContext, value: JsValue) -> Result<q::JSVa
250237
)
251238
};
252239
let s = DroppableValue::new(s, |&mut s| unsafe {
253-
free_value(context, s);
240+
q::JS_FreeValue(context, s);
254241
});
255242
if (*s).tag != TAG_STRING {
256243
return Err(ValueError::Internal(
@@ -263,7 +250,7 @@ fn serialize_value(context: *mut q::JSContext, value: JsValue) -> Result<q::JSVa
263250
let bigint_function = js_create_bigint_function(context);
264251
let bigint_function =
265252
DroppableValue::new(bigint_function, |&mut bigint_function| unsafe {
266-
free_value(context, bigint_function);
253+
q::JS_FreeValue(context, bigint_function);
267254
});
268255
let js_bigint = unsafe {
269256
q::JS_Call(
@@ -300,7 +287,7 @@ fn deserialize_array(
300287
let len_raw = unsafe { q::JS_GetPropertyStr(context, *raw_value, length_name.as_ptr()) };
301288

302289
let len_res = deserialize_value(context, &len_raw);
303-
unsafe { free_value(context, len_raw) };
290+
unsafe { q::JS_FreeValue(context, len_raw) };
304291
let len = match len_res? {
305292
JsValue::Int(x) => x,
306293
_ => {
@@ -317,7 +304,7 @@ fn deserialize_array(
317304
return Err(ValueError::Internal("Could not build array".into()));
318305
}
319306
let value_res = deserialize_value(context, &value_raw);
320-
unsafe { free_value(context, value_raw) };
307+
unsafe { q::JS_FreeValue(context, value_raw) };
321308

322309
let value = value_res?;
323310
values.push(value);
@@ -364,7 +351,7 @@ fn deserialize_object(context: *mut q::JSContext, obj: &q::JSValue) -> Result<Js
364351

365352
let value_res = deserialize_value(context, &raw_value);
366353
unsafe {
367-
free_value(context, raw_value);
354+
q::JS_FreeValue(context, raw_value);
368355
}
369356
let value = value_res?;
370357

@@ -377,7 +364,7 @@ fn deserialize_object(context: *mut q::JSContext, obj: &q::JSValue) -> Result<Js
377364

378365
let key_res = deserialize_value(context, &key_value);
379366
unsafe {
380-
free_value(context, key_value);
367+
q::JS_FreeValue(context, key_value);
381368
}
382369
let key = match key_res? {
383370
JsValue::String(s) => s,
@@ -469,8 +456,8 @@ fn deserialize_value(
469456
unsafe { q::JS_Call(context, getter, *r, 0, std::ptr::null_mut()) };
470457

471458
unsafe {
472-
free_value(context, getter);
473-
free_value(context, date_constructor);
459+
q::JS_FreeValue(context, getter);
460+
q::JS_FreeValue(context, date_constructor);
474461
};
475462

476463
let res = if timestamp_raw.tag == TAG_FLOAT64 {
@@ -488,7 +475,7 @@ fn deserialize_value(
488475
};
489476
return res;
490477
} else {
491-
unsafe { free_value(context, date_constructor) };
478+
unsafe { q::JS_FreeValue(context, date_constructor) };
492479
}
493480
}
494481

@@ -597,11 +584,17 @@ pub struct OwnedValueRef<'a> {
597584
impl<'a> Drop for OwnedValueRef<'a> {
598585
fn drop(&mut self) {
599586
unsafe {
600-
free_value(self.context.context, self.value);
587+
q::JS_FreeValue(self.context.context, self.value);
601588
}
602589
}
603590
}
604591

592+
impl<'a> Clone for OwnedValueRef<'a> {
593+
fn clone(&self) -> Self {
594+
Self::new_dup(self.context, self.value)
595+
}
596+
}
597+
605598
impl<'a> std::fmt::Debug for OwnedValueRef<'a> {
606599
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
607600
match self.value.tag {
@@ -613,6 +606,7 @@ impl<'a> std::fmt::Debug for OwnedValueRef<'a> {
613606
TAG_FLOAT64 => write!(f, "Float(?)"),
614607
TAG_STRING => write!(f, "String(?)"),
615608
TAG_OBJECT => write!(f, "Object(?)"),
609+
TAG_FUNCTION_BYTECODE => write!(f, "Bytecode(?)"),
616610
_ => write!(f, "?"),
617611
}
618612
}
@@ -622,6 +616,11 @@ impl<'a> OwnedValueRef<'a> {
622616
pub fn new(context: &'a ContextWrapper, value: q::JSValue) -> Self {
623617
Self { context, value }
624618
}
619+
pub fn new_dup(context: &'a ContextWrapper, value: q::JSValue) -> Self {
620+
let ret = Self::new(context, value);
621+
unsafe { JS_DupValue(ret.context.context, ret.value) };
622+
ret
623+
}
625624

626625
/// Get the inner JSValue without freeing in drop.
627626
///
@@ -632,6 +631,18 @@ impl<'a> OwnedValueRef<'a> {
632631
v
633632
}
634633

634+
/// Get the inner JSValue without increasing ref count
635+
pub(crate) fn as_inner(&self) -> &q::JSValue {
636+
&self.value
637+
}
638+
639+
/// Get the inner JSValue while increasing ref count, this is handy when you pass a JSValue to a new owner like e.g. setProperty
640+
#[allow(dead_code)]
641+
pub(crate) fn as_inner_dup(&self) -> &q::JSValue {
642+
unsafe { q::JS_DupValue(self.context.context, self.value) };
643+
&self.value
644+
}
645+
635646
pub fn is_null(&self) -> bool {
636647
self.value.tag == TAG_NULL
637648
}
@@ -652,6 +663,10 @@ impl<'a> OwnedValueRef<'a> {
652663
self.value.tag == TAG_STRING
653664
}
654665

666+
pub fn is_compiled_function(&self) -> bool {
667+
self.value.tag == TAG_FUNCTION_BYTECODE
668+
}
669+
655670
pub fn to_string(&self) -> Result<String, ExecutionError> {
656671
let value = if self.is_string() {
657672
self.to_value()?
@@ -680,6 +695,19 @@ impl<'a> OwnedValueRef<'a> {
680695
_ => Err(ValueError::UnexpectedType),
681696
}
682697
}
698+
699+
#[cfg(test)]
700+
pub fn get_ref_count(&self) -> i32 {
701+
if self.value.tag < 0 {
702+
// This transmute is OK since if tag < 0, the union will be a refcount
703+
// pointer.
704+
let ptr = unsafe { self.value.u.ptr as *mut q::JSRefCountHeader };
705+
let pref: &mut q::JSRefCountHeader = &mut unsafe { *ptr };
706+
pref.ref_count
707+
} else {
708+
-1
709+
}
710+
}
683711
}
684712

685713
/// Wraps an object from the quickjs runtime.
@@ -709,7 +737,7 @@ impl<'a> OwnedObjectRef<'a> {
709737
};
710738
let t = raw.tag;
711739
unsafe {
712-
free_value(self.value.context.context, raw);
740+
q::JS_FreeValue(self.value.context.context, raw);
713741
}
714742
Ok(t)
715743
}
@@ -797,7 +825,7 @@ unsafe extern "C" fn native_module_init(
797825
/// Cleanup of the context happens in drop.
798826
pub struct ContextWrapper {
799827
runtime: *mut q::JSRuntime,
800-
context: *mut q::JSContext,
828+
pub(crate) context: *mut q::JSContext,
801829
/// Stores callback closures and quickjs data pointers.
802830
/// This array is write-only and only exists to ensure the lifetime of
803831
/// the closure.
@@ -937,7 +965,7 @@ impl ContextWrapper {
937965
}
938966

939967
/// Get the last exception from the runtime, and if present, convert it to a ExceptionError.
940-
fn get_exception(&self) -> Option<ExecutionError> {
968+
pub(crate) fn get_exception(&self) -> Option<ExecutionError> {
941969
let raw = unsafe { q::JS_GetException(self.context) };
942970
let value = OwnedValueRef::new(self, raw);
943971

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ pub mod console;
4040
mod droppable_value;
4141
mod value;
4242

43+
pub mod utils;
44+
4345
#[cfg(test)]
4446
mod tests;
4547

0 commit comments

Comments
 (0)