1use super::{Value, evaluate_script, utf16_to_utf8};
2use std::ffi::c_void;
3
4#[repr(C)]
5#[derive(Copy, Clone)]
6pub union JSValueUnion {
7 pub int32: i32,
8 pub float64: f64,
9 pub ptr: *mut c_void,
10 pub short_big_int: i64,
11}
12
13#[repr(C)]
14#[derive(Copy, Clone)]
15pub struct JSValue {
16 pub u: JSValueUnion,
17 pub tag: i64,
18}
19
20pub const JS_TAG_FIRST: i32 = -9;
21pub const JS_TAG_BIG_INT: i32 = -9;
22pub const JS_TAG_SYMBOL: i32 = -8;
23pub const JS_TAG_STRING: i32 = -7;
24pub const JS_TAG_STRING_ROPE: i32 = -6;
25pub const JS_TAG_MODULE: i32 = -3;
26pub const JS_TAG_FUNCTION_BYTECODE: i32 = -2;
27pub const JS_TAG_OBJECT: i32 = -1;
28
29pub const JS_TAG_INT: i32 = 0;
30pub const JS_TAG_BOOL: i32 = 1;
31pub const JS_TAG_NULL: i32 = 2;
32pub const JS_TAG_UNDEFINED: i32 = 3;
33pub const JS_TAG_UNINITIALIZED: i32 = 4;
34pub const JS_TAG_CATCH_OFFSET: i32 = 5;
35pub const JS_TAG_EXCEPTION: i32 = 6;
36pub const JS_TAG_SHORT_BIG_INT: i32 = 7;
37pub const JS_TAG_FLOAT64: i32 = 8;
38
39pub const JS_FLOAT64_NAN: f64 = f64::NAN;
40
41impl JSValue {
42 pub fn new_int32(val: i32) -> JSValue {
43 JSValue {
44 u: JSValueUnion { int32: val },
45 tag: JS_TAG_INT as i64,
46 }
47 }
48
49 pub fn new_bool(val: bool) -> JSValue {
50 JSValue {
51 u: JSValueUnion {
52 int32: if val { 1 } else { 0 },
53 },
54 tag: JS_TAG_BOOL as i64,
55 }
56 }
57
58 pub fn new_float64(val: f64) -> JSValue {
59 JSValue {
60 u: JSValueUnion { float64: val },
61 tag: JS_TAG_FLOAT64 as i64,
62 }
63 }
64
65 pub fn new_ptr(tag: i32, ptr: *mut c_void) -> JSValue {
66 JSValue {
67 u: JSValueUnion { ptr },
68 tag: tag as i64,
69 }
70 }
71
72 pub fn has_ref_count(&self) -> bool {
73 let t = self.tag as i32;
74 (JS_TAG_FIRST..=JS_TAG_OBJECT).contains(&t)
75 }
76
77 pub fn get_ptr(&self) -> *mut c_void {
78 unsafe { self.u.ptr }
79 }
80
81 pub fn get_tag(&self) -> i32 {
82 self.tag as i32
83 }
84}
85
86pub const JS_NULL: JSValue = JSValue {
87 u: JSValueUnion { int32: 0 },
88 tag: JS_TAG_NULL as i64,
89};
90
91pub const JS_UNDEFINED: JSValue = JSValue {
92 u: JSValueUnion { int32: 0 },
93 tag: JS_TAG_UNDEFINED as i64,
94};
95
96pub const JS_FALSE: JSValue = JSValue {
97 u: JSValueUnion { int32: 0 },
98 tag: JS_TAG_BOOL as i64,
99};
100
101pub const JS_TRUE: JSValue = JSValue {
102 u: JSValueUnion { int32: 1 },
103 tag: JS_TAG_BOOL as i64,
104};
105
106pub const JS_EXCEPTION: JSValue = JSValue {
107 u: JSValueUnion { int32: 0 },
108 tag: JS_TAG_EXCEPTION as i64,
109};
110
111pub const JS_UNINITIALIZED: JSValue = JSValue {
112 u: JSValueUnion { int32: 0 },
113 tag: JS_TAG_UNINITIALIZED as i64,
114};
115
116#[repr(C)]
117pub struct list_head {
118 pub prev: *mut list_head,
119 pub next: *mut list_head,
120}
121
122impl list_head {
123 pub unsafe fn init(&mut self) {
126 self.prev = self;
127 self.next = self;
128 }
129
130 pub unsafe fn add_tail(&mut self, new_entry: *mut list_head) {
134 unsafe {
135 let prev = self.prev;
136 (*new_entry).next = self;
137 (*new_entry).prev = prev;
138 (*prev).next = new_entry;
139 self.prev = new_entry;
140 }
141 }
142
143 pub unsafe fn del(&mut self) {
146 unsafe {
147 let next = self.next;
148 let prev = self.prev;
149 (*next).prev = prev;
150 (*prev).next = next;
151 self.next = std::ptr::null_mut();
152 self.prev = std::ptr::null_mut();
153 }
154 }
155}
156
157#[repr(C)]
158pub struct JSMallocState {
159 pub malloc_count: usize,
160 pub malloc_size: usize,
161 pub malloc_limit: usize,
162 pub opaque: *mut c_void,
163}
164
165#[repr(C)]
166pub struct JSMallocFunctions {
167 pub js_malloc: Option<unsafe extern "C" fn(*mut JSMallocState, usize) -> *mut c_void>,
168 pub js_free: Option<unsafe extern "C" fn(*mut JSMallocState, *mut c_void)>,
169 pub js_realloc: Option<unsafe extern "C" fn(*mut JSMallocState, *mut c_void, usize) -> *mut c_void>,
170 pub js_malloc_usable_size: Option<unsafe extern "C" fn(*const c_void) -> usize>,
171}
172
173pub type JSAtom = u32;
174
175#[repr(C)]
176pub struct JSRefCountHeader {
177 pub ref_count: i32,
178}
179
180#[repr(C)]
181pub struct JSString {
182 pub header: JSRefCountHeader,
183 pub len: u32, pub hash: u32, pub hash_next: u32,
186 }
188
189pub type JSAtomStruct = JSString;
190
191#[repr(C)]
192pub struct JSClass {
193 pub class_id: u32,
194 pub class_name: JSAtom,
195 pub finalizer: *mut c_void, pub gc_mark: *mut c_void, pub call: *mut c_void, pub exotic: *mut c_void, }
200
201#[repr(C)]
202pub struct JSRuntime {
203 pub mf: JSMallocFunctions,
204 pub malloc_state: JSMallocState,
205 pub rt_info: *const i8,
206
207 pub atom_hash_size: i32,
208 pub atom_count: i32,
209 pub atom_size: i32,
210 pub atom_count_resize: i32,
211 pub atom_hash: *mut u32,
212 pub atom_array: *mut *mut JSAtomStruct,
213 pub atom_free_index: i32,
214
215 pub class_count: i32,
216 pub class_array: *mut JSClass,
217
218 pub context_list: list_head,
219 pub gc_obj_list: list_head,
220 pub gc_zero_ref_count_list: list_head,
221 pub tmp_obj_list: list_head,
222 pub gc_phase: u8,
223 pub malloc_gc_threshold: usize,
224 pub weakref_list: list_head,
225
226 pub shape_hash_bits: i32,
227 pub shape_hash_size: i32,
228 pub shape_hash_count: i32,
229 pub shape_hash: *mut *mut JSShape,
230 pub user_opaque: *mut c_void,
231}
232
233#[repr(C)]
234pub struct JSGCObjectHeader {
235 pub ref_count: i32,
236 pub gc_obj_type: u8, pub mark: u8, pub dummy0: u8, pub dummy1: u8,
240 pub dummy2: u16,
241 pub link: list_head,
242}
243
244#[repr(C)]
245pub struct JSShape {
246 pub header: JSGCObjectHeader,
247 pub is_hashed: u8,
248 pub has_small_array_index: u8,
249 pub hash: u32,
250 pub prop_hash_mask: u32,
251 pub prop_size: i32,
252 pub prop_count: i32,
253 pub deleted_prop_count: i32,
254 pub prop: *mut JSShapeProperty,
255 pub prop_hash: *mut u32,
256 pub first_object: *mut JSObject,
259 pub proto: *mut JSObject,
260}
261
262#[repr(C)]
263pub struct JSContext {
264 pub header: JSGCObjectHeader,
265 pub rt: *mut JSRuntime,
266 pub link: list_head,
267
268 pub binary_object_count: u16,
269 pub binary_object_size: i32,
270 pub std_array_prototype: u8,
271
272 pub array_shape: *mut JSShape,
273 pub arguments_shape: *mut JSShape,
274 pub mapped_arguments_shape: *mut JSShape,
275 pub regexp_shape: *mut JSShape,
276 pub regexp_result_shape: *mut JSShape,
277
278 pub class_proto: *mut JSValue,
279 pub function_proto: JSValue,
280 pub function_ctor: JSValue,
281 pub array_ctor: JSValue,
282 pub regexp_ctor: JSValue,
283 pub promise_ctor: JSValue,
284 pub native_error_proto: [JSValue; 8], pub iterator_ctor: JSValue,
286 pub async_iterator_proto: JSValue,
287 pub array_proto_values: JSValue,
288 pub throw_type_error: JSValue,
289 pub eval_obj: JSValue,
290
291 pub global_obj: JSValue,
292 pub global_var_obj: JSValue,
293
294 pub random_state: u64,
295 pub interrupt_counter: i32,
296
297 pub loaded_modules: list_head,
298
299 pub compile_regexp: Option<unsafe extern "C" fn(*mut JSContext, JSValue, JSValue) -> JSValue>,
300 pub eval_internal: Option<unsafe extern "C" fn(*mut JSContext, JSValue, *const i8, usize, *const i8, i32, i32) -> JSValue>,
301 pub user_opaque: *mut c_void,
302}
303
304#[repr(C)]
305pub struct JSFunctionBytecode {
306 pub header: JSGCObjectHeader,
307 pub js_mode: u8,
308 pub flags: u16, pub byte_code_buf: *mut u8,
310 pub byte_code_len: i32,
311 pub func_name: JSAtom,
312 pub vardefs: *mut c_void, pub closure_var: *mut c_void, pub arg_count: u16,
315 pub var_count: u16,
316 pub defined_arg_count: u16,
317 pub stack_size: u16,
318 pub var_ref_count: u16,
319 pub realm: *mut JSContext,
320 pub cpool: *mut JSValue,
321 pub cpool_count: i32,
322 pub closure_var_count: i32,
323 pub filename: JSAtom,
325 pub source_len: i32,
326 pub pc2line_len: i32,
327 pub pc2line_buf: *mut u8,
328 pub source: *mut i8,
329}
330
331#[repr(C)]
332pub struct JSStackFrame {
333 pub prev_frame: *mut JSStackFrame,
334 pub cur_func: JSValue,
335 pub arg_buf: *mut JSValue,
336 pub var_buf: *mut JSValue,
337 pub var_refs: *mut *mut c_void, pub cur_pc: *const u8,
339 pub arg_count: i32,
340 pub js_mode: i32,
341 pub cur_sp: *mut JSValue,
342}
343
344pub const JS_GC_OBJ_TYPE_JS_OBJECT: u8 = 1;
345pub const JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: u8 = 2;
346pub const JS_GC_OBJ_TYPE_SHAPE: u8 = 3;
347pub const JS_GC_OBJ_TYPE_VAR_REF: u8 = 4;
348pub const JS_GC_OBJ_TYPE_ASYNC_FUNCTION: u8 = 5;
349pub const JS_GC_OBJ_TYPE_JS_CONTEXT: u8 = 6;
350
351#[repr(C)]
352pub struct JSShapeProperty {
353 pub hash_next: u32,
354 pub flags: u8,
355 pub atom: JSAtom,
356}
357
358#[repr(C)]
359pub struct JSProperty {
360 pub u: JSPropertyUnion,
361}
362
363#[repr(C)]
364#[derive(Copy, Clone)]
365pub union JSPropertyUnion {
366 pub value: JSValue,
367 pub next: *mut JSProperty, }
369
370#[repr(C)]
371pub struct JSObject {
372 pub header: JSGCObjectHeader,
373 pub shape: *mut JSShape,
374 pub prop: *mut JSProperty,
375 pub first_weak_ref: *mut JSObject,
376 pub next_in_shape: *mut JSObject,
378}
379
380#[repr(C)]
381pub struct JSClassDef {
382 pub class_name: *const i8,
383 pub finalizer: Option<unsafe extern "C" fn(*mut JSRuntime, JSValue)>,
384 pub gc_mark: Option<unsafe extern "C" fn(*mut JSRuntime, JSValue, *mut c_void)>,
385 pub call: Option<unsafe extern "C" fn(*mut JSContext, JSValue, JSValue, i32, *mut JSValue, i32) -> JSValue>,
386 pub exotic: *mut c_void,
387}
388
389impl JSShape {
390 pub unsafe fn find_own_property(&self, atom: JSAtom) -> Option<(i32, *mut JSShapeProperty)> {
393 unsafe {
394 if self.is_hashed != 0 {
395 let h = atom & self.prop_hash_mask;
396 let mut prop_idx = *self.prop_hash.offset(h as isize);
397 while prop_idx != 0 {
398 let idx = (prop_idx - 1) as i32;
399 let pr = self.prop.offset(idx as isize);
400 if (*pr).atom == atom {
401 return Some((idx, pr));
402 }
403 prop_idx = (*pr).hash_next;
404 }
405 None
406 } else {
407 for i in 0..self.prop_count {
408 let pr = self.prop.offset(i as isize);
409 if (*pr).atom == atom {
410 return Some((i, pr));
411 }
412 }
413 None
414 }
415 }
416 }
417}
418
419impl JSRuntime {
420 pub unsafe fn resize_shape(&mut self, sh: *mut JSShape, new_size: i32) -> i32 {
424 unsafe {
425 let new_prop = self.js_realloc_rt(
426 (*sh).prop as *mut c_void,
427 new_size as usize * std::mem::size_of::<JSShapeProperty>(),
428 ) as *mut JSShapeProperty;
429
430 if new_prop.is_null() {
431 return -1;
432 }
433 (*sh).prop = new_prop;
434 (*sh).prop_size = new_size;
435 0
436 }
437 }
438
439 pub unsafe fn add_property(&mut self, sh: *mut JSShape, atom: JSAtom, flags: u8) -> (i32, i32) {
443 unsafe {
444 let prev_prop_size = (*sh).prop_size;
446
447 if let Some((idx, _)) = (*sh).find_own_property(atom) {
449 return (idx, prev_prop_size);
451 }
452
453 if (*sh).prop_count >= (*sh).prop_size {
454 let new_size = if (*sh).prop_size == 0 { 4 } else { (*sh).prop_size * 3 / 2 };
455 if self.resize_shape(sh, new_size) < 0 {
457 return (-1, prev_prop_size);
458 }
459
460 let mut obj_ptr = (*sh).first_object;
464 while !obj_ptr.is_null() {
465 let next_obj = (*obj_ptr).next_in_shape;
467
468 let old_prop = (*obj_ptr).prop;
469 let new_prop_obj = self.js_realloc_rt(old_prop as *mut c_void, (new_size as usize) * std::mem::size_of::<JSProperty>())
470 as *mut JSProperty;
471
472 if new_prop_obj.is_null() {
473 return (-1, prev_prop_size);
474 }
475
476 if old_prop.is_null() {
478 for i in 0..(new_size as isize) {
480 (*new_prop_obj.offset(i)).u.value = JS_UNDEFINED;
481 }
482 } else if new_size > prev_prop_size {
483 let start_index = prev_prop_size as usize;
485 let new_slots = (new_size - prev_prop_size) as usize;
486 if new_slots > 0 {
487 for i in start_index..(start_index + new_slots) {
488 (*new_prop_obj.add(i)).u.value = JS_UNDEFINED;
489 }
490 }
491 }
492
493 (*obj_ptr).prop = new_prop_obj;
494 obj_ptr = next_obj;
495 }
496 }
497
498 if (*sh).prop_count >= 4 && (*sh).is_hashed == 0 {
500 (*sh).is_hashed = 1;
501 (*sh).prop_hash_mask = 15; let hash_size = 16;
503 (*sh).prop_hash = self.js_malloc_rt(hash_size * std::mem::size_of::<u32>()) as *mut u32;
504 if (*sh).prop_hash.is_null() {
505 return (-1, prev_prop_size);
506 }
507 for i in 0..hash_size {
508 *(*sh).prop_hash.add(i) = 0;
509 }
510 for i in 0..(*sh).prop_count {
512 let pr = (*sh).prop.add(i as usize);
513 let h = ((*pr).atom) & (*sh).prop_hash_mask;
514 (*pr).hash_next = *(*sh).prop_hash.add(h as usize);
515 *(*sh).prop_hash.add(h as usize) = (i + 1) as u32;
516 }
517 }
518
519 let idx = (*sh).prop_count;
520 let pr = (*sh).prop.add(idx as usize);
521 (*pr).atom = atom;
522 (*pr).flags = flags;
523 if (*sh).is_hashed != 0 {
524 let h = (atom) & (*sh).prop_hash_mask;
525 (*pr).hash_next = *(*sh).prop_hash.add(h as usize);
526 *(*sh).prop_hash.add(h as usize) = (idx + 1) as u32;
527 } else {
528 (*pr).hash_next = 0;
529 }
530 (*sh).prop_count += 1;
531
532 (idx, prev_prop_size)
534 }
535 }
536
537 pub unsafe fn js_realloc_rt(&mut self, ptr: *mut c_void, size: usize) -> *mut c_void {
541 unsafe {
542 if let Some(realloc_func) = self.mf.js_realloc {
543 realloc_func(&mut self.malloc_state, ptr, size)
544 } else {
545 std::ptr::null_mut()
546 }
547 }
548 }
549
550 pub unsafe fn js_malloc_rt(&mut self, size: usize) -> *mut c_void {
553 unsafe {
554 if let Some(malloc_func) = self.mf.js_malloc {
555 malloc_func(&mut self.malloc_state, size)
556 } else {
557 std::ptr::null_mut()
558 }
559 }
560 }
561
562 pub unsafe fn js_free_rt(&mut self, ptr: *mut c_void) {
566 unsafe {
567 if let Some(free_func) = self.mf.js_free {
568 free_func(&mut self.malloc_state, ptr);
569 }
570 }
571 }
572
573 pub unsafe fn init_atoms(&mut self) {
577 unsafe {
578 self.atom_hash_size = 16;
579 self.atom_count = 0;
580 self.atom_size = 16;
581 self.atom_count_resize = 8;
582 self.atom_hash = self.js_malloc_rt((self.atom_hash_size as usize) * std::mem::size_of::<u32>()) as *mut u32;
583 if self.atom_hash.is_null() {
584 return;
585 }
586 for i in 0..self.atom_hash_size {
587 *self.atom_hash.offset(i as isize) = 0;
588 }
589 self.atom_array =
590 self.js_malloc_rt((self.atom_size as usize) * std::mem::size_of::<*mut JSAtomStruct>()) as *mut *mut JSAtomStruct;
591 if self.atom_array.is_null() {
592 self.js_free_rt(self.atom_hash as *mut c_void);
593 self.atom_hash = std::ptr::null_mut();
594 return;
595 }
596 for i in 0..self.atom_size {
597 *self.atom_array.offset(i as isize) = std::ptr::null_mut();
598 }
599 self.atom_free_index = 0;
600 }
601 }
602
603 pub unsafe fn js_new_shape(&mut self, proto: *mut JSObject) -> *mut JSShape {
607 unsafe {
608 let sh = self.js_malloc_rt(std::mem::size_of::<JSShape>()) as *mut JSShape;
609 if sh.is_null() {
610 return std::ptr::null_mut();
611 }
612 (*sh).header.ref_count = 1;
613 (*sh).header.gc_obj_type = 0; (*sh).header.mark = 0;
615 (*sh).header.dummy0 = 0;
616 (*sh).header.dummy1 = 0;
617 (*sh).header.dummy2 = 0;
618 (*sh).header.link.init();
619 (*sh).is_hashed = 0;
620 (*sh).has_small_array_index = 0;
621 (*sh).hash = 0;
622 (*sh).prop_hash_mask = 0;
623 (*sh).prop_size = 0;
624 (*sh).prop_count = 0;
625 (*sh).deleted_prop_count = 0;
626 (*sh).prop = std::ptr::null_mut();
627 (*sh).prop_hash = std::ptr::null_mut();
628 (*sh).first_object = std::ptr::null_mut();
629 (*sh).proto = proto;
630 sh
631 }
632 }
633
634 pub unsafe fn js_free_shape(&mut self, sh: *mut JSShape) {
638 unsafe {
639 if !sh.is_null() {
640 if !(*sh).prop.is_null() {
641 self.js_free_rt((*sh).prop as *mut c_void);
642 }
643 if !(*sh).prop_hash.is_null() {
644 self.js_free_rt((*sh).prop_hash as *mut c_void);
645 }
646 self.js_free_rt(sh as *mut c_void);
647 }
648 }
649 }
650}
651
652pub unsafe fn JS_DefinePropertyValue(ctx: *mut JSContext, this_obj: JSValue, prop: JSAtom, val: JSValue, flags: i32) -> i32 {
655 if this_obj.tag != JS_TAG_OBJECT as i64 {
656 return -1; }
658 let p = unsafe { this_obj.u.ptr } as *mut JSObject;
659 let sh = unsafe { (*p).shape };
660
661 let (idx, _prev_prop_size) = unsafe { (*(*ctx).rt).add_property(sh, prop, flags as u8) };
666 if idx < 0 {
667 return -1;
668 }
669
670 let old_prop = unsafe { (*p).prop };
676 if old_prop.is_null() && unsafe { (*sh).prop_size } > 0 {
677 let size = unsafe { (*sh).prop_size as usize } * std::mem::size_of::<JSProperty>();
678 let new_prop = unsafe { (*(*ctx).rt).js_realloc_rt(std::ptr::null_mut(), size) as *mut JSProperty };
679 if new_prop.is_null() {
680 return -1;
681 }
682 unsafe {
683 (*p).prop = new_prop;
684 let n = (*sh).prop_size as isize;
686 for i in 0..n {
687 (*new_prop.offset(i)).u.value = JS_UNDEFINED;
688 }
689 }
690 }
691
692 let pr = unsafe { (*p).prop.offset(idx as isize) };
694 let old_val = unsafe { (*pr).u.value };
696 if old_val.has_ref_count() {
697 unsafe { JS_FreeValue((*ctx).rt, old_val) };
698 }
699 if val.has_ref_count() {
701 unsafe { JS_DupValue((*ctx).rt, val) };
702 }
703 unsafe { (*pr).u.value = val };
704
705 1
706}
707
708pub unsafe fn JS_NewRuntime() -> *mut JSRuntime {
712 unsafe extern "C" fn my_malloc(_state: *mut JSMallocState, size: usize) -> *mut c_void {
713 unsafe { libc::malloc(size) }
714 }
715 unsafe extern "C" fn my_free(_state: *mut JSMallocState, ptr: *mut c_void) {
716 unsafe { libc::free(ptr) };
717 }
718 unsafe extern "C" fn my_realloc(_state: *mut JSMallocState, ptr: *mut c_void, size: usize) -> *mut c_void {
719 unsafe { libc::realloc(ptr, size) }
720 }
721
722 unsafe {
723 let rt = libc::malloc(std::mem::size_of::<JSRuntime>()) as *mut JSRuntime;
724 if rt.is_null() {
725 return std::ptr::null_mut();
726 }
727
728 (*rt).mf.js_malloc = Some(my_malloc);
730 (*rt).mf.js_free = Some(my_free);
731 (*rt).mf.js_realloc = Some(my_realloc);
732 (*rt).mf.js_malloc_usable_size = None;
733
734 (*rt).malloc_state = JSMallocState {
735 malloc_count: 0,
736 malloc_size: 0,
737 malloc_limit: 0,
738 opaque: std::ptr::null_mut(),
739 };
740
741 (*rt).rt_info = std::ptr::null();
742
743 (*rt).atom_hash_size = 0;
745 (*rt).atom_count = 0;
746 (*rt).atom_size = 0;
747 (*rt).atom_count_resize = 0;
748 (*rt).atom_hash = std::ptr::null_mut();
749 (*rt).atom_array = std::ptr::null_mut();
750 (*rt).atom_free_index = 0;
751
752 (*rt).class_count = 0;
753 (*rt).class_array = std::ptr::null_mut();
754
755 (*rt).context_list.init();
756 (*rt).gc_obj_list.init();
757 (*rt).gc_zero_ref_count_list.init();
758 (*rt).tmp_obj_list.init();
759 (*rt).gc_phase = 0;
760 (*rt).malloc_gc_threshold = 0;
761 (*rt).weakref_list.init();
762
763 (*rt).shape_hash_bits = 0;
764 (*rt).shape_hash_size = 0;
765 (*rt).shape_hash_count = 0;
766 (*rt).shape_hash = std::ptr::null_mut();
767
768 (*rt).user_opaque = std::ptr::null_mut();
769
770 (*rt).init_atoms();
771
772 rt
773 }
774}
775
776pub unsafe fn JS_FreeRuntime(rt: *mut JSRuntime) {
780 if !rt.is_null() {
781 unsafe { libc::free(rt as *mut c_void) };
784 }
785}
786
787pub unsafe fn JS_NewContext(rt: *mut JSRuntime) -> *mut JSContext {
790 unsafe {
791 let ctx = (*rt).js_malloc_rt(std::mem::size_of::<JSContext>()) as *mut JSContext;
792 if ctx.is_null() {
793 return std::ptr::null_mut();
794 }
795 (*ctx).header.ref_count = 1;
796 (*ctx).header.gc_obj_type = 0;
797 (*ctx).header.mark = 0;
798 (*ctx).header.dummy0 = 0;
799 (*ctx).header.dummy1 = 0;
800 (*ctx).header.dummy2 = 0;
801 (*ctx).header.link.init();
802 (*ctx).rt = rt;
803 (*ctx).link.init();
804 (*ctx).binary_object_count = 0;
806 (*ctx).binary_object_size = 0;
807 (*ctx).std_array_prototype = 0;
808 (*ctx).array_shape = std::ptr::null_mut();
809 (*ctx).arguments_shape = std::ptr::null_mut();
810 (*ctx).mapped_arguments_shape = std::ptr::null_mut();
811 (*ctx).regexp_shape = std::ptr::null_mut();
812 (*ctx).regexp_result_shape = std::ptr::null_mut();
813 (*ctx).class_proto = std::ptr::null_mut();
814 (*ctx).function_proto = JS_NULL;
815 (*ctx).function_ctor = JS_NULL;
816 (*ctx).array_ctor = JS_NULL;
817 (*ctx).regexp_ctor = JS_NULL;
818 (*ctx).promise_ctor = JS_NULL;
819 for i in 0..8 {
820 (*ctx).native_error_proto[i] = JS_NULL;
821 }
822 (*ctx).iterator_ctor = JS_NULL;
823 (*ctx).async_iterator_proto = JS_NULL;
824 (*ctx).array_proto_values = JS_NULL;
825 (*ctx).throw_type_error = JS_NULL;
826 (*ctx).eval_obj = JS_NULL;
827 (*ctx).global_obj = JS_NULL;
828 (*ctx).global_var_obj = JS_NULL;
829 (*ctx).random_state = 0;
830 (*ctx).interrupt_counter = 0;
831 (*ctx).loaded_modules.init();
832 (*ctx).compile_regexp = None;
833 (*ctx).eval_internal = None;
834 (*ctx).user_opaque = std::ptr::null_mut();
835 ctx
836 }
837}
838
839pub unsafe fn JS_FreeContext(ctx: *mut JSContext) {
843 if !ctx.is_null() {
844 unsafe { (*(*ctx).rt).js_free_rt(ctx as *mut c_void) };
845 }
846}
847
848pub unsafe fn JS_NewObject(ctx: *mut JSContext) -> JSValue {
851 unsafe {
852 let obj = (*(*ctx).rt).js_malloc_rt(std::mem::size_of::<JSObject>()) as *mut JSObject;
853 if obj.is_null() {
854 return JS_EXCEPTION;
855 }
856 (*obj).header.ref_count = 1;
857 (*obj).header.gc_obj_type = 0;
858 (*obj).header.mark = 0;
859 (*obj).header.dummy0 = 0;
860 (*obj).header.dummy1 = 0;
861 (*obj).header.dummy2 = 0;
862 (*obj).header.link.init();
863 (*obj).shape = (*(*ctx).rt).js_new_shape(std::ptr::null_mut());
864 if (*obj).shape.is_null() {
865 (*(*ctx).rt).js_free_rt(obj as *mut c_void);
866 return JS_EXCEPTION;
867 }
868 (*obj).prop = std::ptr::null_mut();
869 (*obj).first_weak_ref = std::ptr::null_mut();
870 (*obj).next_in_shape = std::ptr::null_mut();
871
872 if !(*obj).shape.is_null() {
876 let sh = (*obj).shape;
877 (*obj).next_in_shape = (*sh).first_object;
879 (*sh).first_object = obj;
880 }
881 JSValue::new_ptr(JS_TAG_OBJECT, obj as *mut c_void)
882 }
883}
884
885pub unsafe fn JS_NewString(ctx: *mut JSContext, s: &[u16]) -> JSValue {
888 unsafe {
889 let utf8_str = utf16_to_utf8(s);
890 let len = utf8_str.len();
891 if len == 0 {
892 return JSValue::new_ptr(JS_TAG_STRING, std::ptr::null_mut());
894 }
895 let str_size = std::mem::size_of::<JSString>() + len;
896 let p = (*(*ctx).rt).js_malloc_rt(str_size) as *mut JSString;
897 if p.is_null() {
898 return JS_EXCEPTION;
899 }
900 (*p).header.ref_count = 1;
901 (*p).len = len as u32;
902 (*p).hash = 0; (*p).hash_next = 0;
904 let str_data = (p as *mut u8).add(std::mem::size_of::<JSString>());
906 for (i, &byte) in utf8_str.as_bytes().iter().enumerate() {
907 *str_data.add(i) = byte;
908 }
909 JSValue::new_ptr(JS_TAG_STRING, p as *mut c_void)
910 }
911}
912
913pub unsafe fn JS_Eval(_ctx: *mut JSContext, input: *const i8, input_len: usize, _filename: *const i8, _eval_flags: i32) -> JSValue {
916 unsafe {
917 if input_len == 0 {
918 return JS_UNDEFINED;
919 }
920 let s = std::slice::from_raw_parts(input as *const u8, input_len);
921 let script = std::str::from_utf8(s).unwrap_or("");
922
923 match evaluate_script(script.trim()) {
925 Ok(Value::Number(num)) => JSValue::new_float64(num),
926 Ok(Value::String(s)) => JS_NewString(_ctx, &s),
927 Ok(Value::Boolean(b)) => {
928 if b {
929 JS_TRUE
930 } else {
931 JS_FALSE
932 }
933 }
934 Ok(Value::Undefined) => JS_UNDEFINED,
935 Ok(Value::Object(_)) => JS_UNDEFINED, Ok(Value::Function(_)) => JS_UNDEFINED, Ok(Value::Closure(_, _, _)) => JS_UNDEFINED, Ok(Value::AsyncClosure(_, _, _)) => JS_UNDEFINED, Ok(Value::ClassDefinition(_)) => JS_UNDEFINED, Ok(Value::Getter(_, _)) => JS_UNDEFINED, Ok(Value::Setter(_, _, _)) => JS_UNDEFINED, Ok(Value::Property { .. }) => JS_UNDEFINED, Ok(Value::Promise(_)) => JS_UNDEFINED, Ok(Value::Symbol(_)) => JS_UNDEFINED, Ok(Value::BigInt(_)) => JS_UNDEFINED,
946 Ok(Value::Map(_)) => JS_UNDEFINED, Ok(Value::Set(_)) => JS_UNDEFINED, Ok(Value::WeakMap(_)) => JS_UNDEFINED, Ok(Value::WeakSet(_)) => JS_UNDEFINED, Ok(Value::GeneratorFunction(_, _, _)) => JS_UNDEFINED, Ok(Value::Generator(_)) => JS_UNDEFINED, Ok(Value::Proxy(_)) => JS_UNDEFINED, Ok(Value::ArrayBuffer(_)) => JS_UNDEFINED, Ok(Value::DataView(_)) => JS_UNDEFINED, Ok(Value::TypedArray(_)) => JS_UNDEFINED, Err(_) => JS_UNDEFINED,
957 }
958 }
959}
960
961pub unsafe fn JS_GetProperty(_ctx: *mut JSContext, this_obj: JSValue, prop: JSAtom) -> JSValue {
964 unsafe {
965 if this_obj.tag != JS_TAG_OBJECT as i64 {
966 return JS_UNDEFINED;
967 }
968 let p = this_obj.u.ptr as *mut JSObject;
969 let sh = (*p).shape;
970 if let Some((idx, _)) = (*sh).find_own_property(prop) {
971 let prop_val = (*(*p).prop.offset(idx as isize)).u.value;
972 if prop_val.has_ref_count() {
974 JS_DupValue((*_ctx).rt, prop_val);
975 }
976 prop_val
977 } else {
978 JS_UNDEFINED
979 }
980 }
981}
982
983pub unsafe fn JS_DupValue(_rt: *mut JSRuntime, v: JSValue) {
989 unsafe {
990 if v.has_ref_count() {
991 let p = v.get_ptr();
992 if !p.is_null() {
993 let header = p as *mut JSRefCountHeader;
994 (*header).ref_count += 1;
995 }
996 }
997 }
998}
999
1000pub unsafe fn JS_FreeValue(rt: *mut JSRuntime, v: JSValue) {
1003 unsafe {
1004 if v.has_ref_count() {
1005 let p = v.get_ptr();
1006 if p.is_null() {
1007 return;
1008 }
1009 let header = p as *mut JSRefCountHeader;
1010 (*header).ref_count -= 1;
1011 if (*header).ref_count > 0 {
1012 return;
1013 }
1014 match v.get_tag() {
1016 x if x == JS_TAG_STRING => {
1017 js_free_string(rt, v);
1018 }
1019 x if x == JS_TAG_OBJECT => {
1020 js_free_object(rt, v);
1021 }
1022 x if x == JS_TAG_FUNCTION_BYTECODE => {
1023 js_free_function_bytecode(rt, v);
1024 }
1025 x if x == JS_TAG_SYMBOL => {
1026 js_free_symbol(rt, v);
1027 }
1028 x if x == JS_TAG_BIG_INT => {
1029 js_free_bigint(rt, v);
1030 }
1031 x if x == JS_TAG_MODULE => {
1032 js_free_module(rt, v);
1033 }
1034 _ => {
1036 (*rt).js_free_rt(p);
1037 }
1038 }
1039 }
1040 }
1041}
1042
1043unsafe fn js_free_string(rt: *mut JSRuntime, v: JSValue) {
1044 unsafe {
1045 let p = v.get_ptr() as *mut JSString;
1046 if p.is_null() {
1047 return;
1048 }
1049 (*rt).js_free_rt(p as *mut c_void);
1051 }
1052}
1053
1054unsafe fn js_free_object(rt: *mut JSRuntime, v: JSValue) {
1055 unsafe {
1056 let p = v.get_ptr() as *mut JSObject;
1057 if p.is_null() {
1058 return;
1059 }
1060 if !(*p).prop.is_null() {
1062 (*rt).js_free_rt((*p).prop as *mut c_void);
1063 (*p).prop = std::ptr::null_mut();
1064 }
1065 if !(*p).shape.is_null() {
1067 let sh = (*p).shape;
1068 if (*sh).first_object == p {
1070 (*sh).first_object = (*p).next_in_shape;
1071 } else {
1072 let mut prev = (*sh).first_object;
1073 while !prev.is_null() {
1074 if (*prev).next_in_shape == p {
1075 (*prev).next_in_shape = (*p).next_in_shape;
1076 break;
1077 }
1078 prev = (*prev).next_in_shape;
1079 }
1080 }
1081 (*p).next_in_shape = std::ptr::null_mut();
1083
1084 if (*sh).first_object.is_null() {
1086 (*rt).js_free_shape(sh);
1087 }
1088 (*p).shape = std::ptr::null_mut();
1089 }
1090 (*rt).js_free_rt(p as *mut c_void);
1092 }
1093}
1094
1095unsafe fn js_free_function_bytecode(rt: *mut JSRuntime, v: JSValue) {
1096 unsafe {
1097 let p = v.get_ptr() as *mut JSFunctionBytecode;
1098 if p.is_null() {
1099 return;
1100 }
1101 if !(*p).byte_code_buf.is_null() {
1103 (*rt).js_free_rt((*p).byte_code_buf as *mut c_void);
1104 (*p).byte_code_buf = std::ptr::null_mut();
1105 }
1106 if !(*p).pc2line_buf.is_null() {
1108 (*rt).js_free_rt((*p).pc2line_buf as *mut c_void);
1109 (*p).pc2line_buf = std::ptr::null_mut();
1110 }
1111 if !(*p).source.is_null() {
1113 (*rt).js_free_rt((*p).source as *mut c_void);
1114 (*p).source = std::ptr::null_mut();
1115 }
1116 if !(*p).cpool.is_null() && (*p).cpool_count > 0 {
1118 for i in 0..(*p).cpool_count as isize {
1119 let val = *(*p).cpool.offset(i);
1120 if val.has_ref_count() {
1121 JS_FreeValue(rt, val);
1122 }
1123 }
1124 (*rt).js_free_rt((*p).cpool as *mut c_void);
1125 (*p).cpool = std::ptr::null_mut();
1126 }
1127 (*rt).js_free_rt(p as *mut c_void);
1129 }
1130}
1131
1132unsafe fn js_free_symbol(rt: *mut JSRuntime, v: JSValue) {
1133 let p = v.get_ptr();
1134 if p.is_null() {
1135 return;
1136 }
1137 unsafe { (*rt).js_free_rt(p) };
1140}
1141
1142unsafe fn js_free_bigint(rt: *mut JSRuntime, v: JSValue) {
1143 let p = v.get_ptr();
1144 if p.is_null() {
1145 return;
1146 }
1147 unsafe { (*rt).js_free_rt(p) };
1149}
1150
1151unsafe fn js_free_module(rt: *mut JSRuntime, v: JSValue) {
1152 let p = v.get_ptr();
1153 if p.is_null() {
1154 return;
1155 }
1156 unsafe { (*rt).js_free_rt(p) };
1158}
1159
1160pub unsafe fn JS_SetProperty(ctx: *mut JSContext, this_obj: JSValue, prop: JSAtom, val: JSValue) -> i32 {
1163 unsafe { JS_DefinePropertyValue(ctx, this_obj, prop, val, 0) }
1164}
1165
1166impl JSRuntime {
1167 pub unsafe fn js_new_atom_len(&mut self, name: *const u8, len: usize) -> JSAtom {
1170 if len == 0 {
1171 return 0; }
1173 let mut h = 0u32;
1175 for i in 0..len {
1176 h = h.wrapping_mul(31).wrapping_add(unsafe { *name.add(i) } as u32);
1177 }
1178 let hash_index = (h % self.atom_hash_size as u32) as i32;
1180 let mut atom = unsafe { *self.atom_hash.offset(hash_index as isize) };
1181 while atom != 0 {
1182 let p = unsafe { *self.atom_array.offset((atom - 1) as isize) };
1183 if unsafe { (*p).len == len as u32 && (*p).hash == h } {
1184 let str_data = unsafe { (p as *mut u8).add(std::mem::size_of::<JSString>()) };
1186 let mut equal = true;
1187 for i in 0..len {
1188 if unsafe { *str_data.add(i) != *name.add(i) } {
1189 equal = false;
1190 break;
1191 }
1192 }
1193 if equal {
1194 return atom;
1195 }
1196 }
1197 atom = unsafe { (*p).hash_next };
1198 }
1199 if self.atom_count >= self.atom_size {
1201 let new_size = self.atom_size * 2;
1202 let new_array = unsafe {
1203 self.js_realloc_rt(
1204 self.atom_array as *mut c_void,
1205 (new_size as usize) * std::mem::size_of::<*mut JSAtomStruct>(),
1206 )
1207 } as *mut *mut JSAtomStruct;
1208 if new_array.is_null() {
1209 return 0;
1210 }
1211 self.atom_array = new_array;
1212 self.atom_size = new_size;
1213 for i in self.atom_count..new_size {
1214 unsafe { *self.atom_array.offset(i as isize) = std::ptr::null_mut() };
1215 }
1216 }
1217 let str_size = std::mem::size_of::<JSString>() + len;
1219 let p = unsafe { self.js_malloc_rt(str_size) } as *mut JSString;
1220 if p.is_null() {
1221 return 0;
1222 }
1223 unsafe { (*p).header.ref_count = 1 };
1224 unsafe { (*p).len = len as u32 };
1225 unsafe { (*p).hash = h };
1226 unsafe { (*p).hash_next = *self.atom_hash.offset(hash_index as isize) };
1227 let str_data = unsafe { (p as *mut u8).add(std::mem::size_of::<JSString>()) };
1229 for i in 0..len {
1230 unsafe { *str_data.add(i) = *name.add(i) };
1231 }
1232 let new_atom = (self.atom_count + 1) as u32;
1233 unsafe { *self.atom_array.offset(self.atom_count as isize) = p };
1234 unsafe { *self.atom_hash.offset(hash_index as isize) = new_atom };
1235 self.atom_count += 1;
1236 new_atom
1237 }
1238}