1#![allow(non_snake_case)]
2#![allow(non_camel_case_types)]
3
4use crate::error::JSError;
5use crate::js_array::{get_array_length, is_array, set_array_length};
6use crate::js_class::{
7 ClassDefinition, ClassMember, call_class_method, call_static_method, create_class_object, evaluate_new, evaluate_super,
8 evaluate_super_call, evaluate_super_method, evaluate_super_property, evaluate_this, is_class_instance, is_instance_of,
9};
10use crate::js_console;
11use crate::js_math;
12use crate::js_number;
13use crate::js_promise::{JSPromise, PromiseState, handle_promise_method, run_event_loop};
14use crate::sprintf;
15use crate::tmpfile;
16use std::cell::RefCell;
17use std::ffi::c_void;
18use std::rc::Rc;
19
20#[repr(C)]
21#[derive(Copy, Clone)]
22pub union JSValueUnion {
23 pub int32: i32,
24 pub float64: f64,
25 pub ptr: *mut c_void,
26 pub short_big_int: i64,
27}
28
29#[repr(C)]
30#[derive(Copy, Clone)]
31pub struct JSValue {
32 pub u: JSValueUnion,
33 pub tag: i64,
34}
35
36pub const JS_TAG_FIRST: i32 = -9;
37pub const JS_TAG_BIG_INT: i32 = -9;
38pub const JS_TAG_SYMBOL: i32 = -8;
39pub const JS_TAG_STRING: i32 = -7;
40pub const JS_TAG_STRING_ROPE: i32 = -6;
41pub const JS_TAG_MODULE: i32 = -3;
42pub const JS_TAG_FUNCTION_BYTECODE: i32 = -2;
43pub const JS_TAG_OBJECT: i32 = -1;
44
45pub const JS_TAG_INT: i32 = 0;
46pub const JS_TAG_BOOL: i32 = 1;
47pub const JS_TAG_NULL: i32 = 2;
48pub const JS_TAG_UNDEFINED: i32 = 3;
49pub const JS_TAG_UNINITIALIZED: i32 = 4;
50pub const JS_TAG_CATCH_OFFSET: i32 = 5;
51pub const JS_TAG_EXCEPTION: i32 = 6;
52pub const JS_TAG_SHORT_BIG_INT: i32 = 7;
53pub const JS_TAG_FLOAT64: i32 = 8;
54
55pub const JS_FLOAT64_NAN: f64 = f64::NAN;
56
57impl JSValue {
58 pub fn new_int32(val: i32) -> JSValue {
59 JSValue {
60 u: JSValueUnion { int32: val },
61 tag: JS_TAG_INT as i64,
62 }
63 }
64
65 pub fn new_bool(val: bool) -> JSValue {
66 JSValue {
67 u: JSValueUnion {
68 int32: if val { 1 } else { 0 },
69 },
70 tag: JS_TAG_BOOL as i64,
71 }
72 }
73
74 pub fn new_float64(val: f64) -> JSValue {
75 JSValue {
76 u: JSValueUnion { float64: val },
77 tag: JS_TAG_FLOAT64 as i64,
78 }
79 }
80
81 pub fn new_ptr(tag: i32, ptr: *mut c_void) -> JSValue {
82 JSValue {
83 u: JSValueUnion { ptr },
84 tag: tag as i64,
85 }
86 }
87
88 pub fn has_ref_count(&self) -> bool {
89 let t = self.tag as i32;
90 (JS_TAG_FIRST..=JS_TAG_OBJECT).contains(&t)
91 }
92
93 pub fn get_ptr(&self) -> *mut c_void {
94 unsafe { self.u.ptr }
95 }
96
97 pub fn get_tag(&self) -> i32 {
98 self.tag as i32
99 }
100}
101
102pub const JS_NULL: JSValue = JSValue {
103 u: JSValueUnion { int32: 0 },
104 tag: JS_TAG_NULL as i64,
105};
106
107pub const JS_UNDEFINED: JSValue = JSValue {
108 u: JSValueUnion { int32: 0 },
109 tag: JS_TAG_UNDEFINED as i64,
110};
111
112pub const JS_FALSE: JSValue = JSValue {
113 u: JSValueUnion { int32: 0 },
114 tag: JS_TAG_BOOL as i64,
115};
116
117pub const JS_TRUE: JSValue = JSValue {
118 u: JSValueUnion { int32: 1 },
119 tag: JS_TAG_BOOL as i64,
120};
121
122pub const JS_EXCEPTION: JSValue = JSValue {
123 u: JSValueUnion { int32: 0 },
124 tag: JS_TAG_EXCEPTION as i64,
125};
126
127pub const JS_UNINITIALIZED: JSValue = JSValue {
128 u: JSValueUnion { int32: 0 },
129 tag: JS_TAG_UNINITIALIZED as i64,
130};
131
132#[repr(C)]
133pub struct list_head {
134 pub prev: *mut list_head,
135 pub next: *mut list_head,
136}
137
138impl list_head {
139 pub unsafe fn init(&mut self) {
142 self.prev = self;
143 self.next = self;
144 }
145
146 pub unsafe fn add_tail(&mut self, new_entry: *mut list_head) {
150 unsafe {
151 let prev = self.prev;
152 (*new_entry).next = self;
153 (*new_entry).prev = prev;
154 (*prev).next = new_entry;
155 self.prev = new_entry;
156 }
157 }
158
159 pub unsafe fn del(&mut self) {
162 unsafe {
163 let next = self.next;
164 let prev = self.prev;
165 (*next).prev = prev;
166 (*prev).next = next;
167 self.next = std::ptr::null_mut();
168 self.prev = std::ptr::null_mut();
169 }
170 }
171}
172
173#[repr(C)]
174pub struct JSMallocState {
175 pub malloc_count: usize,
176 pub malloc_size: usize,
177 pub malloc_limit: usize,
178 pub opaque: *mut c_void,
179}
180
181#[repr(C)]
182pub struct JSMallocFunctions {
183 pub js_malloc: Option<unsafe extern "C" fn(*mut JSMallocState, usize) -> *mut c_void>,
184 pub js_free: Option<unsafe extern "C" fn(*mut JSMallocState, *mut c_void)>,
185 pub js_realloc: Option<unsafe extern "C" fn(*mut JSMallocState, *mut c_void, usize) -> *mut c_void>,
186 pub js_malloc_usable_size: Option<unsafe extern "C" fn(*const c_void) -> usize>,
187}
188
189pub type JSAtom = u32;
190
191#[repr(C)]
192pub struct JSRefCountHeader {
193 pub ref_count: i32,
194}
195
196#[repr(C)]
197pub struct JSString {
198 pub header: JSRefCountHeader,
199 pub len: u32, pub hash: u32, pub hash_next: u32,
202 }
204
205pub type JSAtomStruct = JSString;
206
207#[repr(C)]
208pub struct JSClass {
209 pub class_id: u32,
210 pub class_name: JSAtom,
211 pub finalizer: *mut c_void, pub gc_mark: *mut c_void, pub call: *mut c_void, pub exotic: *mut c_void, }
216
217#[repr(C)]
218pub struct JSRuntime {
219 pub mf: JSMallocFunctions,
220 pub malloc_state: JSMallocState,
221 pub rt_info: *const i8,
222
223 pub atom_hash_size: i32,
224 pub atom_count: i32,
225 pub atom_size: i32,
226 pub atom_count_resize: i32,
227 pub atom_hash: *mut u32,
228 pub atom_array: *mut *mut JSAtomStruct,
229 pub atom_free_index: i32,
230
231 pub class_count: i32,
232 pub class_array: *mut JSClass,
233
234 pub context_list: list_head,
235 pub gc_obj_list: list_head,
236 pub gc_zero_ref_count_list: list_head,
237 pub tmp_obj_list: list_head,
238 pub gc_phase: u8,
239 pub malloc_gc_threshold: usize,
240 pub weakref_list: list_head,
241
242 pub shape_hash_bits: i32,
243 pub shape_hash_size: i32,
244 pub shape_hash_count: i32,
245 pub shape_hash: *mut *mut JSShape,
246 pub user_opaque: *mut c_void,
247}
248
249#[repr(C)]
250pub struct JSGCObjectHeader {
251 pub ref_count: i32,
252 pub gc_obj_type: u8, pub mark: u8, pub dummy0: u8, pub dummy1: u8,
256 pub dummy2: u16,
257 pub link: list_head,
258}
259
260#[repr(C)]
261pub struct JSShape {
262 pub header: JSGCObjectHeader,
263 pub is_hashed: u8,
264 pub has_small_array_index: u8,
265 pub hash: u32,
266 pub prop_hash_mask: u32,
267 pub prop_size: i32,
268 pub prop_count: i32,
269 pub deleted_prop_count: i32,
270 pub prop: *mut JSShapeProperty,
271 pub prop_hash: *mut u32,
272 pub proto: *mut JSObject,
273}
274
275#[repr(C)]
276pub struct JSContext {
277 pub header: JSGCObjectHeader,
278 pub rt: *mut JSRuntime,
279 pub link: list_head,
280
281 pub binary_object_count: u16,
282 pub binary_object_size: i32,
283 pub std_array_prototype: u8,
284
285 pub array_shape: *mut JSShape,
286 pub arguments_shape: *mut JSShape,
287 pub mapped_arguments_shape: *mut JSShape,
288 pub regexp_shape: *mut JSShape,
289 pub regexp_result_shape: *mut JSShape,
290
291 pub class_proto: *mut JSValue,
292 pub function_proto: JSValue,
293 pub function_ctor: JSValue,
294 pub array_ctor: JSValue,
295 pub regexp_ctor: JSValue,
296 pub promise_ctor: JSValue,
297 pub native_error_proto: [JSValue; 8], pub iterator_ctor: JSValue,
299 pub async_iterator_proto: JSValue,
300 pub array_proto_values: JSValue,
301 pub throw_type_error: JSValue,
302 pub eval_obj: JSValue,
303
304 pub global_obj: JSValue,
305 pub global_var_obj: JSValue,
306
307 pub random_state: u64,
308 pub interrupt_counter: i32,
309
310 pub loaded_modules: list_head,
311
312 pub compile_regexp: Option<unsafe extern "C" fn(*mut JSContext, JSValue, JSValue) -> JSValue>,
313 pub eval_internal: Option<unsafe extern "C" fn(*mut JSContext, JSValue, *const i8, usize, *const i8, i32, i32) -> JSValue>,
314 pub user_opaque: *mut c_void,
315}
316
317#[repr(C)]
318pub struct JSFunctionBytecode {
319 pub header: JSGCObjectHeader,
320 pub js_mode: u8,
321 pub flags: u16, pub byte_code_buf: *mut u8,
323 pub byte_code_len: i32,
324 pub func_name: JSAtom,
325 pub vardefs: *mut c_void, pub closure_var: *mut c_void, pub arg_count: u16,
328 pub var_count: u16,
329 pub defined_arg_count: u16,
330 pub stack_size: u16,
331 pub var_ref_count: u16,
332 pub realm: *mut JSContext,
333 pub cpool: *mut JSValue,
334 pub cpool_count: i32,
335 pub closure_var_count: i32,
336 pub filename: JSAtom,
338 pub source_len: i32,
339 pub pc2line_len: i32,
340 pub pc2line_buf: *mut u8,
341 pub source: *mut i8,
342}
343
344#[repr(C)]
345pub struct JSStackFrame {
346 pub prev_frame: *mut JSStackFrame,
347 pub cur_func: JSValue,
348 pub arg_buf: *mut JSValue,
349 pub var_buf: *mut JSValue,
350 pub var_refs: *mut *mut c_void, pub cur_pc: *const u8,
352 pub arg_count: i32,
353 pub js_mode: i32,
354 pub cur_sp: *mut JSValue,
355}
356
357pub const JS_GC_OBJ_TYPE_JS_OBJECT: u8 = 1;
358pub const JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: u8 = 2;
359pub const JS_GC_OBJ_TYPE_SHAPE: u8 = 3;
360pub const JS_GC_OBJ_TYPE_VAR_REF: u8 = 4;
361pub const JS_GC_OBJ_TYPE_ASYNC_FUNCTION: u8 = 5;
362pub const JS_GC_OBJ_TYPE_JS_CONTEXT: u8 = 6;
363
364#[repr(C)]
365pub struct JSShapeProperty {
366 pub hash_next: u32,
367 pub flags: u8,
368 pub atom: JSAtom,
369}
370
371#[repr(C)]
372pub struct JSProperty {
373 pub u: JSPropertyUnion,
374}
375
376#[repr(C)]
377#[derive(Copy, Clone)]
378pub union JSPropertyUnion {
379 pub value: JSValue,
380 pub next: *mut JSProperty, }
382
383#[repr(C)]
384pub struct JSObject {
385 pub header: JSGCObjectHeader,
386 pub shape: *mut JSShape,
387 pub prop: *mut JSProperty,
388 pub first_weak_ref: *mut JSObject,
389}
390
391#[repr(C)]
392pub struct JSClassDef {
393 pub class_name: *const i8,
394 pub finalizer: Option<unsafe extern "C" fn(*mut JSRuntime, JSValue)>,
395 pub gc_mark: Option<unsafe extern "C" fn(*mut JSRuntime, JSValue, *mut c_void)>,
396 pub call: Option<unsafe extern "C" fn(*mut JSContext, JSValue, JSValue, i32, *mut JSValue, i32) -> JSValue>,
397 pub exotic: *mut c_void,
398}
399
400impl JSShape {
401 pub unsafe fn find_own_property(&self, atom: JSAtom) -> Option<(i32, *mut JSShapeProperty)> {
404 unsafe {
405 if self.is_hashed != 0 {
406 let h = atom & self.prop_hash_mask;
407 let mut prop_idx = *self.prop_hash.offset(h as isize);
408 while prop_idx != 0 {
409 let idx = (prop_idx - 1) as i32;
410 let pr = self.prop.offset(idx as isize);
411 if (*pr).atom == atom {
412 return Some((idx, pr));
413 }
414 prop_idx = (*pr).hash_next;
415 }
416 None
417 } else {
418 for i in 0..self.prop_count {
419 let pr = self.prop.offset(i as isize);
420 if (*pr).atom == atom {
421 return Some((i, pr));
422 }
423 }
424 None
425 }
426 }
427 }
428}
429
430impl JSRuntime {
431 pub unsafe fn resize_shape(&mut self, sh: *mut JSShape, new_size: i32) -> i32 {
435 unsafe {
436 let new_prop = self.js_realloc_rt(
437 (*sh).prop as *mut c_void,
438 new_size as usize * std::mem::size_of::<JSShapeProperty>(),
439 ) as *mut JSShapeProperty;
440
441 if new_prop.is_null() {
442 return -1;
443 }
444 (*sh).prop = new_prop;
445 (*sh).prop_size = new_size;
446 0
447 }
448 }
449
450 pub unsafe fn add_property(&mut self, sh: *mut JSShape, atom: JSAtom, flags: u8) -> i32 {
454 unsafe {
455 if let Some((idx, _)) = (*sh).find_own_property(atom) {
457 return idx;
459 }
460
461 if (*sh).prop_count >= (*sh).prop_size {
462 let new_size = if (*sh).prop_size == 0 { 4 } else { (*sh).prop_size * 3 / 2 };
463 if self.resize_shape(sh, new_size) < 0 {
464 return -1;
465 }
466 }
467
468 if (*sh).prop_count >= 4 && (*sh).is_hashed == 0 {
470 (*sh).is_hashed = 1;
471 (*sh).prop_hash_mask = 15; let hash_size = 16;
473 (*sh).prop_hash = self.js_malloc_rt(hash_size * std::mem::size_of::<u32>()) as *mut u32;
474 if (*sh).prop_hash.is_null() {
475 return -1;
476 }
477 for i in 0..hash_size {
478 *(*sh).prop_hash.add(i) = 0;
479 }
480 for i in 0..(*sh).prop_count {
482 let pr = (*sh).prop.add(i as usize);
483 let h = ((*pr).atom) & (*sh).prop_hash_mask;
484 (*pr).hash_next = *(*sh).prop_hash.add(h as usize);
485 *(*sh).prop_hash.add(h as usize) = (i + 1) as u32;
486 }
487 }
488
489 let idx = (*sh).prop_count;
490 let pr = (*sh).prop.add(idx as usize);
491 (*pr).atom = atom;
492 (*pr).flags = flags;
493 if (*sh).is_hashed != 0 {
494 let h = (atom) & (*sh).prop_hash_mask;
495 (*pr).hash_next = *(*sh).prop_hash.add(h as usize);
496 *(*sh).prop_hash.add(h as usize) = (idx + 1) as u32;
497 } else {
498 (*pr).hash_next = 0;
499 }
500 (*sh).prop_count += 1;
501
502 idx
503 }
504 }
505
506 pub unsafe fn js_realloc_rt(&mut self, ptr: *mut c_void, size: usize) -> *mut c_void {
510 unsafe {
511 if let Some(realloc_func) = self.mf.js_realloc {
512 realloc_func(&mut self.malloc_state, ptr, size)
513 } else {
514 std::ptr::null_mut()
515 }
516 }
517 }
518
519 pub unsafe fn js_malloc_rt(&mut self, size: usize) -> *mut c_void {
522 unsafe {
523 if let Some(malloc_func) = self.mf.js_malloc {
524 malloc_func(&mut self.malloc_state, size)
525 } else {
526 std::ptr::null_mut()
527 }
528 }
529 }
530
531 pub unsafe fn js_free_rt(&mut self, ptr: *mut c_void) {
535 unsafe {
536 if let Some(free_func) = self.mf.js_free {
537 free_func(&mut self.malloc_state, ptr);
538 }
539 }
540 }
541
542 pub unsafe fn init_atoms(&mut self) {
546 unsafe {
547 self.atom_hash_size = 16;
548 self.atom_count = 0;
549 self.atom_size = 16;
550 self.atom_count_resize = 8;
551 self.atom_hash = self.js_malloc_rt((self.atom_hash_size as usize) * std::mem::size_of::<u32>()) as *mut u32;
552 if self.atom_hash.is_null() {
553 return;
554 }
555 for i in 0..self.atom_hash_size {
556 *self.atom_hash.offset(i as isize) = 0;
557 }
558 self.atom_array =
559 self.js_malloc_rt((self.atom_size as usize) * std::mem::size_of::<*mut JSAtomStruct>()) as *mut *mut JSAtomStruct;
560 if self.atom_array.is_null() {
561 self.js_free_rt(self.atom_hash as *mut c_void);
562 self.atom_hash = std::ptr::null_mut();
563 return;
564 }
565 for i in 0..self.atom_size {
566 *self.atom_array.offset(i as isize) = std::ptr::null_mut();
567 }
568 self.atom_free_index = 0;
569 }
570 }
571
572 pub unsafe fn js_new_shape(&mut self, proto: *mut JSObject) -> *mut JSShape {
576 unsafe {
577 let sh = self.js_malloc_rt(std::mem::size_of::<JSShape>()) as *mut JSShape;
578 if sh.is_null() {
579 return std::ptr::null_mut();
580 }
581 (*sh).header.ref_count = 1;
582 (*sh).header.gc_obj_type = 0; (*sh).header.mark = 0;
584 (*sh).header.dummy0 = 0;
585 (*sh).header.dummy1 = 0;
586 (*sh).header.dummy2 = 0;
587 (*sh).header.link.init();
588 (*sh).is_hashed = 0;
589 (*sh).has_small_array_index = 0;
590 (*sh).hash = 0;
591 (*sh).prop_hash_mask = 0;
592 (*sh).prop_size = 0;
593 (*sh).prop_count = 0;
594 (*sh).deleted_prop_count = 0;
595 (*sh).prop = std::ptr::null_mut();
596 (*sh).prop_hash = std::ptr::null_mut();
597 (*sh).proto = proto;
598 sh
599 }
600 }
601
602 pub unsafe fn js_free_shape(&mut self, sh: *mut JSShape) {
606 unsafe {
607 if !sh.is_null() {
608 if !(*sh).prop.is_null() {
609 self.js_free_rt((*sh).prop as *mut c_void);
610 }
611 if !(*sh).prop_hash.is_null() {
612 self.js_free_rt((*sh).prop_hash as *mut c_void);
613 }
614 self.js_free_rt(sh as *mut c_void);
615 }
616 }
617 }
618}
619
620pub unsafe fn JS_DefinePropertyValue(ctx: *mut JSContext, this_obj: JSValue, prop: JSAtom, val: JSValue, flags: i32) -> i32 {
623 if this_obj.tag != JS_TAG_OBJECT as i64 {
624 return -1; }
626 let p = unsafe { this_obj.u.ptr } as *mut JSObject;
627 let sh = unsafe { (*p).shape };
628
629 let idx = unsafe { (*(*ctx).rt).add_property(sh, prop, flags as u8) };
634 if idx < 0 {
635 return -1;
636 }
637
638 let old_prop = unsafe { (*p).prop };
663 let new_prop = unsafe {
664 (*(*ctx).rt).js_realloc_rt(
665 (*p).prop as *mut c_void,
666 ((*sh).prop_size as usize) * std::mem::size_of::<JSProperty>(),
667 ) as *mut JSProperty
668 };
669
670 if new_prop.is_null() {
671 return -1;
672 }
673 unsafe { (*p).prop = new_prop };
674 if old_prop.is_null() && !new_prop.is_null() {
677 let size_bytes = unsafe { ((*sh).prop_size as usize) * std::mem::size_of::<JSProperty>() };
678 unsafe { std::ptr::write_bytes(new_prop as *mut u8, 0, size_bytes) };
679 }
680
681 let pr = unsafe { (*p).prop.offset(idx as isize) };
683 let old_val = unsafe { (*pr).u.value };
685 if old_val.has_ref_count() {
686 unsafe { JS_FreeValue((*ctx).rt, old_val) };
687 }
688 if val.has_ref_count() {
690 unsafe { JS_DupValue((*ctx).rt, val) };
691 }
692 unsafe { (*pr).u.value = val };
693
694 1
695}
696
697pub unsafe fn JS_NewRuntime() -> *mut JSRuntime {
701 unsafe extern "C" fn my_malloc(_state: *mut JSMallocState, size: usize) -> *mut c_void {
702 unsafe { libc::malloc(size) }
703 }
704 unsafe extern "C" fn my_free(_state: *mut JSMallocState, ptr: *mut c_void) {
705 unsafe { libc::free(ptr) };
706 }
707 unsafe extern "C" fn my_realloc(_state: *mut JSMallocState, ptr: *mut c_void, size: usize) -> *mut c_void {
708 unsafe { libc::realloc(ptr, size) }
709 }
710
711 unsafe {
712 let rt = libc::malloc(std::mem::size_of::<JSRuntime>()) as *mut JSRuntime;
713 if rt.is_null() {
714 return std::ptr::null_mut();
715 }
716
717 (*rt).mf.js_malloc = Some(my_malloc);
719 (*rt).mf.js_free = Some(my_free);
720 (*rt).mf.js_realloc = Some(my_realloc);
721 (*rt).mf.js_malloc_usable_size = None;
722
723 (*rt).malloc_state = JSMallocState {
724 malloc_count: 0,
725 malloc_size: 0,
726 malloc_limit: 0,
727 opaque: std::ptr::null_mut(),
728 };
729
730 (*rt).rt_info = std::ptr::null();
731
732 (*rt).atom_hash_size = 0;
734 (*rt).atom_count = 0;
735 (*rt).atom_size = 0;
736 (*rt).atom_count_resize = 0;
737 (*rt).atom_hash = std::ptr::null_mut();
738 (*rt).atom_array = std::ptr::null_mut();
739 (*rt).atom_free_index = 0;
740
741 (*rt).class_count = 0;
742 (*rt).class_array = std::ptr::null_mut();
743
744 (*rt).context_list.init();
745 (*rt).gc_obj_list.init();
746 (*rt).gc_zero_ref_count_list.init();
747 (*rt).tmp_obj_list.init();
748 (*rt).gc_phase = 0;
749 (*rt).malloc_gc_threshold = 0;
750 (*rt).weakref_list.init();
751
752 (*rt).shape_hash_bits = 0;
753 (*rt).shape_hash_size = 0;
754 (*rt).shape_hash_count = 0;
755 (*rt).shape_hash = std::ptr::null_mut();
756
757 (*rt).user_opaque = std::ptr::null_mut();
758
759 (*rt).init_atoms();
760
761 rt
762 }
763}
764
765pub unsafe fn JS_FreeRuntime(rt: *mut JSRuntime) {
769 if !rt.is_null() {
770 unsafe { libc::free(rt as *mut c_void) };
773 }
774}
775
776pub unsafe fn JS_NewContext(rt: *mut JSRuntime) -> *mut JSContext {
779 unsafe {
780 let ctx = (*rt).js_malloc_rt(std::mem::size_of::<JSContext>()) as *mut JSContext;
781 if ctx.is_null() {
782 return std::ptr::null_mut();
783 }
784 (*ctx).header.ref_count = 1;
785 (*ctx).header.gc_obj_type = 0;
786 (*ctx).header.mark = 0;
787 (*ctx).header.dummy0 = 0;
788 (*ctx).header.dummy1 = 0;
789 (*ctx).header.dummy2 = 0;
790 (*ctx).header.link.init();
791 (*ctx).rt = rt;
792 (*ctx).link.init();
793 (*ctx).binary_object_count = 0;
795 (*ctx).binary_object_size = 0;
796 (*ctx).std_array_prototype = 0;
797 (*ctx).array_shape = std::ptr::null_mut();
798 (*ctx).arguments_shape = std::ptr::null_mut();
799 (*ctx).mapped_arguments_shape = std::ptr::null_mut();
800 (*ctx).regexp_shape = std::ptr::null_mut();
801 (*ctx).regexp_result_shape = std::ptr::null_mut();
802 (*ctx).class_proto = std::ptr::null_mut();
803 (*ctx).function_proto = JS_NULL;
804 (*ctx).function_ctor = JS_NULL;
805 (*ctx).array_ctor = JS_NULL;
806 (*ctx).regexp_ctor = JS_NULL;
807 (*ctx).promise_ctor = JS_NULL;
808 for i in 0..8 {
809 (*ctx).native_error_proto[i] = JS_NULL;
810 }
811 (*ctx).iterator_ctor = JS_NULL;
812 (*ctx).async_iterator_proto = JS_NULL;
813 (*ctx).array_proto_values = JS_NULL;
814 (*ctx).throw_type_error = JS_NULL;
815 (*ctx).eval_obj = JS_NULL;
816 (*ctx).global_obj = JS_NULL;
817 (*ctx).global_var_obj = JS_NULL;
818 (*ctx).random_state = 0;
819 (*ctx).interrupt_counter = 0;
820 (*ctx).loaded_modules.init();
821 (*ctx).compile_regexp = None;
822 (*ctx).eval_internal = None;
823 (*ctx).user_opaque = std::ptr::null_mut();
824 ctx
825 }
826}
827
828pub unsafe fn JS_FreeContext(ctx: *mut JSContext) {
832 if !ctx.is_null() {
833 unsafe { (*(*ctx).rt).js_free_rt(ctx as *mut c_void) };
834 }
835}
836
837pub unsafe fn JS_NewObject(ctx: *mut JSContext) -> JSValue {
840 unsafe {
841 let obj = (*(*ctx).rt).js_malloc_rt(std::mem::size_of::<JSObject>()) as *mut JSObject;
842 if obj.is_null() {
843 return JS_EXCEPTION;
844 }
845 (*obj).header.ref_count = 1;
846 (*obj).header.gc_obj_type = 0;
847 (*obj).header.mark = 0;
848 (*obj).header.dummy0 = 0;
849 (*obj).header.dummy1 = 0;
850 (*obj).header.dummy2 = 0;
851 (*obj).header.link.init();
852 (*obj).shape = (*(*ctx).rt).js_new_shape(std::ptr::null_mut());
853 if (*obj).shape.is_null() {
854 (*(*ctx).rt).js_free_rt(obj as *mut c_void);
855 return JS_EXCEPTION;
856 }
857 (*obj).prop = std::ptr::null_mut();
858 (*obj).first_weak_ref = std::ptr::null_mut();
859 JSValue::new_ptr(JS_TAG_OBJECT, obj as *mut c_void)
860 }
861}
862
863pub unsafe fn JS_NewString(ctx: *mut JSContext, s: &[u16]) -> JSValue {
866 unsafe {
867 let utf8_str = utf16_to_utf8(s);
868 let len = utf8_str.len();
869 if len == 0 {
870 return JSValue::new_ptr(JS_TAG_STRING, std::ptr::null_mut());
872 }
873 let str_size = std::mem::size_of::<JSString>() + len;
874 let p = (*(*ctx).rt).js_malloc_rt(str_size) as *mut JSString;
875 if p.is_null() {
876 return JS_EXCEPTION;
877 }
878 (*p).header.ref_count = 1;
879 (*p).len = len as u32;
880 (*p).hash = 0; (*p).hash_next = 0;
882 let str_data = (p as *mut u8).add(std::mem::size_of::<JSString>());
884 for (i, &byte) in utf8_str.as_bytes().iter().enumerate() {
885 *str_data.add(i) = byte;
886 }
887 JSValue::new_ptr(JS_TAG_STRING, p as *mut c_void)
888 }
889}
890
891pub unsafe fn JS_Eval(_ctx: *mut JSContext, input: *const i8, input_len: usize, _filename: *const i8, _eval_flags: i32) -> JSValue {
894 unsafe {
895 if input_len == 0 {
896 return JS_UNDEFINED;
897 }
898 let s = std::slice::from_raw_parts(input as *const u8, input_len);
899 let script = std::str::from_utf8(s).unwrap_or("");
900
901 match evaluate_script(script.trim()) {
903 Ok(Value::Number(num)) => JSValue::new_float64(num),
904 Ok(Value::String(s)) => JS_NewString(_ctx, &s),
905 Ok(Value::Boolean(b)) => {
906 if b {
907 JS_TRUE
908 } else {
909 JS_FALSE
910 }
911 }
912 Ok(Value::Undefined) => JS_UNDEFINED,
913 Ok(Value::Object(_)) => JS_UNDEFINED, Ok(Value::Function(_)) => JS_UNDEFINED, Ok(Value::Closure(_, _, _)) => 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, Err(_) => JS_UNDEFINED,
922 }
923 }
924}
925
926pub fn evaluate_script<T: AsRef<str>>(script: T) -> Result<Value, JSError> {
927 let script = script.as_ref();
928 log::debug!("evaluate_script async called with script len {}", script.len());
929 let filtered = filter_input_script(script);
930 log::trace!("filtered script:\n{}", filtered);
931 let mut tokens = match tokenize(&filtered) {
932 Ok(t) => t,
933 Err(e) => {
934 log::debug!("tokenize error: {e:?}");
935 return Err(e);
936 }
937 };
938 let statements = match parse_statements(&mut tokens) {
939 Ok(s) => s,
940 Err(e) => {
941 log::debug!("parse_statements error: {e:?}");
942 return Err(e);
943 }
944 };
945 log::debug!("parsed {} statements", statements.len());
946 for (i, stmt) in statements.iter().enumerate() {
947 log::trace!("stmt[{i}] = {stmt:?}");
948 }
949 let env: JSObjectDataPtr = Rc::new(RefCell::new(JSObjectData::new()));
950 env.borrow_mut().is_function_scope = true;
951
952 for line in script.lines() {
955 let l = line.trim();
956 if l.starts_with("import * as")
957 && l.contains("from")
958 && let (Some(as_idx), Some(from_idx)) = (l.find("as"), l.find("from"))
959 {
960 let name_part = &l[as_idx + 2..from_idx].trim();
961 let name = name_part.trim();
962 if let Some(start_quote) = l[from_idx..].find(|c: char| ['"', '\''].contains(&c)) {
963 let quote_char = l[from_idx + start_quote..].chars().next().unwrap();
964 let rest = &l[from_idx + start_quote + 1..];
965 if let Some(end_quote) = rest.find(quote_char) {
966 let module = &rest[..end_quote];
967 if module == "std" {
968 obj_set_value(&env, name, Value::Object(crate::js_std::make_std_object()?))?;
969 } else if module == "os" {
970 obj_set_value(&env, name, Value::Object(crate::js_os::make_os_object()?))?;
971 }
972 }
973 }
974 }
975 }
976
977 initialize_global_constructors(&env);
979
980 match evaluate_statements(&env, &statements) {
981 Ok(v) => {
982 if let Value::Object(obj) = &v
984 && let Some(promise_val_rc) = obj_get_value(obj, "__promise")?
985 && let Value::Promise(promise) = &*promise_val_rc.borrow()
986 {
987 loop {
989 run_event_loop()?;
990 let promise_borrow = promise.borrow();
991 match &promise_borrow.state {
992 PromiseState::Fulfilled(val) => return Ok(val.clone()),
993 PromiseState::Rejected(reason) => {
994 return Err(JSError::EvaluationError {
995 message: format!("Promise rejected: {}", value_to_string(reason)),
996 });
997 }
998 PromiseState::Pending => {
999 }
1001 }
1002 }
1003 }
1004 run_event_loop()?;
1006 Ok(v)
1007 }
1008 Err(e) => {
1009 log::debug!("evaluate_statements error: {e:?}");
1010 Err(e)
1011 }
1012 }
1013}
1014
1015pub fn parse_statements(tokens: &mut Vec<Token>) -> Result<Vec<Statement>, JSError> {
1016 let mut statements = Vec::new();
1017 while !tokens.is_empty() && !matches!(tokens[0], Token::RBrace) {
1018 let stmt = parse_statement(tokens)?;
1019 statements.push(stmt);
1020 if !tokens.is_empty() && matches!(tokens[0], Token::Semicolon) {
1021 tokens.remove(0);
1022 }
1023 }
1024 Ok(statements)
1025}
1026
1027fn parse_statement(tokens: &mut Vec<Token>) -> Result<Statement, JSError> {
1028 if !tokens.is_empty() && matches!(tokens[0], Token::Break) {
1029 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1031 return Err(JSError::ParseError);
1032 }
1033 tokens.remove(0); return Ok(Statement::Break);
1035 }
1036 if !tokens.is_empty() && matches!(tokens[0], Token::Continue) {
1037 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1039 return Err(JSError::ParseError);
1040 }
1041 tokens.remove(0); return Ok(Statement::Continue);
1043 }
1044 if !tokens.is_empty() && matches!(tokens[0], Token::While) {
1045 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1047 return Err(JSError::ParseError);
1048 }
1049 tokens.remove(0); let condition = parse_expression(tokens)?;
1051 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1052 return Err(JSError::ParseError);
1053 }
1054 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1056 return Err(JSError::ParseError);
1057 }
1058 tokens.remove(0); let body = parse_statements(tokens)?;
1060 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1061 return Err(JSError::ParseError);
1062 }
1063 tokens.remove(0); return Ok(Statement::While(condition, body));
1065 }
1066 if !tokens.is_empty() && matches!(tokens[0], Token::Do) {
1067 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1069 return Err(JSError::ParseError);
1070 }
1071 tokens.remove(0); let body = parse_statements(tokens)?;
1073 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1074 return Err(JSError::ParseError);
1075 }
1076 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::While) {
1078 return Err(JSError::ParseError);
1079 }
1080 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1082 return Err(JSError::ParseError);
1083 }
1084 tokens.remove(0); let condition = parse_expression(tokens)?;
1086 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1087 return Err(JSError::ParseError);
1088 }
1089 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1091 return Err(JSError::ParseError);
1092 }
1093 tokens.remove(0); return Ok(Statement::DoWhile(body, condition));
1095 }
1096 if !tokens.is_empty() && matches!(tokens[0], Token::Switch) {
1097 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1099 return Err(JSError::ParseError);
1100 }
1101 tokens.remove(0); let expr = parse_expression(tokens)?;
1103 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1104 return Err(JSError::ParseError);
1105 }
1106 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1108 return Err(JSError::ParseError);
1109 }
1110 tokens.remove(0); let mut cases = Vec::new();
1112 while !tokens.is_empty() && !matches!(tokens[0], Token::RBrace) {
1113 if matches!(tokens[0], Token::Case) {
1114 tokens.remove(0); let case_value = parse_expression(tokens)?;
1116 if tokens.is_empty() || !matches!(tokens[0], Token::Colon) {
1117 return Err(JSError::ParseError);
1118 }
1119 tokens.remove(0); let mut case_stmts = Vec::new();
1121 while !tokens.is_empty()
1122 && !matches!(tokens[0], Token::Case)
1123 && !matches!(tokens[0], Token::Default)
1124 && !matches!(tokens[0], Token::RBrace)
1125 {
1126 let stmt = parse_statement(tokens)?;
1127 case_stmts.push(stmt);
1128 if !tokens.is_empty() && matches!(tokens[0], Token::Semicolon) {
1129 tokens.remove(0);
1130 }
1131 }
1132 cases.push(SwitchCase::Case(case_value, case_stmts));
1133 } else if matches!(tokens[0], Token::Default) {
1134 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::Colon) {
1136 return Err(JSError::ParseError);
1137 }
1138 tokens.remove(0); let mut default_stmts = Vec::new();
1140 while !tokens.is_empty() && !matches!(tokens[0], Token::RBrace) {
1141 let stmt = parse_statement(tokens)?;
1142 default_stmts.push(stmt);
1143 if !tokens.is_empty() && matches!(tokens[0], Token::Semicolon) {
1144 tokens.remove(0);
1145 }
1146 }
1147 cases.push(SwitchCase::Default(default_stmts));
1148 } else {
1149 return Err(JSError::ParseError);
1150 }
1151 }
1152 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1153 return Err(JSError::ParseError);
1154 }
1155 tokens.remove(0); return Ok(Statement::Switch(expr, cases));
1157 }
1158 if !tokens.is_empty() && matches!(tokens[0], Token::Throw) {
1159 tokens.remove(0); let expr = parse_expression(tokens)?;
1161 if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1162 return Err(JSError::ParseError);
1163 }
1164 tokens.remove(0); return Ok(Statement::Throw(expr));
1166 }
1167 if !tokens.is_empty() && matches!(tokens[0], Token::Async) {
1168 tokens.remove(0); if !tokens.is_empty() && matches!(tokens[0], Token::Function) {
1170 tokens.remove(0); if let Some(Token::Identifier(name)) = tokens.first().cloned() {
1172 tokens.remove(0);
1173 if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
1174 tokens.remove(0); let mut params = Vec::new();
1176 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
1177 loop {
1178 if let Some(Token::Identifier(param)) = tokens.first().cloned() {
1179 tokens.remove(0);
1180 params.push(param);
1181 if tokens.is_empty() {
1182 return Err(JSError::ParseError);
1183 }
1184 if matches!(tokens[0], Token::RParen) {
1185 break;
1186 }
1187 if !matches!(tokens[0], Token::Comma) {
1188 return Err(JSError::ParseError);
1189 }
1190 tokens.remove(0); } else {
1192 return Err(JSError::ParseError);
1193 }
1194 }
1195 }
1196 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1197 return Err(JSError::ParseError);
1198 }
1199 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1201 return Err(JSError::ParseError);
1202 }
1203 tokens.remove(0); let body = parse_statements(tokens)?;
1205 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1206 return Err(JSError::ParseError);
1207 }
1208 tokens.remove(0); return Ok(Statement::Let(name, Some(Expr::AsyncFunction(params, body))));
1210 }
1211 }
1212 }
1213 return Err(JSError::ParseError);
1214 }
1215 if !tokens.is_empty() && matches!(tokens[0], Token::Function) {
1216 tokens.remove(0); if let Some(Token::Identifier(name)) = tokens.first().cloned() {
1218 tokens.remove(0);
1219 if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
1220 tokens.remove(0); let mut params = Vec::new();
1222 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
1223 loop {
1224 if let Some(Token::Identifier(param)) = tokens.first().cloned() {
1225 tokens.remove(0);
1226 params.push(param);
1227 if tokens.is_empty() {
1228 return Err(JSError::ParseError);
1229 }
1230 if matches!(tokens[0], Token::RParen) {
1231 break;
1232 }
1233 if !matches!(tokens[0], Token::Comma) {
1234 return Err(JSError::ParseError);
1235 }
1236 tokens.remove(0); } else {
1238 return Err(JSError::ParseError);
1239 }
1240 }
1241 }
1242 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1243 return Err(JSError::ParseError);
1244 }
1245 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1247 return Err(JSError::ParseError);
1248 }
1249 tokens.remove(0); let body = parse_statements(tokens)?;
1251 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1252 return Err(JSError::ParseError);
1253 }
1254 tokens.remove(0); return Ok(Statement::Let(name, Some(Expr::Function(params, body))));
1256 }
1257 }
1258 }
1259 if !tokens.is_empty() && matches!(tokens[0], Token::If) {
1260 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1262 return Err(JSError::ParseError);
1263 }
1264 tokens.remove(0); let condition = parse_expression(tokens)?;
1266 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1267 return Err(JSError::ParseError);
1268 }
1269 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1271 return Err(JSError::ParseError);
1272 }
1273 tokens.remove(0); let then_body = parse_statements(tokens)?;
1275 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1276 return Err(JSError::ParseError);
1277 }
1278 tokens.remove(0); let else_body = if !tokens.is_empty() && matches!(tokens[0], Token::Else) {
1281 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1283 return Err(JSError::ParseError);
1284 }
1285 tokens.remove(0); let body = parse_statements(tokens)?;
1287 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1288 return Err(JSError::ParseError);
1289 }
1290 tokens.remove(0); Some(body)
1292 } else {
1293 None
1294 };
1295
1296 return Ok(Statement::If(condition, then_body, else_body));
1297 }
1298 if !tokens.is_empty() && matches!(tokens[0], Token::Try) {
1299 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1301 return Err(JSError::ParseError);
1302 }
1303 tokens.remove(0); let try_body = parse_statements(tokens)?;
1305 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1306 return Err(JSError::ParseError);
1307 }
1308 tokens.remove(0); let mut catch_param = String::new();
1312 let mut catch_body: Vec<Statement> = Vec::new();
1313 let mut finally_body: Option<Vec<Statement>> = None;
1314
1315 if !tokens.is_empty() && matches!(tokens[0], Token::Catch) {
1316 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1318 return Err(JSError::ParseError);
1319 }
1320 tokens.remove(0); if tokens.is_empty() {
1322 return Err(JSError::ParseError);
1323 }
1324 if let Token::Identifier(name) = tokens.remove(0) {
1325 catch_param = name;
1326 } else {
1327 return Err(JSError::ParseError);
1328 }
1329 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1330 return Err(JSError::ParseError);
1331 }
1332 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1334 return Err(JSError::ParseError);
1335 }
1336 tokens.remove(0); catch_body = parse_statements(tokens)?;
1338 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1339 return Err(JSError::ParseError);
1340 }
1341 tokens.remove(0); }
1343
1344 if !tokens.is_empty() && matches!(tokens[0], Token::Finally) {
1346 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1348 return Err(JSError::ParseError);
1349 }
1350 tokens.remove(0); let fb = parse_statements(tokens)?;
1352 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1353 return Err(JSError::ParseError);
1354 }
1355 tokens.remove(0); finally_body = Some(fb);
1357 }
1358
1359 return Ok(Statement::TryCatch(try_body, catch_param, catch_body, finally_body));
1360 }
1361 if !tokens.is_empty() && matches!(tokens[0], Token::For) {
1362 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1364 return Err(JSError::ParseError);
1365 }
1366 tokens.remove(0); if !tokens.is_empty() && (matches!(tokens[0], Token::Let) || matches!(tokens[0], Token::Var) || matches!(tokens[0], Token::Const)) {
1370 let saved_declaration_token = tokens[0].clone();
1371 tokens.remove(0); if let Some(Token::Identifier(var_name)) = tokens.first().cloned() {
1373 let saved_identifier_token = tokens[0].clone();
1374 tokens.remove(0);
1375 if !tokens.is_empty() && matches!(tokens[0], Token::Identifier(ref s) if s == "of") {
1376 tokens.remove(0); let iterable = parse_expression(tokens)?;
1379 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1380 return Err(JSError::ParseError);
1381 }
1382 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1384 return Err(JSError::ParseError);
1385 }
1386 tokens.remove(0); let body = parse_statements(tokens)?;
1388 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1389 return Err(JSError::ParseError);
1390 }
1391 tokens.remove(0); return Ok(Statement::ForOf(var_name, iterable, body));
1393 } else {
1394 tokens.insert(0, saved_identifier_token);
1396 tokens.insert(0, saved_declaration_token);
1397 }
1398 } else {
1399 tokens.insert(0, saved_declaration_token);
1401 }
1402 }
1403
1404 let init = if !tokens.is_empty() && (matches!(tokens[0], Token::Let) || matches!(tokens[0], Token::Var)) {
1406 Some(Box::new(parse_statement(tokens)?))
1407 } else if !matches!(tokens[0], Token::Semicolon) {
1408 Some(Box::new(Statement::Expr(parse_expression(tokens)?)))
1409 } else {
1410 None
1411 };
1412
1413 if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1414 return Err(JSError::ParseError);
1415 }
1416 tokens.remove(0); let condition = if !matches!(tokens[0], Token::Semicolon) {
1420 Some(parse_expression(tokens)?)
1421 } else {
1422 None
1423 };
1424
1425 if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1426 return Err(JSError::ParseError);
1427 }
1428 tokens.remove(0); let increment = if !matches!(tokens[0], Token::RParen) {
1432 Some(Box::new(Statement::Expr(parse_expression(tokens)?)))
1433 } else {
1434 None
1435 };
1436
1437 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
1438 return Err(JSError::ParseError);
1439 }
1440 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1443 return Err(JSError::ParseError);
1444 }
1445 tokens.remove(0); let body = parse_statements(tokens)?;
1448
1449 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1450 return Err(JSError::ParseError);
1451 }
1452 tokens.remove(0); return Ok(Statement::For(init, condition, increment, body));
1455 }
1456 if !tokens.is_empty() && matches!(tokens[0], Token::Return) {
1457 tokens.remove(0); if tokens.is_empty() || matches!(tokens[0], Token::Semicolon) {
1459 return Ok(Statement::Return(None));
1460 }
1461 let expr = parse_expression(tokens)?;
1462 return Ok(Statement::Return(Some(expr)));
1463 }
1464 if !tokens.is_empty() && (matches!(tokens[0], Token::Let) || matches!(tokens[0], Token::Var) || matches!(tokens[0], Token::Const)) {
1465 let is_const = matches!(tokens[0], Token::Const);
1466 let is_var = matches!(tokens[0], Token::Var);
1467 tokens.remove(0); if !tokens.is_empty() && matches!(tokens[0], Token::LBracket) {
1471 let pattern = parse_array_destructuring_pattern(tokens)?;
1473 if tokens.is_empty() || !matches!(tokens[0], Token::Assign) {
1474 return Err(JSError::ParseError);
1475 }
1476 tokens.remove(0); let expr = parse_expression(tokens)?;
1478 if is_const {
1479 return Ok(Statement::ConstDestructuringArray(pattern, expr));
1480 } else {
1481 return Ok(Statement::LetDestructuringArray(pattern, expr));
1482 }
1483 } else if !tokens.is_empty() && matches!(tokens[0], Token::LBrace) {
1484 let pattern = parse_object_destructuring_pattern(tokens)?;
1486 if tokens.is_empty() || !matches!(tokens[0], Token::Assign) {
1487 return Err(JSError::ParseError);
1488 }
1489 tokens.remove(0); let expr = parse_expression(tokens)?;
1491 if is_const {
1492 return Ok(Statement::ConstDestructuringObject(pattern, expr));
1493 } else {
1494 return Ok(Statement::LetDestructuringObject(pattern, expr));
1495 }
1496 } else {
1497 if let Some(Token::Identifier(name)) = tokens.first().cloned() {
1499 tokens.remove(0);
1500 if !tokens.is_empty() && matches!(tokens[0], Token::Assign) {
1501 tokens.remove(0);
1502 let expr = parse_expression(tokens)?;
1503 if is_const {
1504 return Ok(Statement::Const(name, expr));
1505 } else if is_var {
1506 return Ok(Statement::Var(name, Some(expr)));
1507 } else {
1508 return Ok(Statement::Let(name, Some(expr)));
1509 }
1510 } else if !is_const {
1511 if is_var {
1512 return Ok(Statement::Var(name, None));
1513 } else {
1514 return Ok(Statement::Let(name, None));
1515 }
1516 }
1517 }
1518 }
1519 }
1520 if !tokens.is_empty() && matches!(tokens[0], Token::Class) {
1521 tokens.remove(0); if let Some(Token::Identifier(name)) = tokens.first().cloned() {
1523 tokens.remove(0);
1524 let extends = if !tokens.is_empty() && matches!(tokens[0], Token::Extends) {
1525 tokens.remove(0); if let Some(Token::Identifier(parent_name)) = tokens.first().cloned() {
1527 tokens.remove(0);
1528 Some(parent_name)
1529 } else {
1530 return Err(JSError::ParseError);
1531 }
1532 } else {
1533 None
1534 };
1535
1536 if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1538 return Err(JSError::ParseError);
1539 }
1540 tokens.remove(0); let mut members = Vec::new();
1543 while !tokens.is_empty() && !matches!(tokens[0], Token::RBrace) {
1544 let is_static = if !tokens.is_empty() && matches!(tokens[0], Token::Static) {
1545 tokens.remove(0);
1546 true
1547 } else {
1548 false
1549 };
1550
1551 if let Some(Token::Identifier(method_name)) = tokens.first() {
1552 let method_name = method_name.clone();
1553 if method_name == "constructor" {
1554 tokens.remove(0);
1555 if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1557 return Err(JSError::ParseError);
1558 }
1559 tokens.remove(0); let params = parse_parameters(tokens)?;
1561 if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1562 return Err(JSError::ParseError);
1563 }
1564 tokens.remove(0); let body = parse_statement_block(tokens)?;
1566 members.push(ClassMember::Constructor(params, body));
1567 } else {
1568 tokens.remove(0);
1569 if tokens.is_empty() {
1570 return Err(JSError::ParseError);
1571 }
1572 let is_getter = matches!(tokens[0], Token::Identifier(ref id) if id == "get");
1574 let is_setter = matches!(tokens[0], Token::Identifier(ref id) if id == "set");
1575 if is_getter || is_setter {
1576 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::Identifier(_)) {
1578 return Err(JSError::ParseError);
1579 }
1580 let prop_name = if let Token::Identifier(name) = tokens.remove(0) {
1581 name
1582 } else {
1583 return Err(JSError::ParseError);
1584 };
1585 if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1586 return Err(JSError::ParseError);
1587 }
1588 tokens.remove(0); let params = parse_parameters(tokens)?;
1590 if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1591 return Err(JSError::ParseError);
1592 }
1593 tokens.remove(0); let body = parse_statement_block(tokens)?;
1595 if is_getter {
1596 if !params.is_empty() {
1597 return Err(JSError::ParseError); }
1599 if is_static {
1600 members.push(ClassMember::StaticGetter(prop_name, body));
1601 } else {
1602 members.push(ClassMember::Getter(prop_name, body));
1603 }
1604 } else {
1605 if params.len() != 1 {
1607 return Err(JSError::ParseError); }
1609 if is_static {
1610 members.push(ClassMember::StaticSetter(prop_name, params, body));
1611 } else {
1612 members.push(ClassMember::Setter(prop_name, params, body));
1613 }
1614 }
1615 } else if matches!(tokens[0], Token::LParen) {
1616 tokens.remove(0); let params = parse_parameters(tokens)?;
1619 if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1620 return Err(JSError::ParseError);
1621 }
1622 tokens.remove(0); let body = parse_statement_block(tokens)?;
1624 if is_static {
1625 members.push(ClassMember::StaticMethod(method_name, params, body));
1626 } else {
1627 members.push(ClassMember::Method(method_name, params, body));
1628 }
1629 } else if matches!(tokens[0], Token::Assign) {
1630 tokens.remove(0); let value = parse_expression(tokens)?;
1633 if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1634 return Err(JSError::ParseError);
1635 }
1636 tokens.remove(0); if is_static {
1638 members.push(ClassMember::StaticProperty(method_name, value));
1639 } else {
1640 members.push(ClassMember::Property(method_name, value));
1641 }
1642 } else {
1643 return Err(JSError::ParseError);
1644 }
1645 }
1646 } else {
1647 return Err(JSError::ParseError);
1648 }
1649 }
1650
1651 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
1652 return Err(JSError::ParseError);
1653 }
1654 tokens.remove(0); return Ok(Statement::Class(name, extends, members));
1657 }
1658 }
1659 let expr = parse_expression(tokens)?;
1660 if let Expr::Assign(target, value) = &expr
1662 && let Expr::Var(name) = target.as_ref()
1663 {
1664 return Ok(Statement::Assign(name.clone(), *value.clone()));
1665 }
1666 Ok(Statement::Expr(expr))
1667}
1668
1669#[derive(Clone, Debug)]
1670pub enum ControlFlow {
1671 Normal(Value),
1672 Break,
1673 Continue,
1674 Return(Value),
1675}
1676
1677pub fn evaluate_statements(env: &JSObjectDataPtr, statements: &[Statement]) -> Result<Value, JSError> {
1678 match evaluate_statements_with_context(env, statements)? {
1679 ControlFlow::Normal(val) => Ok(val),
1680 ControlFlow::Break => Err(JSError::EvaluationError {
1681 message: "break statement not in loop or switch".to_string(),
1682 }),
1683 ControlFlow::Continue => Err(JSError::EvaluationError {
1684 message: "continue statement not in loop".to_string(),
1685 }),
1686 ControlFlow::Return(val) => Ok(val),
1687 }
1688}
1689
1690fn evaluate_statements_with_context(env: &JSObjectDataPtr, statements: &[Statement]) -> Result<ControlFlow, JSError> {
1691 if env.borrow().is_function_scope {
1693 let mut var_names = std::collections::HashSet::new();
1694 collect_var_names(statements, &mut var_names);
1695 for name in var_names {
1696 env_set(env, &name, Value::Undefined)?;
1697 }
1698 }
1699
1700 let mut last_value = Value::Number(0.0);
1701 for (i, stmt) in statements.iter().enumerate() {
1702 log::trace!("Evaluating statement {i}: {stmt:?}");
1703 let eval_res: Result<Option<ControlFlow>, JSError> = (|| -> Result<Option<ControlFlow>, JSError> {
1710 match stmt {
1711 Statement::Let(name, expr_opt) => {
1712 let val = expr_opt.clone().map_or(Ok(Value::Undefined), |expr| evaluate_expr(env, &expr))?;
1713 env_set(env, name.as_str(), val.clone())?;
1714 last_value = val;
1715 Ok(None)
1716 }
1717 Statement::Var(name, expr_opt) => {
1718 let val = expr_opt.clone().map_or(Ok(Value::Undefined), |expr| evaluate_expr(env, &expr))?;
1719 env_set_var(env, name.as_str(), val.clone())?;
1720 last_value = val;
1721 Ok(None)
1722 }
1723 Statement::Const(name, expr) => {
1724 let val = evaluate_expr(env, expr)?;
1725 env_set_const(env, name.as_str(), val.clone());
1726 last_value = val;
1727 Ok(None)
1728 }
1729 Statement::Class(name, extends, members) => {
1730 let class_obj = create_class_object(name, extends, members, env)?;
1731 env_set(env, name.as_str(), class_obj)?;
1732 last_value = Value::Undefined;
1733 Ok(None)
1734 }
1735 Statement::Assign(name, expr) => {
1736 let val = evaluate_expr(env, expr)?;
1737 env_set_recursive(env, name.as_str(), val.clone())?;
1738 last_value = val;
1739 Ok(None)
1740 }
1741 Statement::Expr(expr) => {
1742 if let Expr::Assign(target, value_expr) = expr {
1748 match target.as_ref() {
1749 Expr::Var(name) => {
1750 let v = evaluate_expr(env, value_expr)?;
1751 env_set_recursive(env, name.as_str(), v.clone())?;
1752 last_value = v;
1753 }
1754 Expr::Property(obj_expr, prop_name) => {
1755 let v = evaluate_expr(env, value_expr)?;
1756 match set_prop_env(env, obj_expr, prop_name.as_str(), v.clone())? {
1760 Some(updated_obj) => last_value = updated_obj,
1761 None => last_value = v,
1762 }
1763 }
1764 Expr::Index(obj_expr, idx_expr) => {
1765 let idx_val = evaluate_expr(env, idx_expr)?;
1767 let key = match idx_val {
1768 Value::Number(n) => n.to_string(),
1769 Value::String(s) => String::from_utf16_lossy(&s),
1770 _ => {
1771 return Err(JSError::EvaluationError {
1772 message: "Invalid index type".to_string(),
1773 });
1774 }
1775 };
1776 let v = evaluate_expr(env, value_expr)?;
1777 match set_prop_env(env, obj_expr, &key, v.clone())? {
1778 Some(updated_obj) => last_value = updated_obj,
1779 None => last_value = v,
1780 }
1781 }
1782 _ => {
1783 last_value = evaluate_expr(env, expr)?;
1785 }
1786 }
1787 } else if let Expr::LogicalAndAssign(target, value_expr) = expr {
1788 let left_val = evaluate_expr(env, target)?;
1790 if is_truthy(&left_val) {
1791 match target.as_ref() {
1792 Expr::Var(name) => {
1793 let v = evaluate_expr(env, value_expr)?;
1794 env_set_recursive(env, name.as_str(), v.clone())?;
1795 last_value = v;
1796 }
1797 Expr::Property(obj_expr, prop_name) => {
1798 let v = evaluate_expr(env, value_expr)?;
1799 match set_prop_env(env, obj_expr, prop_name.as_str(), v.clone())? {
1800 Some(updated_obj) => last_value = updated_obj,
1801 None => last_value = v,
1802 }
1803 }
1804 Expr::Index(obj_expr, idx_expr) => {
1805 let idx_val = evaluate_expr(env, idx_expr)?;
1806 let key = match idx_val {
1807 Value::Number(n) => n.to_string(),
1808 Value::String(s) => String::from_utf16_lossy(&s),
1809 _ => {
1810 return Err(JSError::EvaluationError {
1811 message: "Invalid index type".to_string(),
1812 });
1813 }
1814 };
1815 let v = evaluate_expr(env, value_expr)?;
1816 match set_prop_env(env, obj_expr, &key, v.clone())? {
1817 Some(updated_obj) => last_value = updated_obj,
1818 None => last_value = v,
1819 }
1820 }
1821 _ => {
1822 last_value = evaluate_expr(env, expr)?;
1823 }
1824 }
1825 } else {
1826 last_value = left_val;
1827 }
1828 } else if let Expr::LogicalOrAssign(target, value_expr) = expr {
1829 let left_val = evaluate_expr(env, target)?;
1831 if !is_truthy(&left_val) {
1832 match target.as_ref() {
1833 Expr::Var(name) => {
1834 let v = evaluate_expr(env, value_expr)?;
1835 env_set_recursive(env, name.as_str(), v.clone())?;
1836 last_value = v;
1837 }
1838 Expr::Property(obj_expr, prop_name) => {
1839 let v = evaluate_expr(env, value_expr)?;
1840 match set_prop_env(env, obj_expr, prop_name.as_str(), v.clone())? {
1841 Some(updated_obj) => last_value = updated_obj,
1842 None => last_value = v,
1843 }
1844 }
1845 Expr::Index(obj_expr, idx_expr) => {
1846 let idx_val = evaluate_expr(env, idx_expr)?;
1847 let key = match idx_val {
1848 Value::Number(n) => n.to_string(),
1849 Value::String(s) => String::from_utf16_lossy(&s),
1850 _ => {
1851 return Err(JSError::EvaluationError {
1852 message: "Invalid index type".to_string(),
1853 });
1854 }
1855 };
1856 let v = evaluate_expr(env, value_expr)?;
1857 match set_prop_env(env, obj_expr, &key, v.clone())? {
1858 Some(updated_obj) => last_value = updated_obj,
1859 None => last_value = v,
1860 }
1861 }
1862 _ => {
1863 last_value = evaluate_expr(env, expr)?;
1864 }
1865 }
1866 } else {
1867 last_value = left_val;
1868 }
1869 } else if let Expr::NullishAssign(target, value_expr) = expr {
1870 let left_val = evaluate_expr(env, target)?;
1872 match left_val {
1873 Value::Undefined => match target.as_ref() {
1874 Expr::Var(name) => {
1875 let v = evaluate_expr(env, value_expr)?;
1876 env_set_recursive(env, name.as_str(), v.clone())?;
1877 last_value = v;
1878 }
1879 Expr::Property(obj_expr, prop_name) => {
1880 let v = evaluate_expr(env, value_expr)?;
1881 match set_prop_env(env, obj_expr, prop_name.as_str(), v.clone())? {
1882 Some(updated_obj) => last_value = updated_obj,
1883 None => last_value = v,
1884 }
1885 }
1886 Expr::Index(obj_expr, idx_expr) => {
1887 let idx_val = evaluate_expr(env, idx_expr)?;
1888 let key = match idx_val {
1889 Value::Number(n) => n.to_string(),
1890 Value::String(s) => String::from_utf16_lossy(&s),
1891 _ => {
1892 return Err(JSError::EvaluationError {
1893 message: "Invalid index type".to_string(),
1894 });
1895 }
1896 };
1897 let v = evaluate_expr(env, value_expr)?;
1898 match set_prop_env(env, obj_expr, &key, v.clone())? {
1899 Some(updated_obj) => last_value = updated_obj,
1900 None => last_value = v,
1901 }
1902 }
1903 _ => {
1904 last_value = evaluate_expr(env, expr)?;
1905 }
1906 },
1907 _ => {
1908 last_value = left_val;
1909 }
1910 }
1911 } else {
1912 last_value = evaluate_expr(env, expr)?;
1913 }
1914 Ok(None)
1915 }
1916 Statement::Return(expr_opt) => {
1917 let return_val = match expr_opt {
1918 Some(expr) => evaluate_expr(env, expr)?,
1919 None => Value::Undefined,
1920 };
1921 Ok(Some(ControlFlow::Return(return_val)))
1922 }
1923 Statement::Throw(expr) => {
1924 let throw_val = evaluate_expr(env, expr)?;
1925 Err(JSError::Throw { value: throw_val })
1926 }
1927 Statement::If(condition, then_body, else_body) => {
1928 let cond_val = evaluate_expr(env, condition)?;
1929 if is_truthy(&cond_val) {
1930 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
1932 block_env.borrow_mut().prototype = Some(env.clone());
1933 block_env.borrow_mut().is_function_scope = false;
1934 match evaluate_statements_with_context(&block_env, then_body)? {
1935 ControlFlow::Normal(val) => last_value = val,
1936 cf => return Ok(Some(cf)),
1937 }
1938 } else if let Some(else_stmts) = else_body {
1939 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
1940 block_env.borrow_mut().prototype = Some(env.clone());
1941 block_env.borrow_mut().is_function_scope = false;
1942 match evaluate_statements_with_context(&block_env, else_stmts)? {
1943 ControlFlow::Normal(val) => last_value = val,
1944 cf => return Ok(Some(cf)),
1945 }
1946 }
1947 Ok(None)
1948 }
1949 Statement::TryCatch(try_body, catch_param, catch_body, finally_body_opt) => {
1950 match evaluate_statements_with_context(env, try_body) {
1952 Ok(ControlFlow::Normal(v)) => last_value = v,
1953 Ok(cf) => match cf {
1954 ControlFlow::Return(val) => return Ok(Some(ControlFlow::Return(val))),
1955 ControlFlow::Break => return Ok(Some(ControlFlow::Break)),
1956 ControlFlow::Continue => return Ok(Some(ControlFlow::Continue)),
1957 _ => unreachable!(),
1958 },
1959 Err(err) => {
1960 if catch_param.is_empty() {
1961 if let Some(finally_body) = finally_body_opt {
1962 evaluate_statements_with_context(env, finally_body)?;
1963 }
1964 return Err(err);
1965 } else {
1966 let catch_env = Rc::new(RefCell::new(JSObjectData::new()));
1967 catch_env.borrow_mut().prototype = Some(env.clone());
1968 catch_env.borrow_mut().is_function_scope = false;
1969 let catch_value = match &err {
1970 JSError::Throw { value } => value.clone(),
1971 _ => Value::String(utf8_to_utf16(&err.to_string())),
1972 };
1973 env_set(&catch_env, catch_param.as_str(), catch_value)?;
1974 match evaluate_statements_with_context(&catch_env, catch_body)? {
1975 ControlFlow::Normal(val) => last_value = val,
1976 cf => {
1977 if let Some(finally_body) = finally_body_opt {
1978 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
1979 block_env.borrow_mut().prototype = Some(env.clone());
1980 block_env.borrow_mut().is_function_scope = false;
1981 evaluate_statements_with_context(&block_env, finally_body)?;
1982 }
1983 return Ok(Some(cf));
1984 }
1985 }
1986 }
1987 }
1988 }
1989 if let Some(finally_body) = finally_body_opt {
1991 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
1992 block_env.borrow_mut().prototype = Some(env.clone());
1993 block_env.borrow_mut().is_function_scope = false;
1994 match evaluate_statements_with_context(&block_env, finally_body)? {
1995 ControlFlow::Normal(val) => last_value = val,
1996 cf => return Ok(Some(cf)),
1997 }
1998 }
1999 Ok(None)
2000 }
2001 Statement::For(init, condition, increment, body) => {
2002 let for_env = Rc::new(RefCell::new(JSObjectData::new()));
2003 for_env.borrow_mut().prototype = Some(env.clone());
2004 for_env.borrow_mut().is_function_scope = false;
2005 if let Some(init_stmt) = init {
2007 match init_stmt.as_ref() {
2008 Statement::Let(name, expr_opt) => {
2009 let val = expr_opt
2010 .clone()
2011 .map_or(Ok(Value::Undefined), |expr| evaluate_expr(&for_env, &expr))?;
2012 env_set(&for_env, name.as_str(), val)?;
2013 }
2014 Statement::Var(name, expr_opt) => {
2015 let val = expr_opt
2016 .clone()
2017 .map_or(Ok(Value::Undefined), |expr| evaluate_expr(&for_env, &expr))?;
2018 env_set_var(&for_env, name.as_str(), val)?;
2019 }
2020 Statement::Expr(expr) => {
2021 evaluate_expr(&for_env, expr)?;
2022 }
2023 _ => {
2024 return Err(JSError::EvaluationError {
2025 message: "error".to_string(),
2026 });
2027 } }
2029 }
2030
2031 loop {
2032 let should_continue = if let Some(cond_expr) = condition {
2034 let cond_val = evaluate_expr(&for_env, cond_expr)?;
2035 is_truthy(&cond_val)
2036 } else {
2037 true };
2039
2040 if !should_continue {
2041 break;
2042 }
2043
2044 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
2046 block_env.borrow_mut().prototype = Some(for_env.clone());
2047 block_env.borrow_mut().is_function_scope = false;
2048 match evaluate_statements_with_context(&block_env, body)? {
2049 ControlFlow::Normal(val) => last_value = val,
2050 ControlFlow::Break => break,
2051 ControlFlow::Continue => {}
2052 ControlFlow::Return(val) => return Ok(Some(ControlFlow::Return(val))),
2053 }
2054
2055 if let Some(incr_stmt) = increment {
2057 match incr_stmt.as_ref() {
2058 Statement::Expr(expr) => match expr {
2059 Expr::Assign(target, value) => {
2060 if let Expr::Var(name) = target.as_ref() {
2061 let val = evaluate_expr(&for_env, value)?;
2062 env_set_recursive(&for_env, name.as_str(), val)?;
2063 }
2064 }
2065 _ => {
2066 evaluate_expr(&for_env, expr)?;
2067 }
2068 },
2069 _ => {
2070 return Err(JSError::EvaluationError {
2071 message: "error".to_string(),
2072 });
2073 } }
2075 }
2076 }
2077 Ok(None)
2078 }
2079 Statement::ForOf(var, iterable, body) => {
2080 let iterable_val = evaluate_expr(env, iterable)?;
2081 match iterable_val {
2082 Value::Object(obj_map) => {
2083 if is_array(&obj_map) {
2084 let len = get_array_length(&obj_map).unwrap_or(0);
2085 for i in 0..len {
2086 let key = i.to_string();
2087 if let Some(element_rc) = obj_get_value(&obj_map, &key)? {
2088 let element = element_rc.borrow().clone();
2089 env_set_recursive(env, var.as_str(), element)?;
2090 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
2091 block_env.borrow_mut().prototype = Some(env.clone());
2092 block_env.borrow_mut().is_function_scope = false;
2093 match evaluate_statements_with_context(&block_env, body)? {
2094 ControlFlow::Normal(val) => last_value = val,
2095 ControlFlow::Break => break,
2096 ControlFlow::Continue => {}
2097 ControlFlow::Return(val) => return Ok(Some(ControlFlow::Return(val))),
2098 }
2099 }
2100 }
2101 Ok(None)
2102 } else {
2103 Err(JSError::EvaluationError {
2104 message: "for-of loop requires an iterable".to_string(),
2105 })
2106 }
2107 }
2108 _ => Err(JSError::EvaluationError {
2109 message: "for-of loop requires an iterable".to_string(),
2110 }),
2111 }
2112 }
2113 Statement::While(condition, body) => {
2114 loop {
2115 let cond_val = evaluate_expr(env, condition)?;
2117 if !is_truthy(&cond_val) {
2118 break Ok(None);
2119 }
2120
2121 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
2123 block_env.borrow_mut().prototype = Some(env.clone());
2124 block_env.borrow_mut().is_function_scope = false;
2125 match evaluate_statements_with_context(&block_env, body)? {
2126 ControlFlow::Normal(val) => last_value = val,
2127 ControlFlow::Break => break Ok(None),
2128 ControlFlow::Continue => {}
2129 ControlFlow::Return(val) => return Ok(Some(ControlFlow::Return(val))),
2130 }
2131 }
2132 }
2133 Statement::DoWhile(body, condition) => {
2134 loop {
2135 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
2137 block_env.borrow_mut().prototype = Some(env.clone());
2138 block_env.borrow_mut().is_function_scope = false;
2139 match evaluate_statements_with_context(&block_env, body)? {
2140 ControlFlow::Normal(val) => last_value = val,
2141 ControlFlow::Break => break Ok(None),
2142 ControlFlow::Continue => {}
2143 ControlFlow::Return(val) => return Ok(Some(ControlFlow::Return(val))),
2144 }
2145
2146 let cond_val = evaluate_expr(env, condition)?;
2148 if !is_truthy(&cond_val) {
2149 break Ok(None);
2150 }
2151 }
2152 }
2153 Statement::Switch(expr, cases) => {
2154 let switch_val = evaluate_expr(env, expr)?;
2155 let mut found_match = false;
2156 let mut executed_default = false;
2157
2158 for case in cases {
2159 match case {
2160 SwitchCase::Case(case_expr, case_stmts) => {
2161 if !found_match {
2162 let case_val = evaluate_expr(env, case_expr)?;
2163 if values_equal(&switch_val, &case_val) {
2165 found_match = true;
2166 }
2167 }
2168 if found_match {
2169 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
2170 block_env.borrow_mut().prototype = Some(env.clone());
2171 block_env.borrow_mut().is_function_scope = false;
2172 match evaluate_statements_with_context(&block_env, case_stmts)? {
2173 ControlFlow::Normal(val) => last_value = val,
2174 ControlFlow::Break => break,
2175 cf => return Ok(Some(cf)),
2176 }
2177 }
2178 }
2179 SwitchCase::Default(default_stmts) => {
2180 if !found_match && !executed_default {
2181 executed_default = true;
2182 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
2183 block_env.borrow_mut().prototype = Some(env.clone());
2184 block_env.borrow_mut().is_function_scope = false;
2185 match evaluate_statements_with_context(&block_env, default_stmts)? {
2186 ControlFlow::Normal(val) => last_value = val,
2187 ControlFlow::Break => break,
2188 cf => return Ok(Some(cf)),
2189 }
2190 } else if found_match {
2191 let block_env = Rc::new(RefCell::new(JSObjectData::new()));
2193 block_env.borrow_mut().prototype = Some(env.clone());
2194 block_env.borrow_mut().is_function_scope = false;
2195 match evaluate_statements_with_context(&block_env, default_stmts)? {
2196 ControlFlow::Normal(val) => last_value = val,
2197 ControlFlow::Break => break,
2198 cf => return Ok(Some(cf)),
2199 }
2200 }
2201 }
2202 }
2203 }
2204 Ok(None)
2205 }
2206 Statement::Break => Ok(Some(ControlFlow::Break)),
2207 Statement::Continue => Ok(Some(ControlFlow::Continue)),
2208 Statement::LetDestructuringArray(pattern, expr) => {
2209 let val = evaluate_expr(env, expr)?;
2210 perform_array_destructuring(env, pattern, &val, false)?;
2211 last_value = val;
2212 Ok(None)
2213 }
2214 Statement::ConstDestructuringArray(pattern, expr) => {
2215 let val = evaluate_expr(env, expr)?;
2216 perform_array_destructuring(env, pattern, &val, true)?;
2217 last_value = val;
2218 Ok(None)
2219 }
2220 Statement::LetDestructuringObject(pattern, expr) => {
2221 let val = evaluate_expr(env, expr)?;
2222 perform_object_destructuring(env, pattern, &val, false)?;
2223 last_value = val;
2224 Ok(None)
2225 }
2226 Statement::ConstDestructuringObject(pattern, expr) => {
2227 let val = evaluate_expr(env, expr)?;
2228 perform_object_destructuring(env, pattern, &val, true)?;
2229 last_value = val;
2230 Ok(None)
2231 }
2232 }
2233 })();
2234 match eval_res {
2235 Ok(Some(cf)) => return Ok(cf),
2236 Ok(None) => {}
2237 Err(e) => {
2238 log::error!("evaluate_statements_with_context error at statement {}: {:?} stmt={:?}", i, e, stmt);
2239 return Err(e);
2240 }
2241 }
2242 }
2243 Ok(ControlFlow::Normal(last_value))
2244}
2245
2246fn perform_array_destructuring(
2247 env: &JSObjectDataPtr,
2248 pattern: &Vec<DestructuringElement>,
2249 value: &Value,
2250 is_const: bool,
2251) -> Result<(), JSError> {
2252 match value {
2253 Value::Object(arr) if is_array(arr) => {
2254 let mut index = 0;
2255 let mut rest_index = None;
2256 let mut rest_var = None;
2257
2258 for element in pattern {
2259 match element {
2260 DestructuringElement::Variable(var) => {
2261 let key = index.to_string();
2262 let val = if let Some(val_rc) = obj_get_value(arr, &key)? {
2263 val_rc.borrow().clone()
2264 } else {
2265 Value::Undefined
2266 };
2267 if is_const {
2268 env_set_const(env, var, val);
2269 } else {
2270 env_set(env, var, val)?;
2271 }
2272 index += 1;
2273 }
2274 DestructuringElement::NestedArray(nested_pattern) => {
2275 let key = index.to_string();
2276 let val = if let Some(val_rc) = obj_get_value(arr, &key)? {
2277 val_rc.borrow().clone()
2278 } else {
2279 Value::Undefined
2280 };
2281 perform_array_destructuring(env, nested_pattern, &val, is_const)?;
2282 index += 1;
2283 }
2284 DestructuringElement::NestedObject(nested_pattern) => {
2285 let key = index.to_string();
2286 let val = if let Some(val_rc) = obj_get_value(arr, &key)? {
2287 val_rc.borrow().clone()
2288 } else {
2289 Value::Undefined
2290 };
2291 perform_object_destructuring(env, nested_pattern, &val, is_const)?;
2292 index += 1;
2293 }
2294 DestructuringElement::Rest(var) => {
2295 rest_index = Some(index);
2296 rest_var = Some(var.clone());
2297 break;
2298 }
2299 DestructuringElement::Empty => {
2300 index += 1;
2301 }
2302 }
2303 }
2304
2305 if let (Some(rest_start), Some(var)) = (rest_index, rest_var) {
2307 let mut rest_elements: Vec<Value> = Vec::new();
2308 let len = get_array_length(arr).unwrap_or(0);
2309 for i in rest_start..len {
2310 let key = i.to_string();
2311 if let Some(val_rc) = obj_get_value(arr, &key)? {
2312 rest_elements.push(val_rc.borrow().clone());
2313 }
2314 }
2315 let rest_obj = Rc::new(RefCell::new(JSObjectData::new()));
2316 let mut rest_index = 0;
2317 for elem in rest_elements {
2318 obj_set_value(&rest_obj, rest_index.to_string(), elem)?;
2319 rest_index += 1;
2320 }
2321 set_array_length(&rest_obj, rest_index)?;
2322 let rest_value = Value::Object(rest_obj);
2323 if is_const {
2324 env_set_const(env, &var, rest_value);
2325 } else {
2326 env_set(env, &var, rest_value)?;
2327 }
2328 }
2329 }
2330 _ => {
2331 return Err(JSError::EvaluationError {
2332 message: "Cannot destructure non-array value".to_string(),
2333 });
2334 }
2335 }
2336 Ok(())
2337}
2338
2339fn perform_object_destructuring(
2340 env: &JSObjectDataPtr,
2341 pattern: &Vec<ObjectDestructuringElement>,
2342 value: &Value,
2343 is_const: bool,
2344) -> Result<(), JSError> {
2345 match value {
2346 Value::Object(obj) => {
2347 for element in pattern {
2348 match element {
2349 ObjectDestructuringElement::Property { key, value: dest } => {
2350 let prop_val = if let Some(val_rc) = obj_get_value(obj, key)? {
2351 val_rc.borrow().clone()
2352 } else {
2353 Value::Undefined
2354 };
2355 match dest {
2356 DestructuringElement::Variable(var) => {
2357 if is_const {
2358 env_set_const(env, var, prop_val);
2359 } else {
2360 env_set(env, var, prop_val)?;
2361 }
2362 }
2363 DestructuringElement::NestedArray(nested_pattern) => {
2364 perform_array_destructuring(env, nested_pattern, &prop_val, is_const)?;
2365 }
2366 DestructuringElement::NestedObject(nested_pattern) => {
2367 perform_object_destructuring(env, nested_pattern, &prop_val, is_const)?;
2368 }
2369 _ => {
2370 return Err(JSError::EvaluationError {
2372 message: "Invalid destructuring pattern".to_string(),
2373 });
2374 }
2375 }
2376 }
2377 ObjectDestructuringElement::Rest(var) => {
2378 let rest_obj = Rc::new(RefCell::new(JSObjectData::new()));
2380 let mut assigned_keys = std::collections::HashSet::new();
2381
2382 for element in pattern {
2384 if let ObjectDestructuringElement::Property { key, .. } = element {
2385 assigned_keys.insert(key.clone());
2386 }
2387 }
2388
2389 for (key, val_rc) in obj.borrow().properties.iter() {
2391 if !assigned_keys.contains(key) {
2392 rest_obj.borrow_mut().insert(key.clone(), val_rc.clone());
2393 }
2394 }
2395
2396 let rest_value = Value::Object(rest_obj);
2397 if is_const {
2398 env_set_const(env, var, rest_value);
2399 } else {
2400 env_set(env, var, rest_value)?;
2401 }
2402 }
2403 }
2404 }
2405 }
2406 _ => {
2407 return Err(JSError::EvaluationError {
2408 message: "Cannot destructure non-object value".to_string(),
2409 });
2410 }
2411 }
2412 Ok(())
2413}
2414
2415pub fn evaluate_expr(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
2416 match expr {
2417 Expr::Number(n) => evaluate_number(*n),
2418 Expr::StringLit(s) => evaluate_string_lit(s),
2419 Expr::Boolean(b) => evaluate_boolean(*b),
2420 Expr::Var(name) => evaluate_var(env, name),
2421 Expr::Assign(_target, value) => evaluate_assign(env, value),
2422 Expr::LogicalAndAssign(target, value) => evaluate_logical_and_assign(env, target, value),
2423 Expr::LogicalOrAssign(target, value) => evaluate_logical_or_assign(env, target, value),
2424 Expr::NullishAssign(target, value) => evaluate_nullish_assign(env, target, value),
2425 Expr::AddAssign(target, value) => evaluate_add_assign(env, target, value),
2426 Expr::SubAssign(target, value) => evaluate_sub_assign(env, target, value),
2427 Expr::MulAssign(target, value) => evaluate_mul_assign(env, target, value),
2428 Expr::DivAssign(target, value) => evaluate_div_assign(env, target, value),
2429 Expr::ModAssign(target, value) => evaluate_mod_assign(env, target, value),
2430 Expr::Increment(expr) => evaluate_increment(env, expr),
2431 Expr::Decrement(expr) => evaluate_decrement(env, expr),
2432 Expr::PostIncrement(expr) => evaluate_post_increment(env, expr),
2433 Expr::PostDecrement(expr) => evaluate_post_decrement(env, expr),
2434 Expr::UnaryNeg(expr) => evaluate_unary_neg(env, expr),
2435 Expr::TypeOf(expr) => evaluate_typeof(env, expr),
2436 Expr::Delete(expr) => evaluate_delete(env, expr),
2437 Expr::Void(expr) => evaluate_void(env, expr),
2438 Expr::Binary(left, op, right) => evaluate_binary(env, left, op, right),
2439 Expr::Index(obj, idx) => evaluate_index(env, obj, idx),
2440 Expr::Property(obj, prop) => evaluate_property(env, obj, prop),
2441 Expr::Call(func_expr, args) => evaluate_call(env, func_expr, args),
2442 Expr::Function(params, body) => Ok(Value::Closure(params.clone(), body.clone(), env.clone())),
2443 Expr::ArrowFunction(params, body) => Ok(Value::Closure(params.clone(), body.clone(), env.clone())),
2444 Expr::Object(properties) => evaluate_object(env, properties),
2445 Expr::Array(elements) => evaluate_array(env, elements),
2446 Expr::Getter(func_expr) => evaluate_expr(env, func_expr),
2447 Expr::Setter(func_expr) => evaluate_expr(env, func_expr),
2448 Expr::Spread(_expr) => Err(JSError::EvaluationError {
2449 message: "Spread operator must be used in array, object, or function call context".to_string(),
2450 }),
2451 Expr::OptionalProperty(obj, prop) => evaluate_optional_property(env, obj, prop),
2452 Expr::OptionalCall(func_expr, args) => evaluate_optional_call(env, func_expr, args),
2453 Expr::This => evaluate_this(env),
2454 Expr::New(constructor, args) => evaluate_new(env, constructor, args),
2455 Expr::Super => evaluate_super(env),
2456 Expr::SuperCall(args) => evaluate_super_call(env, args),
2457 Expr::SuperProperty(prop) => evaluate_super_property(env, prop),
2458 Expr::SuperMethod(method, args) => evaluate_super_method(env, method, args),
2459 Expr::ArrayDestructuring(pattern) => evaluate_array_destructuring(env, pattern),
2460 Expr::ObjectDestructuring(pattern) => evaluate_object_destructuring(env, pattern),
2461 Expr::AsyncFunction(params, body) => Ok(Value::Closure(params.clone(), body.clone(), env.clone())),
2462 Expr::Await(expr) => {
2463 let promise_val = evaluate_expr(env, expr)?;
2464 match promise_val {
2465 Value::Promise(promise) => {
2466 loop {
2468 run_event_loop()?;
2469 let promise_borrow = promise.borrow();
2470 match &promise_borrow.state {
2471 PromiseState::Fulfilled(val) => return Ok(val.clone()),
2472 PromiseState::Rejected(reason) => {
2473 return Err(JSError::EvaluationError {
2474 message: format!("Promise rejected: {}", value_to_string(reason)),
2475 });
2476 }
2477 PromiseState::Pending => {
2478 }
2480 }
2481 }
2482 }
2483 Value::Object(obj) => {
2484 if let Some(promise_rc) = obj_get_value(&obj, "__promise")?
2486 && let Value::Promise(promise) = promise_rc.borrow().clone()
2487 {
2488 loop {
2490 run_event_loop()?;
2491 let promise_borrow = promise.borrow();
2492 match &promise_borrow.state {
2493 PromiseState::Fulfilled(val) => return Ok(val.clone()),
2494 PromiseState::Rejected(reason) => {
2495 return Err(JSError::EvaluationError {
2496 message: format!("Promise rejected: {}", value_to_string(reason)),
2497 });
2498 }
2499 PromiseState::Pending => {
2500 }
2502 }
2503 }
2504 }
2505 Err(JSError::EvaluationError {
2506 message: "await can only be used with promises".to_string(),
2507 })
2508 }
2509 _ => Err(JSError::EvaluationError {
2510 message: "await can only be used with promises".to_string(),
2511 }),
2512 }
2513 }
2514 Expr::Value(value) => Ok(value.clone()),
2515 }
2516}
2517
2518fn evaluate_number(n: f64) -> Result<Value, JSError> {
2519 Ok(Value::Number(n))
2520}
2521
2522fn evaluate_string_lit(s: &[u16]) -> Result<Value, JSError> {
2523 Ok(Value::String(s.to_vec()))
2524}
2525
2526fn evaluate_boolean(b: bool) -> Result<Value, JSError> {
2527 Ok(Value::Boolean(b))
2528}
2529
2530fn evaluate_var(env: &JSObjectDataPtr, name: &str) -> Result<Value, JSError> {
2531 if name == "console" {
2532 Ok(Value::Object(js_console::make_console_object()?))
2533 } else if name == "String" {
2534 Ok(Value::Function("String".to_string()))
2535 } else if name == "Math" {
2536 Ok(Value::Object(js_math::make_math_object()?))
2537 } else if name == "JSON" {
2538 let json_obj = Rc::new(RefCell::new(JSObjectData::new()));
2539 obj_set_value(&json_obj, "parse", Value::Function("JSON.parse".to_string()))?;
2540 obj_set_value(&json_obj, "stringify", Value::Function("JSON.stringify".to_string()))?;
2541 Ok(Value::Object(json_obj))
2542 } else if name == "Object" {
2543 Ok(Value::Function("Object".to_string()))
2545 } else if name == "parseInt" {
2546 Ok(Value::Function("parseInt".to_string()))
2547 } else if name == "parseFloat" {
2548 Ok(Value::Function("parseFloat".to_string()))
2549 } else if name == "isNaN" {
2550 Ok(Value::Function("isNaN".to_string()))
2551 } else if name == "isFinite" {
2552 Ok(Value::Function("isFinite".to_string()))
2553 } else if name == "encodeURIComponent" {
2554 Ok(Value::Function("encodeURIComponent".to_string()))
2555 } else if name == "decodeURIComponent" {
2556 Ok(Value::Function("decodeURIComponent".to_string()))
2557 } else if name == "eval" {
2558 Ok(Value::Function("eval".to_string()))
2559 } else if name == "encodeURI" {
2560 Ok(Value::Function("encodeURI".to_string()))
2561 } else if name == "decodeURI" {
2562 Ok(Value::Function("decodeURI".to_string()))
2563 } else if name == "Array" {
2564 Ok(Value::Function("Array".to_string()))
2565 } else if name == "Number" {
2566 Ok(Value::Object(js_number::make_number_object()?))
2567 } else if name == "Boolean" {
2568 Ok(Value::Function("Boolean".to_string()))
2569 } else if name == "Date" {
2570 Ok(Value::Function("Date".to_string()))
2571 } else if name == "RegExp" {
2572 Ok(Value::Function("RegExp".to_string()))
2573 } else if name == "Promise" {
2574 Ok(Value::Function("Promise".to_string()))
2575 } else if name == "new" {
2576 Ok(Value::Function("new".to_string()))
2577 } else if name == "__internal_resolve_promise" {
2578 Ok(Value::Function("__internal_resolve_promise".to_string()))
2579 } else if name == "__internal_reject_promise" {
2580 Ok(Value::Function("__internal_reject_promise".to_string()))
2581 } else if name == "__internal_promise_allsettled_resolve" {
2582 Ok(Value::Function("__internal_promise_allsettled_resolve".to_string()))
2583 } else if name == "__internal_promise_allsettled_reject" {
2584 Ok(Value::Function("__internal_promise_allsettled_reject".to_string()))
2585 } else if name == "NaN" {
2586 Ok(Value::Number(f64::NAN))
2587 } else if name == "Infinity" {
2588 Ok(Value::Number(f64::INFINITY))
2589 } else if let Some(val_rc) = obj_get_value(env, name)? {
2590 log::trace!("evaluate_var - {name} (found)");
2591 Ok(val_rc.borrow().clone())
2592 } else {
2593 Ok(Value::Undefined)
2594 }
2595}
2596
2597fn evaluate_assign(env: &JSObjectDataPtr, value: &Expr) -> Result<Value, JSError> {
2598 evaluate_expr(env, value)
2600}
2601
2602fn evaluate_logical_and_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2603 let left_val = evaluate_expr(env, target)?;
2605 if is_truthy(&left_val) {
2606 evaluate_assignment_expr(env, target, value)
2608 } else {
2609 Ok(left_val)
2611 }
2612}
2613
2614fn evaluate_logical_or_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2615 let left_val = evaluate_expr(env, target)?;
2617 if !is_truthy(&left_val) {
2618 evaluate_assignment_expr(env, target, value)
2620 } else {
2621 Ok(left_val)
2623 }
2624}
2625
2626fn evaluate_nullish_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2627 let left_val = evaluate_expr(env, target)?;
2629 match left_val {
2630 Value::Undefined => {
2631 evaluate_assignment_expr(env, target, value)
2633 }
2634 _ => {
2635 Ok(left_val)
2637 }
2638 }
2639}
2640
2641fn evaluate_add_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2642 let left_val = evaluate_expr(env, target)?;
2644 let right_val = evaluate_expr(env, value)?;
2645 let result = match (left_val, right_val) {
2646 (Value::Number(ln), Value::Number(rn)) => Value::Number(ln + rn),
2647 (Value::String(ls), Value::String(rs)) => {
2648 let mut result = ls.clone();
2649 result.extend_from_slice(&rs);
2650 Value::String(result)
2651 }
2652 (Value::Number(ln), Value::String(rs)) => {
2653 let mut result = utf8_to_utf16(&ln.to_string());
2654 result.extend_from_slice(&rs);
2655 Value::String(result)
2656 }
2657 (Value::String(ls), Value::Number(rn)) => {
2658 let mut result = ls.clone();
2659 result.extend_from_slice(&utf8_to_utf16(&rn.to_string()));
2660 Value::String(result)
2661 }
2662 _ => {
2663 return Err(JSError::EvaluationError {
2664 message: "Invalid operands for +=".to_string(),
2665 });
2666 }
2667 };
2668 let assignment_expr = match &result {
2669 Value::Number(n) => Expr::Number(*n),
2670 Value::String(s) => Expr::StringLit(s.clone()),
2671 _ => unreachable!(),
2672 };
2673 evaluate_assignment_expr(env, target, &assignment_expr)?;
2674 Ok(result)
2675}
2676
2677fn evaluate_sub_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2678 let left_val = evaluate_expr(env, target)?;
2680 let right_val = evaluate_expr(env, value)?;
2681 let result = match (left_val, right_val) {
2682 (Value::Number(ln), Value::Number(rn)) => Value::Number(ln - rn),
2683 _ => {
2684 return Err(JSError::EvaluationError {
2685 message: "Invalid operands for -=".to_string(),
2686 });
2687 }
2688 };
2689 let Value::Number(n) = result else { unreachable!() };
2690 evaluate_assignment_expr(env, target, &Expr::Number(n))?;
2691 Ok(result)
2692}
2693
2694fn evaluate_mul_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2695 let left_val = evaluate_expr(env, target)?;
2697 let right_val = evaluate_expr(env, value)?;
2698 let result = match (left_val, right_val) {
2699 (Value::Number(ln), Value::Number(rn)) => Value::Number(ln * rn),
2700 _ => {
2701 return Err(JSError::EvaluationError {
2702 message: "Invalid operands for *=".to_string(),
2703 });
2704 }
2705 };
2706 let Value::Number(n) = result else { unreachable!() };
2707 evaluate_assignment_expr(env, target, &Expr::Number(n))?;
2708 Ok(result)
2709}
2710
2711fn evaluate_div_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2712 let left_val = evaluate_expr(env, target)?;
2714 let right_val = evaluate_expr(env, value)?;
2715 let result = match (left_val, right_val) {
2716 (Value::Number(ln), Value::Number(rn)) => {
2717 if rn == 0.0 {
2718 return Err(JSError::EvaluationError {
2719 message: "Division by zero".to_string(),
2720 });
2721 }
2722 Value::Number(ln / rn)
2723 }
2724 _ => {
2725 return Err(JSError::EvaluationError {
2726 message: "Invalid operands for /=".to_string(),
2727 });
2728 }
2729 };
2730 let Value::Number(n) = result else { unreachable!() };
2731 evaluate_assignment_expr(env, target, &Expr::Number(n))?;
2732 Ok(result)
2733}
2734
2735fn evaluate_mod_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2736 let left_val = evaluate_expr(env, target)?;
2738 let right_val = evaluate_expr(env, value)?;
2739 let result = match (left_val, right_val) {
2740 (Value::Number(ln), Value::Number(rn)) => {
2741 if rn == 0.0 {
2742 return Err(JSError::EvaluationError {
2743 message: "Division by zero".to_string(),
2744 });
2745 }
2746 Value::Number(ln % rn)
2747 }
2748 _ => {
2749 return Err(JSError::EvaluationError {
2750 message: "Invalid operands for %=".to_string(),
2751 });
2752 }
2753 };
2754 let Value::Number(n) = result else { unreachable!() };
2755 evaluate_assignment_expr(env, target, &Expr::Number(n))?;
2756 Ok(result)
2757}
2758
2759fn evaluate_assignment_expr(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2760 let val = evaluate_expr(env, value)?;
2761 match target {
2762 Expr::Var(name) => {
2763 env_set_recursive(env, name, val.clone())?;
2764 Ok(val)
2765 }
2766 Expr::Property(obj, prop) => {
2767 let obj_val = evaluate_expr(env, obj)?;
2768 match obj_val {
2769 Value::Object(obj_map) => {
2770 obj_set_value(&obj_map, prop, val.clone())?;
2771 Ok(val)
2772 }
2773 _ => Err(JSError::EvaluationError {
2774 message: "Cannot assign to property of non-object".to_string(),
2775 }),
2776 }
2777 }
2778 Expr::Index(obj, idx) => {
2779 let obj_val = evaluate_expr(env, obj)?;
2780 let idx_val = evaluate_expr(env, idx)?;
2781 match (obj_val, idx_val) {
2782 (Value::Object(obj_map), Value::String(s)) => {
2783 let key = String::from_utf16_lossy(&s);
2784 obj_set_value(&obj_map, &key, val.clone())?;
2785 Ok(val)
2786 }
2787 (Value::Object(obj_map), Value::Number(n)) => {
2788 let key = n.to_string();
2789 obj_set_value(&obj_map, &key, val.clone())?;
2790 Ok(val)
2791 }
2792 _ => Err(JSError::EvaluationError {
2793 message: "Invalid index assignment".to_string(),
2794 }),
2795 }
2796 }
2797 _ => Err(JSError::EvaluationError {
2798 message: "Invalid assignment target".to_string(),
2799 }),
2800 }
2801}
2802
2803fn evaluate_increment(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
2804 let current_val = evaluate_expr(env, expr)?;
2806 let new_val = match current_val {
2807 Value::Number(n) => Value::Number(n + 1.0),
2808 _ => {
2809 return Err(JSError::EvaluationError {
2810 message: "Increment operand must be a number".to_string(),
2811 });
2812 }
2813 };
2814 match expr {
2816 Expr::Var(name) => {
2817 env_set_recursive(env, name, new_val.clone())?;
2818 Ok(new_val)
2819 }
2820 Expr::Property(obj, prop) => {
2821 let obj_val = evaluate_expr(env, obj)?;
2822 match obj_val {
2823 Value::Object(obj_map) => {
2824 obj_set_value(&obj_map, prop, new_val.clone())?;
2825 Ok(new_val)
2826 }
2827 _ => Err(JSError::EvaluationError {
2828 message: "Cannot increment property of non-object".to_string(),
2829 }),
2830 }
2831 }
2832 Expr::Index(obj, idx) => {
2833 let obj_val = evaluate_expr(env, obj)?;
2834 let idx_val = evaluate_expr(env, idx)?;
2835 match (obj_val, idx_val) {
2836 (Value::Object(obj_map), Value::String(s)) => {
2837 let key = String::from_utf16_lossy(&s);
2838 obj_set_value(&obj_map, &key, new_val.clone())?;
2839 Ok(new_val)
2840 }
2841 (Value::Object(obj_map), Value::Number(n)) => {
2842 let key = n.to_string();
2843 obj_set_value(&obj_map, &key, new_val.clone())?;
2844 Ok(new_val)
2845 }
2846 _ => Err(JSError::EvaluationError {
2847 message: "Invalid index increment".to_string(),
2848 }),
2849 }
2850 }
2851 _ => Err(JSError::EvaluationError {
2852 message: "Invalid increment target".to_string(),
2853 }),
2854 }
2855}
2856
2857fn evaluate_decrement(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
2858 let current_val = evaluate_expr(env, expr)?;
2860 let new_val = match current_val {
2861 Value::Number(n) => Value::Number(n - 1.0),
2862 _ => {
2863 return Err(JSError::EvaluationError {
2864 message: "Decrement operand must be a number".to_string(),
2865 });
2866 }
2867 };
2868 match expr {
2870 Expr::Var(name) => {
2871 env_set_recursive(env, name, new_val.clone())?;
2872 Ok(new_val)
2873 }
2874 Expr::Property(obj, prop) => {
2875 let obj_val = evaluate_expr(env, obj)?;
2876 match obj_val {
2877 Value::Object(obj_map) => {
2878 obj_set_value(&obj_map, prop, new_val.clone())?;
2879 Ok(new_val)
2880 }
2881 _ => Err(JSError::EvaluationError {
2882 message: "Cannot decrement property of non-object".to_string(),
2883 }),
2884 }
2885 }
2886 Expr::Index(obj, idx) => {
2887 let obj_val = evaluate_expr(env, obj)?;
2888 let idx_val = evaluate_expr(env, idx)?;
2889 match (obj_val, idx_val) {
2890 (Value::Object(obj_map), Value::String(s)) => {
2891 let key = String::from_utf16_lossy(&s);
2892 obj_set_value(&obj_map, &key, new_val.clone())?;
2893 Ok(new_val)
2894 }
2895 (Value::Object(obj_map), Value::Number(n)) => {
2896 let key = n.to_string();
2897 obj_set_value(&obj_map, &key, new_val.clone())?;
2898 Ok(new_val)
2899 }
2900 _ => Err(JSError::EvaluationError {
2901 message: "Invalid index decrement".to_string(),
2902 }),
2903 }
2904 }
2905 _ => Err(JSError::EvaluationError {
2906 message: "Invalid decrement target".to_string(),
2907 }),
2908 }
2909}
2910
2911fn evaluate_post_increment(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
2912 let current_val = evaluate_expr(env, expr)?;
2914 let old_val = current_val.clone();
2915 let new_val = match current_val {
2916 Value::Number(n) => Value::Number(n + 1.0),
2917 _ => {
2918 return Err(JSError::EvaluationError {
2919 message: "Increment operand must be a number".to_string(),
2920 });
2921 }
2922 };
2923 match expr {
2925 Expr::Var(name) => {
2926 env_set_recursive(env, name, new_val)?;
2927 Ok(old_val)
2928 }
2929 Expr::Property(obj, prop) => {
2930 let obj_val = evaluate_expr(env, obj)?;
2931 match obj_val {
2932 Value::Object(obj_map) => {
2933 obj_set_value(&obj_map, prop, new_val)?;
2934 Ok(old_val)
2935 }
2936 _ => Err(JSError::EvaluationError {
2937 message: "Cannot increment property of non-object".to_string(),
2938 }),
2939 }
2940 }
2941 Expr::Index(obj, idx) => {
2942 let obj_val = evaluate_expr(env, obj)?;
2943 let idx_val = evaluate_expr(env, idx)?;
2944 match (obj_val, idx_val) {
2945 (Value::Object(obj_map), Value::String(s)) => {
2946 let key = String::from_utf16_lossy(&s);
2947 obj_set_value(&obj_map, &key, new_val)?;
2948 Ok(old_val)
2949 }
2950 (Value::Object(obj_map), Value::Number(n)) => {
2951 let key = n.to_string();
2952 obj_set_value(&obj_map, &key, new_val)?;
2953 Ok(old_val)
2954 }
2955 _ => Err(JSError::EvaluationError {
2956 message: "Invalid index increment".to_string(),
2957 }),
2958 }
2959 }
2960 _ => Err(JSError::EvaluationError {
2961 message: "Invalid increment target".to_string(),
2962 }),
2963 }
2964}
2965
2966fn evaluate_post_decrement(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
2967 let current_val = evaluate_expr(env, expr)?;
2969 let old_val = current_val.clone();
2970 let new_val = match current_val {
2971 Value::Number(n) => Value::Number(n - 1.0),
2972 _ => {
2973 return Err(JSError::EvaluationError {
2974 message: "Decrement operand must be a number".to_string(),
2975 });
2976 }
2977 };
2978 match expr {
2980 Expr::Var(name) => {
2981 env_set_recursive(env, name, new_val)?;
2982 Ok(old_val)
2983 }
2984 Expr::Property(obj, prop) => {
2985 let obj_val = evaluate_expr(env, obj)?;
2986 match obj_val {
2987 Value::Object(obj_map) => {
2988 obj_set_value(&obj_map, prop, new_val)?;
2989 Ok(old_val)
2990 }
2991 _ => Err(JSError::EvaluationError {
2992 message: "Cannot decrement property of non-object".to_string(),
2993 }),
2994 }
2995 }
2996 Expr::Index(obj, idx) => {
2997 let obj_val = evaluate_expr(env, obj)?;
2998 let idx_val = evaluate_expr(env, idx)?;
2999 match (obj_val, idx_val) {
3000 (Value::Object(obj_map), Value::String(s)) => {
3001 let key = String::from_utf16_lossy(&s);
3002 obj_set_value(&obj_map, &key, new_val)?;
3003 Ok(old_val)
3004 }
3005 (Value::Object(obj_map), Value::Number(n)) => {
3006 let key = n.to_string();
3007 obj_set_value(&obj_map, &key, new_val)?;
3008 Ok(old_val)
3009 }
3010 _ => Err(JSError::EvaluationError {
3011 message: "Invalid index decrement".to_string(),
3012 }),
3013 }
3014 }
3015 _ => Err(JSError::EvaluationError {
3016 message: "Invalid decrement target".to_string(),
3017 }),
3018 }
3019}
3020
3021fn evaluate_unary_neg(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
3022 let val = evaluate_expr(env, expr)?;
3023 match val {
3024 Value::Number(n) => Ok(Value::Number(-n)),
3025 _ => Err(JSError::EvaluationError {
3026 message: "error".to_string(),
3027 }),
3028 }
3029}
3030
3031fn evaluate_typeof(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
3032 let val = evaluate_expr(env, expr)?;
3033 let type_str = match val {
3034 Value::Undefined => "undefined",
3035 Value::Boolean(_) => "boolean",
3036 Value::Number(_) => "number",
3037 Value::String(_) => "string",
3038 Value::Object(_) => "object",
3039 Value::Function(_) => "function",
3040 Value::Closure(_, _, _) => "function",
3041 Value::ClassDefinition(_) => "function",
3042 Value::Getter(_, _) => "function",
3043 Value::Setter(_, _, _) => "function",
3044 Value::Property { .. } => "undefined",
3045 Value::Promise(_) => "object",
3046 };
3047 Ok(Value::String(utf8_to_utf16(type_str)))
3048}
3049
3050fn evaluate_delete(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
3051 match expr {
3052 Expr::Var(_) => {
3053 Ok(Value::Boolean(false))
3055 }
3056 Expr::Property(obj, prop) => {
3057 let obj_val = evaluate_expr(env, obj)?;
3059 match obj_val {
3060 Value::Object(obj_map) => {
3061 let deleted = obj_delete(&obj_map, prop);
3062 Ok(Value::Boolean(deleted))
3063 }
3064 _ => Ok(Value::Boolean(false)),
3065 }
3066 }
3067 Expr::Index(obj, idx) => {
3068 let obj_val = evaluate_expr(env, obj)?;
3070 let idx_val = evaluate_expr(env, idx)?;
3071 match (obj_val, idx_val) {
3072 (Value::Object(obj_map), Value::String(s)) => {
3073 let key = String::from_utf16_lossy(&s);
3074 let deleted = obj_delete(&obj_map, &key);
3075 Ok(Value::Boolean(deleted))
3076 }
3077 (Value::Object(obj_map), Value::Number(n)) => {
3078 let key = n.to_string();
3079 let deleted = obj_delete(&obj_map, &key);
3080 Ok(Value::Boolean(deleted))
3081 }
3082 _ => Ok(Value::Boolean(false)),
3083 }
3084 }
3085 _ => {
3086 Ok(Value::Boolean(false))
3088 }
3089 }
3090}
3091
3092fn evaluate_void(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
3093 evaluate_expr(env, expr)?;
3095 Ok(Value::Undefined)
3096}
3097
3098fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Expr) -> Result<Value, JSError> {
3099 let l = evaluate_expr(env, left)?;
3100 let r = evaluate_expr(env, right)?;
3101 match op {
3102 BinaryOp::Add => match (l, r) {
3103 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(ln + rn)),
3104 (Value::String(ls), Value::String(rs)) => {
3105 let mut result = ls.clone();
3106 result.extend_from_slice(&rs);
3107 Ok(Value::String(result))
3108 }
3109 (Value::Number(ln), Value::String(rs)) => {
3110 let mut result = utf8_to_utf16(&ln.to_string());
3111 result.extend_from_slice(&rs);
3112 Ok(Value::String(result))
3113 }
3114 (Value::String(ls), Value::Number(rn)) => {
3115 let mut result = ls.clone();
3116 result.extend_from_slice(&utf8_to_utf16(&rn.to_string()));
3117 Ok(Value::String(result))
3118 }
3119 (Value::Boolean(lb), Value::String(rs)) => {
3120 let mut result = utf8_to_utf16(&lb.to_string());
3121 result.extend_from_slice(&rs);
3122 Ok(Value::String(result))
3123 }
3124 (Value::String(ls), Value::Boolean(rb)) => {
3125 let mut result = ls.clone();
3126 result.extend_from_slice(&utf8_to_utf16(&rb.to_string()));
3127 Ok(Value::String(result))
3128 }
3129 _ => Err(JSError::EvaluationError {
3130 message: "error".to_string(),
3131 }),
3132 },
3133 BinaryOp::Sub => match (l, r) {
3134 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(ln - rn)),
3135 _ => Err(JSError::EvaluationError {
3136 message: "error".to_string(),
3137 }),
3138 },
3139 BinaryOp::Mul => match (l, r) {
3140 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(ln * rn)),
3141 _ => Err(JSError::EvaluationError {
3142 message: "error".to_string(),
3143 }),
3144 },
3145 BinaryOp::Div => match (l, r) {
3146 (Value::Number(ln), Value::Number(rn)) => {
3147 if rn == 0.0 {
3148 Err(JSError::EvaluationError {
3149 message: "error".to_string(),
3150 })
3151 } else {
3152 Ok(Value::Number(ln / rn))
3153 }
3154 }
3155 _ => Err(JSError::EvaluationError {
3156 message: "error".to_string(),
3157 }),
3158 },
3159 BinaryOp::Equal => match (l, r) {
3160 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(if ln == rn { 1.0 } else { 0.0 })),
3161 (Value::String(ls), Value::String(rs)) => Ok(Value::Number(if ls == rs { 1.0 } else { 0.0 })),
3162 _ => Ok(Value::Number(0.0)), },
3164 BinaryOp::StrictEqual => match (l, r) {
3165 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(if ln == rn { 1.0 } else { 0.0 })),
3166 (Value::String(ls), Value::String(rs)) => Ok(Value::Number(if ls == rs { 1.0 } else { 0.0 })),
3167 _ => Ok(Value::Number(0.0)), },
3169 BinaryOp::LessThan => match (l, r) {
3170 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(if ln < rn { 1.0 } else { 0.0 })),
3171 (Value::String(ls), Value::String(rs)) => Ok(Value::Number(if ls < rs { 1.0 } else { 0.0 })),
3172 _ => Err(JSError::EvaluationError {
3173 message: "error".to_string(),
3174 }),
3175 },
3176 BinaryOp::GreaterThan => match (l, r) {
3177 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(if ln > rn { 1.0 } else { 0.0 })),
3178 (Value::String(ls), Value::String(rs)) => Ok(Value::Number(if ls > rs { 1.0 } else { 0.0 })),
3179 _ => Err(JSError::EvaluationError {
3180 message: "error".to_string(),
3181 }),
3182 },
3183 BinaryOp::LessEqual => match (l, r) {
3184 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(if ln <= rn { 1.0 } else { 0.0 })),
3185 (Value::String(ls), Value::String(rs)) => Ok(Value::Number(if ls <= rs { 1.0 } else { 0.0 })),
3186 _ => Err(JSError::EvaluationError {
3187 message: "error".to_string(),
3188 }),
3189 },
3190 BinaryOp::GreaterEqual => match (l, r) {
3191 (Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(if ln >= rn { 1.0 } else { 0.0 })),
3192 (Value::String(ls), Value::String(rs)) => Ok(Value::Number(if ls >= rs { 1.0 } else { 0.0 })),
3193 _ => Err(JSError::EvaluationError {
3194 message: "error".to_string(),
3195 }),
3196 },
3197 BinaryOp::Mod => match (l, r) {
3198 (Value::Number(ln), Value::Number(rn)) => {
3199 if rn == 0.0 {
3200 Err(JSError::EvaluationError {
3201 message: "Division by zero".to_string(),
3202 })
3203 } else {
3204 Ok(Value::Number(ln % rn))
3205 }
3206 }
3207 _ => Err(JSError::EvaluationError {
3208 message: "Modulo operation only supported for numbers".to_string(),
3209 }),
3210 },
3211 BinaryOp::InstanceOf => {
3212 match (l, r) {
3214 (Value::Object(obj), Value::Object(constructor)) => Ok(Value::Boolean(is_instance_of(&obj, &constructor)?)),
3215 _ => Ok(Value::Boolean(false)),
3216 }
3217 }
3218 BinaryOp::In => {
3219 match (l, r) {
3221 (Value::String(prop), Value::Object(obj)) => {
3222 let prop_str = String::from_utf16_lossy(&prop);
3223 Ok(Value::Boolean(obj_get_value(&obj, &prop_str)?.is_some()))
3224 }
3225 _ => Ok(Value::Boolean(false)),
3226 }
3227 }
3228 BinaryOp::NullishCoalescing => {
3229 match l {
3231 Value::Undefined => Ok(r),
3232 _ => Ok(l),
3233 }
3234 }
3235 }
3236}
3237
3238fn evaluate_index(env: &JSObjectDataPtr, obj: &Expr, idx: &Expr) -> Result<Value, JSError> {
3239 let obj_val = evaluate_expr(env, obj)?;
3240 let idx_val = evaluate_expr(env, idx)?;
3241 match (obj_val, idx_val) {
3242 (Value::String(s), Value::Number(n)) => {
3243 let idx = n as usize;
3244 if let Some(ch) = utf16_char_at(&s, idx) {
3245 Ok(Value::String(vec![ch]))
3246 } else {
3247 Ok(Value::String(Vec::new())) }
3249 }
3250 (Value::Object(obj_map), Value::Number(n)) => {
3251 let key = n.to_string();
3253 if let Some(val) = obj_get_value(&obj_map, &key)? {
3254 Ok(val.borrow().clone())
3255 } else {
3256 Ok(Value::Undefined)
3257 }
3258 }
3259 (Value::Object(obj_map), Value::String(s)) => {
3260 let key = String::from_utf16_lossy(&s);
3262 if let Some(val) = obj_get_value(&obj_map, &key)? {
3263 Ok(val.borrow().clone())
3264 } else {
3265 Ok(Value::Undefined)
3266 }
3267 }
3268 _ => Err(JSError::EvaluationError {
3269 message: "error".to_string(),
3270 }), }
3272}
3273
3274fn evaluate_property(env: &JSObjectDataPtr, obj: &Expr, prop: &str) -> Result<Value, JSError> {
3275 let obj_val = evaluate_expr(env, obj)?;
3276 log::trace!("Property access prop={prop}");
3277 match obj_val {
3278 Value::String(s) if prop == "length" => Ok(Value::Number(utf16_len(&s) as f64)),
3279 Value::Object(obj_map) => {
3280 if let Some(val) = obj_get_value(&obj_map, prop)? {
3281 Ok(val.borrow().clone())
3282 } else {
3283 Ok(Value::Undefined)
3284 }
3285 }
3286 _ => Err(JSError::EvaluationError {
3287 message: format!("Property not found for obj_val={obj_val:?}, prop={prop}"),
3288 }),
3289 }
3290}
3291
3292fn evaluate_optional_property(env: &JSObjectDataPtr, obj: &Expr, prop: &str) -> Result<Value, JSError> {
3293 let obj_val = evaluate_expr(env, obj)?;
3294 log::trace!("Optional property access prop={prop}");
3295 match obj_val {
3296 Value::Undefined => Ok(Value::Undefined),
3297 Value::Object(obj_map) => {
3298 if let Some(val) = obj_get_value(&obj_map, prop)? {
3299 Ok(val.borrow().clone())
3300 } else {
3301 Ok(Value::Undefined)
3302 }
3303 }
3304 Value::String(s) if prop == "length" => Ok(Value::Number(utf16_len(&s) as f64)),
3305 _ => Err(JSError::EvaluationError {
3306 message: format!("Property not found for obj_val={obj_val:?}, prop={prop}"),
3307 }),
3308 }
3309}
3310
3311fn evaluate_call(env: &JSObjectDataPtr, func_expr: &Expr, args: &[Expr]) -> Result<Value, JSError> {
3312 log::trace!("evaluate_call entry: args_len={} func_expr=...", args.len());
3313 if let Expr::Property(obj_expr, method_name) = func_expr {
3315 if let Expr::Var(var_name) = &**obj_expr
3317 && var_name == "Array"
3318 {
3319 return crate::js_array::handle_array_static_method(method_name, args, env);
3320 }
3321
3322 let obj_val = evaluate_expr(env, obj_expr)?;
3323 log::trace!("evaluate_call - object evaluated");
3324 match (obj_val, method_name.as_str()) {
3325 (Value::Object(obj_map), "log") if obj_map.borrow().contains_key("log") => {
3326 js_console::handle_console_method(method_name, args, env)
3327 }
3328 (obj_val, "toString") => crate::js_object::handle_to_string_method(&obj_val, args),
3329 (obj_val, "valueOf") => crate::js_object::handle_value_of_method(&obj_val, args),
3330 (Value::Object(obj_map), method) => {
3331 if obj_map.borrow().contains_key("sprintf") {
3333 match method {
3334 "sprintf" => {
3335 log::trace!("js dispatch calling sprintf with {} args", args.len());
3336 return sprintf::handle_sprintf_call(env, args);
3337 }
3338 "tmpfile" => {
3339 return tmpfile::create_tmpfile();
3340 }
3341 _ => {}
3342 }
3343 }
3344
3345 if obj_map.borrow().contains_key("open") {
3347 return crate::js_os::handle_os_method(&obj_map, method, args, env);
3348 }
3349
3350 if obj_map.borrow().contains_key("join") {
3352 return crate::js_os::handle_os_method(&obj_map, method, args, env);
3353 }
3354
3355 if obj_map.borrow().contains_key("__file_id") {
3357 return tmpfile::handle_file_method(&obj_map, method, args, env);
3358 }
3359 if obj_map.borrow().contains_key("PI") && obj_map.borrow().contains_key("E") {
3361 js_math::handle_math_method(method, args, env)
3362 } else if obj_map.borrow().contains_key("parse") && obj_map.borrow().contains_key("stringify") {
3363 crate::js_json::handle_json_method(method, args, env)
3364 } else if obj_map.borrow().contains_key("keys") && obj_map.borrow().contains_key("values") {
3365 crate::js_object::handle_object_method(method, args, env)
3366 } else if obj_map.borrow().contains_key("MAX_VALUE") && obj_map.borrow().contains_key("MIN_VALUE") {
3367 crate::js_number::handle_number_method(method, args, env)
3368 } else if obj_map.borrow().contains_key("__timestamp") {
3369 crate::js_date::handle_date_method(&obj_map, method, args)
3371 } else if obj_map.borrow().contains_key("__regex") {
3372 crate::js_regexp::handle_regexp_method(&obj_map, method, args, env)
3374 } else if is_array(&obj_map) {
3375 crate::js_array::handle_array_instance_method(&obj_map, method, args, env, obj_expr)
3377 } else if obj_map.borrow().contains_key("__promise") {
3378 handle_promise_method(&obj_map, method, args, env)
3380 } else if obj_map.borrow().contains_key("__class_def__") {
3381 call_static_method(&obj_map, method, args, env)
3383 } else if is_class_instance(&obj_map)? {
3384 call_class_method(&obj_map, method, args, env)
3385 } else {
3386 if let Some(prop_val) = obj_get_value(&obj_map, method)? {
3388 match prop_val.borrow().clone() {
3389 Value::Closure(params, body, captured_env) => {
3390 let mut evaluated_args = Vec::new();
3393 expand_spread_in_call_args(env, args, &mut evaluated_args)?;
3394 let func_env = captured_env.clone();
3396 for (i, param) in params.iter().enumerate() {
3398 if i < evaluated_args.len() {
3399 env_set(&func_env, param.as_str(), evaluated_args[i].clone())?;
3400 } else {
3401 env_set(&func_env, param.as_str(), Value::Undefined)?;
3402 }
3403 }
3404 evaluate_statements(&func_env, &body)
3406 }
3407 Value::Function(func_name) => crate::js_function::handle_global_function(&func_name, args, env),
3408 _ => Err(JSError::EvaluationError {
3409 message: format!("Property '{}' is not a function", method),
3410 }),
3411 }
3412 } else {
3413 Err(JSError::EvaluationError {
3414 message: format!("Method {method} not found on object"),
3415 })
3416 }
3417 }
3418 }
3419 (Value::Function(func_name), method) => {
3420 match func_name.as_str() {
3422 "Object" => crate::js_object::handle_object_method(method, args, env),
3423 "Array" => crate::js_array::handle_array_static_method(method, args, env),
3424 "Promise" => crate::js_promise::handle_promise_static_method(method, args, env),
3425 "Date" => crate::js_date::handle_date_static_method(method, args, env),
3426 _ => Err(JSError::EvaluationError {
3427 message: format!("{} has no static method '{}'", func_name, method),
3428 }),
3429 }
3430 }
3431 (Value::String(s), method) => crate::js_string::handle_string_method(&s, method, args, env),
3432 _ => Err(JSError::EvaluationError {
3433 message: "error".to_string(),
3434 }),
3435 }
3436 } else if let Expr::OptionalProperty(obj_expr, method_name) = func_expr {
3437 let obj_val = evaluate_expr(env, obj_expr)?;
3439 match obj_val {
3440 Value::Undefined => Ok(Value::Undefined),
3441 Value::Object(obj_map) => handle_optional_method_call(&obj_map, method_name, args, env, obj_expr),
3442 Value::Function(func_name) => {
3443 match func_name.as_str() {
3445 "Object" => crate::js_object::handle_object_method(method_name, args, env),
3446 "Array" => crate::js_array::handle_array_static_method(method_name, args, env),
3447 "Promise" => crate::js_promise::handle_promise_static_method(method_name, args, env),
3448 _ => Err(JSError::EvaluationError {
3449 message: format!("{} has no static method '{}'", func_name, method_name),
3450 }),
3451 }
3452 }
3453 Value::String(s) => crate::js_string::handle_string_method(&s, method_name, args, env),
3454 _ => Err(JSError::EvaluationError {
3455 message: "error".to_string(),
3456 }),
3457 }
3458 } else {
3459 let func_val = evaluate_expr(env, func_expr)?;
3461 match func_val {
3462 Value::Function(func_name) => crate::js_function::handle_global_function(&func_name, args, env),
3463 Value::Closure(params, body, captured_env) => {
3464 let mut evaluated_args = Vec::new();
3467 expand_spread_in_call_args(env, args, &mut evaluated_args)?;
3468 let func_env = captured_env.clone();
3470 for (i, param) in params.iter().enumerate() {
3472 if i < evaluated_args.len() {
3473 env_set(&func_env, param.as_str(), evaluated_args[i].clone())?;
3474 } else {
3475 env_set(&func_env, param.as_str(), Value::Undefined)?;
3476 }
3477 }
3478 evaluate_statements(&func_env, &body)
3480 }
3481 Value::Object(obj_map) => {
3482 if obj_map.borrow().contains_key("MAX_VALUE") && obj_map.borrow().contains_key("MIN_VALUE") {
3484 crate::js_function::handle_global_function("Number", args, env)
3486 } else {
3487 Err(JSError::EvaluationError {
3488 message: "error".to_string(),
3489 })
3490 }
3491 }
3492 _ => Err(JSError::EvaluationError {
3493 message: "error".to_string(),
3494 }),
3495 }
3496 }
3497}
3498
3499fn evaluate_optional_call(env: &JSObjectDataPtr, func_expr: &Expr, args: &[Expr]) -> Result<Value, JSError> {
3500 log::trace!("evaluate_optional_call entry: args_len={} func_expr=...", args.len());
3501 if let Expr::Property(obj_expr, method_name) = func_expr {
3503 if let Expr::Var(var_name) = &**obj_expr
3505 && var_name == "Array"
3506 {
3507 return crate::js_array::handle_array_static_method(method_name, args, env);
3508 }
3509
3510 let obj_val = evaluate_expr(env, obj_expr)?;
3511 log::trace!("evaluate_optional_call - object eval result: {obj_val:?}");
3512 match obj_val {
3513 Value::Undefined => Ok(Value::Undefined),
3514 Value::Object(obj_map) => {
3515 if obj_map.borrow().contains_key("sprintf") {
3517 match method_name.as_str() {
3518 "sprintf" => {
3519 log::trace!("js dispatch calling sprintf with {} args", args.len());
3520 return sprintf::handle_sprintf_call(env, args);
3521 }
3522 "tmpfile" => {
3523 return tmpfile::create_tmpfile();
3524 }
3525 _ => {}
3526 }
3527 }
3528
3529 if obj_map.borrow().contains_key("open") {
3531 return crate::js_os::handle_os_method(&obj_map, method_name, args, env);
3532 }
3533
3534 if obj_map.borrow().contains_key("join") {
3536 return crate::js_os::handle_os_method(&obj_map, method_name, args, env);
3537 }
3538
3539 if obj_map.borrow().contains_key("__file_id") {
3541 return tmpfile::handle_file_method(&obj_map, method_name, args, env);
3542 }
3543 if obj_map.borrow().contains_key("PI") && obj_map.borrow().contains_key("E") {
3545 js_math::handle_math_method(method_name, args, env)
3546 } else if obj_map.borrow().contains_key("parse") && obj_map.borrow().contains_key("stringify") {
3547 crate::js_json::handle_json_method(method_name, args, env)
3548 } else if obj_map.borrow().contains_key("keys") && obj_map.borrow().contains_key("values") {
3549 crate::js_object::handle_object_method(method_name, args, env)
3550 } else if obj_map.borrow().contains_key("MAX_VALUE") && obj_map.borrow().contains_key("MIN_VALUE") {
3551 crate::js_number::handle_number_method(method_name, args, env)
3552 } else if obj_map.borrow().contains_key("__timestamp") {
3553 crate::js_date::handle_date_method(&obj_map, method_name, args)
3555 } else if obj_map.borrow().contains_key("__regex") {
3556 crate::js_regexp::handle_regexp_method(&obj_map, method_name, args, env)
3558 } else if is_array(&obj_map) {
3559 crate::js_array::handle_array_instance_method(&obj_map, method_name, args, env, obj_expr)
3561 } else if obj_map.borrow().contains_key("__promise") {
3562 handle_promise_method(&obj_map, method_name, args, env)
3564 } else if obj_map.borrow().contains_key("__class_def__") {
3565 call_static_method(&obj_map, method_name, args, env)
3567 } else if is_class_instance(&obj_map)? {
3568 call_class_method(&obj_map, method_name, args, env)
3569 } else {
3570 Err(JSError::EvaluationError {
3571 message: format!("Method {method_name} not found on object"),
3572 })
3573 }
3574 }
3575 Value::Function(func_name) => {
3576 match func_name.as_str() {
3578 "Object" => crate::js_object::handle_object_method(method_name, args, env),
3579 "Array" => crate::js_array::handle_array_static_method(method_name, args, env),
3580 "Date" => crate::js_date::handle_date_static_method(method_name, args, env),
3581 _ => Err(JSError::EvaluationError {
3582 message: format!("{} has no static method '{}'", func_name, method_name),
3583 }),
3584 }
3585 }
3586 Value::String(s) => crate::js_string::handle_string_method(&s, method_name, args, env),
3587 _ => Err(JSError::EvaluationError {
3588 message: "error".to_string(),
3589 }),
3590 }
3591 } else {
3592 let func_val = evaluate_expr(env, func_expr)?;
3594 match func_val {
3595 Value::Undefined => Ok(Value::Undefined),
3596 Value::Function(func_name) => crate::js_function::handle_global_function(&func_name, args, env),
3597 Value::Closure(params, body, captured_env) => {
3598 let mut evaluated_args = Vec::new();
3601 expand_spread_in_call_args(env, args, &mut evaluated_args)?;
3602 let func_env = captured_env.clone();
3604 for (i, param) in params.iter().enumerate() {
3606 if i < evaluated_args.len() {
3607 env_set(&func_env, param.as_str(), evaluated_args[i].clone())?;
3608 } else {
3609 env_set(&func_env, param.as_str(), Value::Undefined)?;
3610 }
3611 }
3612 evaluate_statements(&func_env, &body)
3614 }
3615 _ => Err(JSError::EvaluationError {
3616 message: "error".to_string(),
3617 }),
3618 }
3619 }
3620}
3621
3622fn evaluate_object(env: &JSObjectDataPtr, properties: &Vec<(String, Expr)>) -> Result<Value, JSError> {
3623 let obj = Rc::new(RefCell::new(JSObjectData::new()));
3624 for (key, value_expr) in properties {
3625 if key.is_empty() && matches!(value_expr, Expr::Spread(_)) {
3626 if let Expr::Spread(expr) = value_expr {
3628 let spread_val = evaluate_expr(env, expr)?;
3629 if let Value::Object(spread_obj) = spread_val {
3630 for (prop_key, prop_val) in spread_obj.borrow().properties.iter() {
3632 obj.borrow_mut().insert(prop_key.clone(), prop_val.clone());
3633 }
3634 } else {
3635 return Err(JSError::EvaluationError {
3636 message: "Spread operator can only be applied to objects".to_string(),
3637 });
3638 }
3639 }
3640 } else {
3641 match value_expr {
3642 Expr::Getter(func_expr) => {
3643 if let Expr::Function(_params, body) = func_expr.as_ref() {
3644 let existing_opt = obj.borrow().get(key);
3646 if let Some(existing) = existing_opt {
3647 let mut val = existing.borrow().clone();
3648 if let Value::Property {
3649 value: _,
3650 getter,
3651 setter: _,
3652 } = &mut val
3653 {
3654 getter.replace((body.clone(), env.clone()));
3656 obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(val)));
3657 } else {
3658 let prop = Value::Property {
3660 value: Some(existing.clone()),
3661 getter: Some((body.clone(), env.clone())),
3662 setter: None,
3663 };
3664 obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(prop)));
3665 }
3666 } else {
3667 let prop = Value::Property {
3669 value: None,
3670 getter: Some((body.clone(), env.clone())),
3671 setter: None,
3672 };
3673 obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(prop)));
3674 }
3675 } else {
3676 return Err(JSError::EvaluationError {
3677 message: "Getter must be a function".to_string(),
3678 });
3679 }
3680 }
3681 Expr::Setter(func_expr) => {
3682 if let Expr::Function(params, body) = func_expr.as_ref() {
3683 let existing_opt = obj.borrow().get(key);
3685 if let Some(existing) = existing_opt {
3686 let mut val = existing.borrow().clone();
3687 if let Value::Property {
3688 value: _,
3689 getter: _,
3690 setter,
3691 } = &mut val
3692 {
3693 setter.replace((params.clone(), body.clone(), env.clone()));
3695 obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(val)));
3696 } else {
3697 let prop = Value::Property {
3699 value: Some(existing.clone()),
3700 getter: None,
3701 setter: Some((params.clone(), body.clone(), env.clone())),
3702 };
3703 obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(prop)));
3704 }
3705 } else {
3706 let prop = Value::Property {
3708 value: None,
3709 getter: None,
3710 setter: Some((params.clone(), body.clone(), env.clone())),
3711 };
3712 obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(prop)));
3713 }
3714 } else {
3715 return Err(JSError::EvaluationError {
3716 message: "Setter must be a function".to_string(),
3717 });
3718 }
3719 }
3720 _ => {
3721 let value = evaluate_expr(env, value_expr)?;
3722 let existing_rc = obj.borrow().get(key);
3724 if let Some(existing) = existing_rc {
3725 let mut existing_val = existing.borrow().clone();
3726 if let Value::Property {
3727 value: prop_value,
3728 getter: _,
3729 setter: _,
3730 } = &mut existing_val
3731 {
3732 prop_value.replace(Rc::new(RefCell::new(value)));
3734 obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(existing_val)));
3735 } else {
3736 let prop = Value::Property {
3738 value: Some(Rc::new(RefCell::new(value))),
3739 getter: None,
3740 setter: None,
3741 };
3742 obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(prop)));
3743 }
3744 } else {
3745 obj_set_value(&obj, key.as_str(), value)?;
3746 }
3747 }
3748 }
3749 }
3750 }
3751 Ok(Value::Object(obj))
3752}
3753
3754fn evaluate_array(env: &JSObjectDataPtr, elements: &Vec<Expr>) -> Result<Value, JSError> {
3755 let arr = Rc::new(RefCell::new(JSObjectData::new()));
3756 let mut index = 0;
3757 for elem_expr in elements {
3758 if let Expr::Spread(spread_expr) = elem_expr {
3759 let spread_val = evaluate_expr(env, spread_expr)?;
3761 if let Value::Object(spread_obj) = spread_val {
3762 let mut i = 0;
3764 loop {
3765 let key = i.to_string();
3766 if let Some(val) = obj_get_value(&spread_obj, &key)? {
3767 obj_set_value(&arr, index.to_string(), val.borrow().clone())?;
3768 index += 1;
3769 i += 1;
3770 } else {
3771 break;
3772 }
3773 }
3774 } else {
3775 return Err(JSError::EvaluationError {
3776 message: "Spread operator can only be applied to arrays".to_string(),
3777 });
3778 }
3779 } else {
3780 let value = evaluate_expr(env, elem_expr)?;
3781 obj_set_value(&arr, index.to_string(), value)?;
3782 index += 1;
3783 }
3784 }
3785 set_array_length(&arr, index)?;
3787 Ok(Value::Object(arr))
3788}
3789
3790fn evaluate_array_destructuring(_env: &JSObjectDataPtr, _pattern: &Vec<DestructuringElement>) -> Result<Value, JSError> {
3791 Err(JSError::EvaluationError {
3793 message: "Array destructuring should not be evaluated as an expression".to_string(),
3794 })
3795}
3796
3797fn evaluate_object_destructuring(_env: &JSObjectDataPtr, _pattern: &Vec<ObjectDestructuringElement>) -> Result<Value, JSError> {
3798 Err(JSError::EvaluationError {
3800 message: "Object destructuring should not be evaluated as an expression".to_string(),
3801 })
3802}
3803
3804pub type JSObjectDataPtr = Rc<RefCell<JSObjectData>>;
3805
3806#[derive(Clone, Default)]
3807pub struct JSObjectData {
3808 pub properties: std::collections::HashMap<String, Rc<RefCell<Value>>>,
3809 pub constants: std::collections::HashSet<String>,
3810 pub prototype: Option<Rc<RefCell<JSObjectData>>>,
3811 pub is_function_scope: bool,
3812}
3813
3814impl std::fmt::Debug for JSObjectData {
3815 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3816 write!(
3817 f,
3818 "JSObjectData {{ properties: {}, constants: {}, prototype: {}, is_function_scope: {} }}",
3819 self.properties.len(),
3820 self.constants.len(),
3821 self.prototype.is_some(),
3822 self.is_function_scope
3823 )
3824 }
3825}
3826
3827impl JSObjectData {
3828 pub fn new() -> Self {
3829 JSObjectData::default()
3830 }
3831
3832 pub fn insert(&mut self, key: String, val: Rc<RefCell<Value>>) {
3833 self.properties.insert(key, val);
3834 }
3835
3836 pub fn get(&self, key: &str) -> Option<Rc<RefCell<Value>>> {
3837 self.properties.get(key).cloned()
3838 }
3839
3840 pub fn contains_key(&self, key: &str) -> bool {
3841 self.properties.contains_key(key)
3842 }
3843
3844 pub fn remove(&mut self, key: &str) -> Option<Rc<RefCell<Value>>> {
3845 self.properties.remove(key)
3846 }
3847
3848 pub fn keys(&self) -> std::collections::hash_map::Keys<'_, String, Rc<RefCell<Value>>> {
3849 self.properties.keys()
3850 }
3851
3852 pub fn is_const(&self, key: &str) -> bool {
3853 self.constants.contains(key)
3854 }
3855
3856 pub fn set_const(&mut self, key: String) {
3857 self.constants.insert(key);
3858 }
3859}
3860
3861#[derive(Clone)]
3862pub enum Value {
3863 Number(f64),
3864 String(Vec<u16>), Boolean(bool),
3866 Undefined,
3867 Object(JSObjectDataPtr), Function(String), Closure(Vec<String>, Vec<Statement>, JSObjectDataPtr), ClassDefinition(Rc<ClassDefinition>), Getter(Vec<Statement>, JSObjectDataPtr), Setter(Vec<String>, Vec<Statement>, JSObjectDataPtr), Property {
3874 value: Option<Rc<RefCell<Value>>>,
3876 getter: Option<(Vec<Statement>, JSObjectDataPtr)>,
3877 setter: Option<(Vec<String>, Vec<Statement>, JSObjectDataPtr)>,
3878 },
3879 Promise(Rc<RefCell<JSPromise>>), }
3881
3882impl std::fmt::Debug for Value {
3883 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3884 match self {
3885 Value::Number(n) => write!(f, "Number({})", n),
3886 Value::String(s) => write!(f, "String({})", String::from_utf16_lossy(s)),
3887 Value::Boolean(b) => write!(f, "Boolean({})", b),
3888 Value::Undefined => write!(f, "Undefined"),
3889 Value::Object(obj) => write!(f, "Object({:p})", Rc::as_ptr(obj)),
3890 Value::Function(name) => write!(f, "Function({})", name),
3891 Value::Closure(_, _, _) => write!(f, "Closure"),
3892 Value::ClassDefinition(_) => write!(f, "ClassDefinition"),
3893 Value::Getter(_, _) => write!(f, "Getter"),
3894 Value::Setter(_, _, _) => write!(f, "Setter"),
3895 Value::Property { .. } => write!(f, "Property"),
3896 Value::Promise(p) => write!(f, "Promise({:p})", Rc::as_ptr(p)),
3897 }
3898 }
3899}
3900
3901pub fn utf8_to_utf16(s: &str) -> Vec<u16> {
3903 s.encode_utf16().collect()
3904}
3905
3906pub fn utf16_to_utf8(v: &[u16]) -> String {
3907 String::from_utf16_lossy(v)
3908}
3909
3910pub fn utf16_len(v: &[u16]) -> usize {
3911 v.len()
3912}
3913
3914pub fn utf16_slice(v: &[u16], start: usize, end: usize) -> Vec<u16> {
3915 if start >= v.len() {
3916 Vec::new()
3917 } else {
3918 let end = end.min(v.len());
3919 v[start..end].to_vec()
3920 }
3921}
3922
3923pub fn utf16_char_at(v: &[u16], index: usize) -> Option<u16> {
3924 v.get(index).copied()
3925}
3926
3927pub fn utf16_to_uppercase(v: &[u16]) -> Vec<u16> {
3928 let s = utf16_to_utf8(v);
3929 utf8_to_utf16(&s.to_uppercase())
3930}
3931
3932pub fn utf16_to_lowercase(v: &[u16]) -> Vec<u16> {
3933 let s = utf16_to_utf8(v);
3934 utf8_to_utf16(&s.to_lowercase())
3935}
3936
3937pub fn utf16_find(v: &[u16], pattern: &[u16]) -> Option<usize> {
3938 if pattern.is_empty() {
3939 return Some(0);
3940 }
3941 (0..=v.len().saturating_sub(pattern.len())).find(|&i| v[i..i + pattern.len()] == *pattern)
3942}
3943
3944pub fn utf16_rfind(v: &[u16], pattern: &[u16]) -> Option<usize> {
3945 if pattern.is_empty() {
3946 return Some(v.len());
3947 }
3948 (0..=v.len().saturating_sub(pattern.len()))
3949 .rev()
3950 .find(|&i| v[i..i + pattern.len()] == *pattern)
3951}
3952
3953pub fn utf16_replace(v: &[u16], search: &[u16], replace: &[u16]) -> Vec<u16> {
3954 if let Some(pos) = utf16_find(v, search) {
3955 let mut result = v[..pos].to_vec();
3956 result.extend_from_slice(replace);
3957 result.extend_from_slice(&v[pos + search.len()..]);
3958 result
3959 } else {
3960 v.to_vec()
3961 }
3962}
3963
3964pub fn values_equal(a: &Value, b: &Value) -> bool {
3966 match (a, b) {
3967 (Value::Number(na), Value::Number(nb)) => na == nb,
3968 (Value::String(sa), Value::String(sb)) => sa == sb,
3969 (Value::Boolean(ba), Value::Boolean(bb)) => ba == bb,
3970 (Value::Undefined, Value::Undefined) => true,
3971 (Value::Object(_), Value::Object(_)) => false, _ => false, }
3974}
3975
3976pub fn value_to_string(val: &Value) -> String {
3978 match val {
3979 Value::Number(n) => n.to_string(),
3980 Value::String(s) => String::from_utf16_lossy(s),
3981 Value::Boolean(b) => b.to_string(),
3982 Value::Undefined => "undefined".to_string(),
3983 Value::Object(_) => "[object Object]".to_string(),
3984 Value::Function(name) => format!("function {}", name),
3985 Value::Closure(_, _, _) => "function".to_string(),
3986 Value::ClassDefinition(_) => "class".to_string(),
3987 Value::Getter(_, _) => "getter".to_string(),
3988 Value::Setter(_, _, _) => "setter".to_string(),
3989 Value::Property { .. } => "[property]".to_string(),
3990 Value::Promise(_) => "[object Promise]".to_string(),
3991 }
3992}
3993
3994pub fn value_to_sort_string(val: &Value) -> String {
3996 match val {
3997 Value::Number(n) => {
3998 if n.is_nan() {
3999 "NaN".to_string()
4000 } else if *n == f64::INFINITY {
4001 "Infinity".to_string()
4002 } else if *n == f64::NEG_INFINITY {
4003 "-Infinity".to_string()
4004 } else {
4005 n.to_string()
4006 }
4007 }
4008 Value::String(s) => String::from_utf16_lossy(s),
4009 Value::Boolean(b) => b.to_string(),
4010 Value::Undefined => "undefined".to_string(),
4011 Value::Object(_) => "[object Object]".to_string(),
4012 Value::Function(name) => format!("[function {}]", name),
4013 Value::Closure(_, _, _) => "[function]".to_string(),
4014 Value::ClassDefinition(_) => "[class]".to_string(),
4015 Value::Getter(_, _) => "[getter]".to_string(),
4016 Value::Setter(_, _, _) => "[setter]".to_string(),
4017 Value::Property { .. } => "[property]".to_string(),
4018 Value::Promise(_) => "[object Promise]".to_string(),
4019 }
4020}
4021
4022pub fn obj_get_value<T: AsRef<str>>(js_obj: &JSObjectDataPtr, key: T) -> Result<Option<Rc<RefCell<Value>>>, JSError> {
4024 let obj = js_obj.borrow().get(key.as_ref());
4025 if let Some(val) = obj {
4026 let val_clone = val.borrow().clone();
4028 match val_clone {
4029 Value::Property { value, getter, .. } => {
4030 log::trace!("obj_get_value - property descriptor found for key {}", key.as_ref());
4031 if let Some((body, env)) = getter {
4032 let getter_env = Rc::new(RefCell::new(JSObjectData::new()));
4034 getter_env.borrow_mut().prototype = Some(env);
4035 env_set(&getter_env, "this", Value::Object(js_obj.clone()))?;
4036 let result = evaluate_statements(&getter_env, &body)?;
4037 Ok(Some(Rc::new(RefCell::new(result))))
4038 } else if let Some(val_rc) = value {
4039 Ok(Some(val_rc))
4040 } else {
4041 Ok(Some(Rc::new(RefCell::new(Value::Undefined))))
4042 }
4043 }
4044 Value::Getter(body, env) => {
4045 log::trace!("obj_get_value - getter found for key {}", key.as_ref());
4046 let getter_env = Rc::new(RefCell::new(JSObjectData::new()));
4048 getter_env.borrow_mut().prototype = Some(env);
4049 env_set(&getter_env, "this", Value::Object(js_obj.clone()))?;
4050 let result = evaluate_statements(&getter_env, &body)?;
4051 Ok(Some(Rc::new(RefCell::new(result))))
4052 }
4053 _ => {
4054 log::trace!("obj_get_value - raw value found for key {}", key.as_ref());
4055 Ok(Some(val.clone()))
4056 }
4057 }
4058 } else if let Some(ref proto) = js_obj.borrow().prototype {
4059 obj_get_value(proto, key)
4060 } else {
4061 Ok(None)
4062 }
4063}
4064
4065pub fn obj_set_value<T: AsRef<str>>(js_obj: &JSObjectDataPtr, key: T, val: Value) -> Result<(), JSError> {
4066 let key = key.as_ref().to_string();
4067 let existing_opt = js_obj.borrow().get(&key);
4069 if let Some(existing) = existing_opt {
4070 match existing.borrow().clone() {
4071 Value::Property { value: _, getter, setter } => {
4072 if let Some((param, body, env)) = setter {
4073 let setter_env = Rc::new(RefCell::new(JSObjectData::new()));
4075 setter_env.borrow_mut().prototype = Some(env);
4076 env_set(&setter_env, "this", Value::Object(js_obj.clone()))?;
4077 env_set(&setter_env, ¶m[0], val)?;
4078 let _v = evaluate_statements(&setter_env, &body)?;
4079 } else {
4080 let value = Some(Rc::new(RefCell::new(val)));
4082 let new_prop = Value::Property { value, getter, setter };
4083 js_obj.borrow_mut().insert(key, Rc::new(RefCell::new(new_prop)));
4084 }
4085 return Ok(());
4086 }
4087 Value::Setter(param, body, env) => {
4088 let setter_env = Rc::new(RefCell::new(JSObjectData::new()));
4090 setter_env.borrow_mut().prototype = Some(env);
4091 env_set(&setter_env, "this", Value::Object(js_obj.clone()))?;
4092 env_set(&setter_env, ¶m[0], val)?;
4093 evaluate_statements(&setter_env, &body)?;
4094 return Ok(());
4095 }
4096 _ => {}
4097 }
4098 }
4099 js_obj.borrow_mut().insert(key, Rc::new(RefCell::new(val)));
4101 Ok(())
4102}
4103
4104pub fn obj_set_rc(map: &JSObjectDataPtr, key: &str, val_rc: Rc<RefCell<Value>>) {
4105 map.borrow_mut().insert(key.to_string(), val_rc);
4106}
4107
4108pub fn obj_delete(map: &JSObjectDataPtr, key: &str) -> bool {
4109 map.borrow_mut().remove(key);
4110 true }
4112
4113pub fn env_get<T: AsRef<str>>(env: &JSObjectDataPtr, key: T) -> Option<Rc<RefCell<Value>>> {
4114 env.borrow().get(key.as_ref())
4115}
4116
4117pub fn env_set<T: AsRef<str>>(env: &JSObjectDataPtr, key: T, val: Value) -> Result<(), JSError> {
4118 let key = key.as_ref();
4119 if env.borrow().is_const(key) {
4120 return Err(JSError::TypeError {
4121 message: format!("Assignment to constant variable '{key}'"),
4122 });
4123 }
4124 env.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(val)));
4125 Ok(())
4126}
4127
4128pub fn env_set_recursive<T: AsRef<str>>(env: &JSObjectDataPtr, key: T, val: Value) -> Result<(), JSError> {
4129 let key = key.as_ref();
4130 let mut current = env.clone();
4131 loop {
4132 if current.borrow().contains_key(key) {
4133 return env_set(¤t, key, val);
4134 }
4135 let parent_opt = current.borrow().prototype.clone();
4136 if let Some(parent) = parent_opt {
4137 current = parent;
4138 } else {
4139 return env_set(env, key, val);
4141 }
4142 }
4143}
4144
4145pub fn env_set_var(env: &JSObjectDataPtr, key: &str, val: Value) -> Result<(), JSError> {
4146 let mut current = env.clone();
4147 loop {
4148 if current.borrow().is_function_scope {
4149 return env_set(¤t, key, val);
4150 }
4151 let parent_opt = current.borrow().prototype.clone();
4152 if let Some(parent) = parent_opt {
4153 current = parent;
4154 } else {
4155 return env_set(env, key, val);
4157 }
4158 }
4159}
4160
4161fn collect_var_names(statements: &[Statement], names: &mut std::collections::HashSet<String>) {
4162 for stmt in statements {
4163 match stmt {
4164 Statement::Var(name, _) => {
4165 names.insert(name.clone());
4166 }
4167 Statement::If(_, then_body, else_body) => {
4168 collect_var_names(then_body, names);
4169 if let Some(else_stmts) = else_body {
4170 collect_var_names(else_stmts, names);
4171 }
4172 }
4173 Statement::For(_, _, _, body) => {
4174 collect_var_names(body, names);
4175 }
4176 Statement::ForOf(_, _, body) => {
4177 collect_var_names(body, names);
4178 }
4179 Statement::While(_, body) => {
4180 collect_var_names(body, names);
4181 }
4182 Statement::DoWhile(body, _) => {
4183 collect_var_names(body, names);
4184 }
4185 Statement::Switch(_, cases) => {
4186 for case in cases {
4187 match case {
4188 SwitchCase::Case(_, stmts) => collect_var_names(stmts, names),
4189 SwitchCase::Default(stmts) => collect_var_names(stmts, names),
4190 }
4191 }
4192 }
4193 Statement::TryCatch(try_body, _, catch_body, finally_body) => {
4194 collect_var_names(try_body, names);
4195 collect_var_names(catch_body, names);
4196 if let Some(finally_stmts) = finally_body {
4197 collect_var_names(finally_stmts, names);
4198 }
4199 }
4200 _ => {}
4201 }
4202 }
4203}
4204
4205pub fn env_set_const(env: &JSObjectDataPtr, key: &str, val: Value) {
4206 let mut env_mut = env.borrow_mut();
4207 env_mut.insert(key.to_string(), Rc::new(RefCell::new(val)));
4208 env_mut.set_const(key.to_string());
4209}
4210
4211pub fn get_prop_env(env: &JSObjectDataPtr, obj_expr: &Expr, prop: &str) -> Result<Option<Rc<RefCell<Value>>>, JSError> {
4214 let obj_val = evaluate_expr(env, obj_expr)?;
4215 match obj_val {
4216 Value::Object(map) => obj_get_value(&map, prop),
4217 _ => Ok(None),
4218 }
4219}
4220
4221pub fn set_prop_env(env: &JSObjectDataPtr, obj_expr: &Expr, prop: &str, val: Value) -> Result<Option<Value>, JSError> {
4229 if let Expr::Var(varname) = obj_expr
4231 && let Some(rc_val) = env_get(env, varname)
4232 {
4233 let mut borrowed = rc_val.borrow_mut();
4234 if let Value::Object(ref mut map) = *borrowed {
4235 if prop == "__proto__" {
4237 if let Value::Object(proto_map) = val {
4238 map.borrow_mut().prototype = Some(proto_map);
4239 return Ok(None);
4240 } else {
4241 map.borrow_mut().prototype = None;
4243 return Ok(None);
4244 }
4245 }
4246
4247 obj_set_value(map, prop, val)?;
4248 return Ok(None);
4249 }
4250 }
4251
4252 let obj_val = evaluate_expr(env, obj_expr)?;
4254 match obj_val {
4255 Value::Object(obj) => {
4256 if prop == "__proto__" {
4258 if let Value::Object(proto_map) = val {
4259 obj.borrow_mut().prototype = Some(proto_map);
4260 return Ok(Some(Value::Object(obj)));
4261 } else {
4262 obj.borrow_mut().prototype = None;
4263 return Ok(Some(Value::Object(obj)));
4264 }
4265 }
4266
4267 obj_set_value(&obj, prop, val)?;
4268 Ok(Some(Value::Object(obj)))
4269 }
4270 _ => Err(JSError::EvaluationError {
4271 message: "not an object".to_string(),
4272 }),
4273 }
4274}
4275
4276#[derive(Clone, Debug)]
4277pub enum SwitchCase {
4278 Case(Expr, Vec<Statement>), Default(Vec<Statement>), }
4281
4282#[derive(Clone)]
4283pub enum Statement {
4284 Let(String, Option<Expr>),
4285 Var(String, Option<Expr>),
4286 Const(String, Expr),
4287 LetDestructuringArray(Vec<DestructuringElement>, Expr), ConstDestructuringArray(Vec<DestructuringElement>, Expr), LetDestructuringObject(Vec<ObjectDestructuringElement>, Expr), ConstDestructuringObject(Vec<ObjectDestructuringElement>, Expr), Class(String, Option<String>, Vec<ClassMember>), Assign(String, Expr), Expr(Expr),
4294 Return(Option<Expr>),
4295 If(Expr, Vec<Statement>, Option<Vec<Statement>>), For(Option<Box<Statement>>, Option<Expr>, Option<Box<Statement>>, Vec<Statement>), ForOf(String, Expr, Vec<Statement>), While(Expr, Vec<Statement>), DoWhile(Vec<Statement>, Expr), Switch(Expr, Vec<SwitchCase>), Break,
4302 Continue,
4303 TryCatch(Vec<Statement>, String, Vec<Statement>, Option<Vec<Statement>>), Throw(Expr), }
4306
4307impl std::fmt::Debug for Statement {
4308 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4309 match self {
4310 Statement::Let(var, expr) => write!(f, "Let({}, {:?})", var, expr),
4311 Statement::Var(var, expr) => write!(f, "Var({}, {:?})", var, expr),
4312 Statement::Const(var, expr) => write!(f, "Const({}, {:?})", var, expr),
4313 Statement::LetDestructuringArray(pattern, expr) => write!(f, "LetDestructuringArray({:?}, {:?})", pattern, expr),
4314 Statement::ConstDestructuringArray(pattern, expr) => write!(f, "ConstDestructuringArray({:?}, {:?})", pattern, expr),
4315 Statement::LetDestructuringObject(pattern, expr) => write!(f, "LetDestructuringObject({:?}, {:?})", pattern, expr),
4316 Statement::ConstDestructuringObject(pattern, expr) => write!(f, "ConstDestructuringObject({:?}, {:?})", pattern, expr),
4317 Statement::Class(name, extends, members) => write!(f, "Class({name}, {extends:?}, {members:?})"),
4318 Statement::Assign(var, expr) => write!(f, "Assign({}, {:?})", var, expr),
4319 Statement::Expr(expr) => write!(f, "Expr({:?})", expr),
4320 Statement::Return(Some(expr)) => write!(f, "Return({:?})", expr),
4321 Statement::Return(None) => write!(f, "Return(None)"),
4322 Statement::If(cond, then_body, else_body) => {
4323 write!(f, "If({:?}, {:?}, {:?})", cond, then_body, else_body)
4324 }
4325 Statement::For(init, cond, incr, body) => {
4326 write!(f, "For({:?}, {:?}, {:?}, {:?})", init, cond, incr, body)
4327 }
4328 Statement::ForOf(var, iterable, body) => {
4329 write!(f, "ForOf({}, {:?}, {:?})", var, iterable, body)
4330 }
4331 Statement::While(cond, body) => {
4332 write!(f, "While({:?}, {:?})", cond, body)
4333 }
4334 Statement::DoWhile(body, cond) => {
4335 write!(f, "DoWhile({:?}, {:?})", body, cond)
4336 }
4337 Statement::Switch(expr, cases) => {
4338 write!(f, "Switch({:?}, {:?})", expr, cases)
4339 }
4340 Statement::Break => write!(f, "Break"),
4341 Statement::Continue => write!(f, "Continue"),
4342 Statement::TryCatch(try_body, catch_param, catch_body, finally_body) => {
4343 write!(f, "TryCatch({:?}, {}, {:?}, {:?})", try_body, catch_param, catch_body, finally_body)
4344 }
4345 Statement::Throw(expr) => {
4346 write!(f, "Throw({:?})", expr)
4347 }
4348 }
4349 }
4350}
4351
4352#[derive(Debug, Clone)]
4353pub enum Expr {
4354 Number(f64),
4355 StringLit(Vec<u16>),
4356 Boolean(bool),
4357 Var(String),
4358 Binary(Box<Expr>, BinaryOp, Box<Expr>),
4359 UnaryNeg(Box<Expr>),
4360 TypeOf(Box<Expr>),
4361 Delete(Box<Expr>),
4362 Void(Box<Expr>),
4363 Assign(Box<Expr>, Box<Expr>), LogicalAndAssign(Box<Expr>, Box<Expr>), LogicalOrAssign(Box<Expr>, Box<Expr>), NullishAssign(Box<Expr>, Box<Expr>), AddAssign(Box<Expr>, Box<Expr>), SubAssign(Box<Expr>, Box<Expr>), MulAssign(Box<Expr>, Box<Expr>), DivAssign(Box<Expr>, Box<Expr>), ModAssign(Box<Expr>, Box<Expr>), Increment(Box<Expr>),
4373 Decrement(Box<Expr>),
4374 PostIncrement(Box<Expr>),
4375 PostDecrement(Box<Expr>),
4376 Index(Box<Expr>, Box<Expr>),
4377 Property(Box<Expr>, String),
4378 Call(Box<Expr>, Vec<Expr>),
4379 Function(Vec<String>, Vec<Statement>), AsyncFunction(Vec<String>, Vec<Statement>), ArrowFunction(Vec<String>, Vec<Statement>), Object(Vec<(String, Expr)>), Array(Vec<Expr>), Getter(Box<Expr>), Setter(Box<Expr>), Spread(Box<Expr>), OptionalProperty(Box<Expr>, String), OptionalCall(Box<Expr>, Vec<Expr>), Await(Box<Expr>), This, New(Box<Expr>, Vec<Expr>), Super, SuperCall(Vec<Expr>), SuperProperty(String), SuperMethod(String, Vec<Expr>), ArrayDestructuring(Vec<DestructuringElement>), ObjectDestructuring(Vec<ObjectDestructuringElement>), Value(Value), }
4400
4401#[derive(Debug, Clone)]
4402pub enum BinaryOp {
4403 Add,
4404 Sub,
4405 Mul,
4406 Div,
4407 Mod,
4408 Equal,
4409 StrictEqual,
4410 LessThan,
4411 GreaterThan,
4412 LessEqual,
4413 GreaterEqual,
4414 InstanceOf,
4415 In,
4416 NullishCoalescing,
4417}
4418
4419#[derive(Debug, Clone)]
4420pub enum DestructuringElement {
4421 Variable(String), NestedArray(Vec<DestructuringElement>), NestedObject(Vec<ObjectDestructuringElement>), Rest(String), Empty, }
4427
4428#[derive(Debug, Clone)]
4429pub enum ObjectDestructuringElement {
4430 Property { key: String, value: DestructuringElement }, Rest(String), }
4433
4434fn parse_string_literal(chars: &[char], start: &mut usize, end_char: char) -> Result<Vec<u16>, JSError> {
4435 let mut result = Vec::new();
4436 while *start < chars.len() && chars[*start] != end_char {
4437 if chars[*start] == '\\' {
4438 *start += 1;
4439 if *start >= chars.len() {
4440 return Err(JSError::TokenizationError);
4441 }
4442 match chars[*start] {
4443 'n' => result.push('\n' as u16),
4444 't' => result.push('\t' as u16),
4445 'r' => result.push('\r' as u16),
4446 '\\' => result.push('\\' as u16),
4447 '"' => result.push('"' as u16),
4448 '\'' => result.push('\'' as u16),
4449 '`' => result.push('`' as u16),
4450 'u' => {
4451 *start += 1;
4453 if *start + 4 > chars.len() {
4454 return Err(JSError::TokenizationError);
4455 }
4456 let hex_str: String = chars[*start..*start + 4].iter().collect();
4457 *start += 3; match u16::from_str_radix(&hex_str, 16) {
4459 Ok(code) => {
4460 result.push(code);
4461 }
4462 Err(_) => return Err(JSError::TokenizationError), }
4464 }
4465 'x' => {
4466 *start += 1;
4468 if *start + 2 > chars.len() {
4469 return Err(JSError::TokenizationError);
4470 }
4471 let hex_str: String = chars[*start..*start + 2].iter().collect();
4472 *start += 1; match u8::from_str_radix(&hex_str, 16) {
4474 Ok(code) => {
4475 result.push(code as u16);
4476 }
4477 Err(_) => return Err(JSError::TokenizationError),
4478 }
4479 }
4480 other => {
4483 result.push('\\' as u16);
4484 result.push(other as u16);
4485 }
4486 }
4487 } else {
4488 result.push(chars[*start] as u16);
4489 }
4490 *start += 1;
4491 }
4492 if *start >= chars.len() {
4493 return Err(JSError::TokenizationError);
4494 }
4495 Ok(result)
4496}
4497
4498pub fn tokenize(expr: &str) -> Result<Vec<Token>, JSError> {
4499 let mut tokens = Vec::new();
4500 let chars: Vec<char> = expr.chars().collect();
4501 let mut i = 0;
4502 while i < chars.len() {
4503 match chars[i] {
4504 ' ' | '\t' | '\n' => i += 1,
4505 '+' => {
4506 if i + 1 < chars.len() && chars[i + 1] == '+' {
4507 tokens.push(Token::Increment);
4508 i += 2;
4509 } else if i + 1 < chars.len() && chars[i + 1] == '=' {
4510 tokens.push(Token::AddAssign);
4511 i += 2;
4512 } else {
4513 tokens.push(Token::Plus);
4514 i += 1;
4515 }
4516 }
4517 '-' => {
4518 if i + 1 < chars.len() && chars[i + 1] == '-' {
4519 tokens.push(Token::Decrement);
4520 i += 2;
4521 } else if i + 1 < chars.len() && chars[i + 1] == '=' {
4522 tokens.push(Token::SubAssign);
4523 i += 2;
4524 } else {
4525 tokens.push(Token::Minus);
4526 i += 1;
4527 }
4528 }
4529 '*' => {
4530 if i + 1 < chars.len() && chars[i + 1] == '=' {
4531 tokens.push(Token::MulAssign);
4532 i += 2;
4533 } else {
4534 tokens.push(Token::Multiply);
4535 i += 1;
4536 }
4537 }
4538 '/' => {
4539 if i + 1 < chars.len() && chars[i + 1] == '=' {
4540 tokens.push(Token::DivAssign);
4541 i += 2;
4542 } else {
4543 tokens.push(Token::Divide);
4544 i += 1;
4545 }
4546 }
4547 '%' => {
4548 if i + 1 < chars.len() && chars[i + 1] == '=' {
4549 tokens.push(Token::ModAssign);
4550 i += 2;
4551 } else {
4552 tokens.push(Token::Mod);
4553 i += 1;
4554 }
4555 }
4556 '(' => {
4557 tokens.push(Token::LParen);
4558 i += 1;
4559 }
4560 ')' => {
4561 tokens.push(Token::RParen);
4562 i += 1;
4563 }
4564 '[' => {
4565 tokens.push(Token::LBracket);
4566 i += 1;
4567 }
4568 ']' => {
4569 tokens.push(Token::RBracket);
4570 i += 1;
4571 }
4572 '{' => {
4573 tokens.push(Token::LBrace);
4574 i += 1;
4575 }
4576 '}' => {
4577 tokens.push(Token::RBrace);
4578 i += 1;
4579 }
4580 ':' => {
4581 tokens.push(Token::Colon);
4582 i += 1;
4583 }
4584 '.' => {
4585 if i + 2 < chars.len() && chars[i + 1] == '.' && chars[i + 2] == '.' {
4586 tokens.push(Token::Spread);
4587 i += 3;
4588 } else {
4589 tokens.push(Token::Dot);
4590 i += 1;
4591 }
4592 }
4593 '?' => {
4594 if i + 2 < chars.len() && chars[i + 1] == '?' && chars[i + 2] == '=' {
4596 tokens.push(Token::NullishAssign);
4597 i += 3;
4598 } else if i + 1 < chars.len() && chars[i + 1] == '?' {
4599 tokens.push(Token::NullishCoalescing);
4600 i += 2;
4601 } else if i + 1 < chars.len() && chars[i + 1] == '.' {
4602 tokens.push(Token::OptionalChain);
4603 i += 2;
4604 } else {
4605 return Err(JSError::TokenizationError);
4606 }
4607 }
4608 '=' => {
4609 if i + 1 < chars.len() && chars[i + 1] == '=' {
4610 if i + 2 < chars.len() && chars[i + 2] == '=' {
4611 tokens.push(Token::StrictEqual);
4612 i += 3;
4613 } else {
4614 tokens.push(Token::Equal);
4615 i += 2;
4616 }
4617 } else if i + 1 < chars.len() && chars[i + 1] == '>' {
4618 tokens.push(Token::Arrow);
4619 i += 2;
4620 } else if i + 1 < chars.len() && chars[i + 1] == '+' {
4621 tokens.push(Token::AddAssign);
4622 i += 2;
4623 } else if i + 1 < chars.len() && chars[i + 1] == '-' {
4624 tokens.push(Token::SubAssign);
4625 i += 2;
4626 } else if i + 1 < chars.len() && chars[i + 1] == '*' {
4627 tokens.push(Token::MulAssign);
4628 i += 2;
4629 } else if i + 1 < chars.len() && chars[i + 1] == '/' {
4630 tokens.push(Token::DivAssign);
4631 i += 2;
4632 } else if i + 1 < chars.len() && chars[i + 1] == '%' {
4633 tokens.push(Token::ModAssign);
4634 i += 2;
4635 } else {
4636 tokens.push(Token::Assign);
4637 i += 1;
4638 }
4639 }
4640 '<' => {
4641 if i + 1 < chars.len() && chars[i + 1] == '=' {
4642 tokens.push(Token::LessEqual);
4643 i += 2;
4644 } else {
4645 tokens.push(Token::LessThan);
4646 i += 1;
4647 }
4648 }
4649 '>' => {
4650 if i + 1 < chars.len() && chars[i + 1] == '=' {
4651 tokens.push(Token::GreaterEqual);
4652 i += 2;
4653 } else {
4654 tokens.push(Token::GreaterThan);
4655 i += 1;
4656 }
4657 }
4658 '&' => {
4659 if i + 2 < chars.len() && chars[i + 1] == '&' && chars[i + 2] == '=' {
4661 tokens.push(Token::LogicalAndAssign);
4662 i += 3;
4663 } else if i + 1 < chars.len() && chars[i + 1] == '&' {
4664 tokens.push(Token::LogicalAnd);
4665 i += 2;
4666 } else {
4667 return Err(JSError::TokenizationError);
4668 }
4669 }
4670 '|' => {
4671 if i + 2 < chars.len() && chars[i + 1] == '|' && chars[i + 2] == '=' {
4673 tokens.push(Token::LogicalOrAssign);
4674 i += 3;
4675 } else if i + 1 < chars.len() && chars[i + 1] == '|' {
4676 tokens.push(Token::LogicalOr);
4677 i += 2;
4678 } else {
4679 return Err(JSError::TokenizationError);
4680 }
4681 }
4682 '0'..='9' => {
4683 let start = i;
4684 while i < chars.len() && (chars[i].is_ascii_digit() || chars[i] == '.') {
4686 i += 1;
4687 }
4688 if i < chars.len() && (chars[i] == 'e' || chars[i] == 'E') {
4690 let mut j = i + 1;
4691 if j < chars.len() && (chars[j] == '+' || chars[j] == '-') {
4693 j += 1;
4694 }
4695 if j >= chars.len() || !chars[j].is_ascii_digit() {
4697 return Err(JSError::TokenizationError);
4698 }
4699 while j < chars.len() && chars[j].is_ascii_digit() {
4701 j += 1;
4702 }
4703 i = j;
4704 }
4705 let num_str: String = chars[start..i].iter().collect();
4706 let num = num_str.parse::<f64>().map_err(|_| JSError::TokenizationError)?;
4707 tokens.push(Token::Number(num));
4708 }
4709 '"' => {
4710 i += 1; let mut start = i;
4712 let str_lit = parse_string_literal(&chars, &mut start, '"')?;
4713 tokens.push(Token::StringLit(str_lit));
4714 i = start + 1; }
4716 '\'' => {
4717 i += 1; let mut start = i;
4719 let str_lit = parse_string_literal(&chars, &mut start, '\'')?;
4720 tokens.push(Token::StringLit(str_lit));
4721 i = start + 1; }
4723 '`' => {
4724 i += 1; let mut parts = Vec::new();
4726 let mut current_start = i;
4727 while i < chars.len() && chars[i] != '`' {
4728 if chars[i] == '$' && i + 1 < chars.len() && chars[i + 1] == '{' {
4729 if current_start < i {
4731 let mut start_idx = current_start;
4732 let str_part = parse_string_literal(&chars, &mut start_idx, '$')?;
4733 parts.push(TemplatePart::String(str_part));
4734 i = start_idx; }
4736 i += 2; let expr_start = i;
4738 let mut brace_count = 1;
4739 while i < chars.len() && brace_count > 0 {
4740 if chars[i] == '{' {
4741 brace_count += 1;
4742 } else if chars[i] == '}' {
4743 brace_count -= 1;
4744 }
4745 i += 1;
4746 }
4747 if brace_count != 0 {
4748 return Err(JSError::TokenizationError);
4749 }
4750 let expr_str: String = chars[expr_start..i - 1].iter().collect();
4751 let expr_tokens = tokenize(&expr_str)?;
4753 parts.push(TemplatePart::Expr(expr_tokens));
4754 current_start = i;
4755 } else {
4756 i += 1;
4757 }
4758 }
4759 if i >= chars.len() {
4760 return Err(JSError::TokenizationError);
4761 }
4762 if current_start < i {
4764 let mut start_idx = current_start;
4765 let str_part = parse_string_literal(&chars, &mut start_idx, '`')?;
4766 parts.push(TemplatePart::String(str_part));
4767 }
4768 tokens.push(Token::TemplateString(parts));
4769 i += 1; }
4771 'a'..='z' | 'A'..='Z' | '_' => {
4772 let start = i;
4773 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
4774 i += 1;
4775 }
4776 let ident: String = chars[start..i].iter().collect();
4777 match ident.as_str() {
4778 "let" => tokens.push(Token::Let),
4779 "var" => tokens.push(Token::Var),
4780 "const" => tokens.push(Token::Const),
4781 "class" => tokens.push(Token::Class),
4782 "extends" => tokens.push(Token::Extends),
4783 "super" => tokens.push(Token::Super),
4784 "this" => tokens.push(Token::This),
4785 "static" => tokens.push(Token::Static),
4786 "new" => tokens.push(Token::New),
4787 "instanceof" => tokens.push(Token::InstanceOf),
4788 "typeof" => tokens.push(Token::TypeOf),
4789 "delete" => tokens.push(Token::Delete),
4790 "void" => tokens.push(Token::Void),
4791 "in" => tokens.push(Token::In),
4792 "try" => tokens.push(Token::Try),
4793 "catch" => tokens.push(Token::Catch),
4794 "finally" => tokens.push(Token::Finally),
4795 "throw" => tokens.push(Token::Throw),
4796 "function" => tokens.push(Token::Function),
4797 "return" => tokens.push(Token::Return),
4798 "if" => tokens.push(Token::If),
4799 "else" => tokens.push(Token::Else),
4800 "for" => tokens.push(Token::For),
4801 "while" => tokens.push(Token::While),
4802 "do" => tokens.push(Token::Do),
4803 "switch" => tokens.push(Token::Switch),
4804 "case" => tokens.push(Token::Case),
4805 "default" => tokens.push(Token::Default),
4806 "break" => tokens.push(Token::Break),
4807 "continue" => tokens.push(Token::Continue),
4808 "true" => tokens.push(Token::True),
4809 "false" => tokens.push(Token::False),
4810 "async" => tokens.push(Token::Async),
4811 "await" => tokens.push(Token::Await),
4812 _ => tokens.push(Token::Identifier(ident)),
4813 }
4814 }
4815 ',' => {
4816 tokens.push(Token::Comma);
4817 i += 1;
4818 }
4819 ';' => {
4820 tokens.push(Token::Semicolon);
4821 i += 1;
4822 }
4823 _ => return Err(JSError::TokenizationError),
4824 }
4825 }
4826 Ok(tokens)
4827}
4828
4829#[derive(Debug, Clone)]
4830pub enum TemplatePart {
4831 String(Vec<u16>),
4832 Expr(Vec<Token>),
4833}
4834
4835#[derive(Debug, Clone)]
4836pub enum Token {
4837 Number(f64),
4838 StringLit(Vec<u16>),
4839 TemplateString(Vec<TemplatePart>),
4840 Identifier(String),
4841 Plus,
4842 Minus,
4843 Multiply,
4844 Divide,
4845 Mod,
4846 LParen,
4847 RParen,
4848 LBracket,
4849 RBracket,
4850 LBrace,
4851 RBrace,
4852 Colon,
4853 Dot,
4854 Comma,
4855 Let,
4856 Var,
4857 Const,
4858 Class,
4859 Extends,
4860 Super,
4861 This,
4862 Static,
4863 New,
4864 InstanceOf,
4865 TypeOf,
4866 In,
4867 Delete,
4868 Void,
4869 Function,
4870 Return,
4871 If,
4872 Else,
4873 For,
4874 While,
4875 Do,
4876 Switch,
4877 Case,
4878 Default,
4879 Break,
4880 Continue,
4881 Try,
4882 Catch,
4883 Finally,
4884 Throw,
4885 Assign,
4886 Semicolon,
4887 Equal,
4888 StrictEqual,
4889 LessThan,
4890 GreaterThan,
4891 LessEqual,
4892 GreaterEqual,
4893 True,
4894 False,
4895 Arrow,
4896 Spread,
4897 OptionalChain,
4898 NullishCoalescing,
4899 LogicalAnd,
4900 LogicalOr,
4901 LogicalAndAssign,
4902 LogicalOrAssign,
4903 NullishAssign,
4904 AddAssign,
4905 SubAssign,
4906 MulAssign,
4907 DivAssign,
4908 ModAssign,
4909 Increment,
4910 Decrement,
4911 Async,
4912 Await,
4913}
4914
4915impl Token {
4916 pub fn as_identifier_string(&self) -> Option<String> {
4918 match self {
4919 Token::Identifier(s) => Some(s.clone()),
4920 Token::Let => Some("let".to_string()),
4921 Token::Var => Some("var".to_string()),
4922 Token::Const => Some("const".to_string()),
4923 Token::Class => Some("class".to_string()),
4924 Token::Extends => Some("extends".to_string()),
4925 Token::Super => Some("super".to_string()),
4926 Token::This => Some("this".to_string()),
4927 Token::Static => Some("static".to_string()),
4928 Token::New => Some("new".to_string()),
4929 Token::InstanceOf => Some("instanceof".to_string()),
4930 Token::TypeOf => Some("typeof".to_string()),
4931 Token::In => Some("in".to_string()),
4932 Token::Delete => Some("delete".to_string()),
4933 Token::Void => Some("void".to_string()),
4934 Token::Function => Some("function".to_string()),
4935 Token::Return => Some("return".to_string()),
4936 Token::If => Some("if".to_string()),
4937 Token::Else => Some("else".to_string()),
4938 Token::For => Some("for".to_string()),
4939 Token::While => Some("while".to_string()),
4940 Token::Do => Some("do".to_string()),
4941 Token::Switch => Some("switch".to_string()),
4942 Token::Case => Some("case".to_string()),
4943 Token::Default => Some("default".to_string()),
4944 Token::Break => Some("break".to_string()),
4945 Token::Continue => Some("continue".to_string()),
4946 Token::Try => Some("try".to_string()),
4947 Token::Catch => Some("catch".to_string()),
4948 Token::Finally => Some("finally".to_string()),
4949 Token::Throw => Some("throw".to_string()),
4950 Token::True => Some("true".to_string()),
4951 Token::False => Some("false".to_string()),
4952 Token::Async => Some("async".to_string()),
4953 Token::Await => Some("await".to_string()),
4954 _ => None,
4955 }
4956 }
4957}
4958
4959fn is_truthy(val: &Value) -> bool {
4960 match val {
4961 Value::Number(n) => *n != 0.0 && !n.is_nan(),
4962 Value::String(s) => !s.is_empty(),
4963 Value::Boolean(b) => *b,
4964 Value::Undefined => false,
4965 Value::Object(_) => true,
4966 Value::Function(_) => true,
4967 Value::Closure(_, _, _) => true,
4968 Value::ClassDefinition(_) => true,
4969 Value::Getter(_, _) => true,
4970 Value::Setter(_, _, _) => true,
4971 Value::Property { .. } => true,
4972 Value::Promise(_) => true,
4973 }
4974}
4975
4976fn parse_parameters(tokens: &mut Vec<Token>) -> Result<Vec<String>, JSError> {
4977 let mut params = Vec::new();
4978 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
4979 loop {
4980 if let Some(Token::Identifier(param)) = tokens.first().cloned() {
4981 tokens.remove(0);
4982 params.push(param);
4983 if tokens.is_empty() {
4984 return Err(JSError::ParseError);
4985 }
4986 if matches!(tokens[0], Token::RParen) {
4987 break;
4988 }
4989 if !matches!(tokens[0], Token::Comma) {
4990 return Err(JSError::ParseError);
4991 }
4992 tokens.remove(0); } else {
4994 return Err(JSError::ParseError);
4995 }
4996 }
4997 }
4998 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
4999 return Err(JSError::ParseError);
5000 }
5001 tokens.remove(0); Ok(params)
5003}
5004
5005fn parse_statement_block(tokens: &mut Vec<Token>) -> Result<Vec<Statement>, JSError> {
5006 let body = parse_statements(tokens)?;
5007 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
5008 return Err(JSError::ParseError);
5009 }
5010 tokens.remove(0); Ok(body)
5012}
5013
5014fn parse_expression(tokens: &mut Vec<Token>) -> Result<Expr, JSError> {
5015 parse_assignment(tokens)
5016}
5017
5018fn parse_assignment(tokens: &mut Vec<Token>) -> Result<Expr, JSError> {
5019 let left = parse_nullish(tokens)?;
5020 if tokens.is_empty() {
5021 return Ok(left);
5022 }
5023 match &tokens[0] {
5024 Token::Assign => {
5025 tokens.remove(0);
5026 let right = parse_assignment(tokens)?;
5027 Ok(Expr::Assign(Box::new(left), Box::new(right)))
5028 }
5029 Token::LogicalAndAssign => {
5030 tokens.remove(0);
5031 let right = parse_assignment(tokens)?;
5032 Ok(Expr::LogicalAndAssign(Box::new(left), Box::new(right)))
5033 }
5034 Token::LogicalOrAssign => {
5035 tokens.remove(0);
5036 let right = parse_assignment(tokens)?;
5037 Ok(Expr::LogicalOrAssign(Box::new(left), Box::new(right)))
5038 }
5039 Token::NullishAssign => {
5040 tokens.remove(0);
5041 let right = parse_assignment(tokens)?;
5042 Ok(Expr::NullishAssign(Box::new(left), Box::new(right)))
5043 }
5044 Token::AddAssign => {
5045 tokens.remove(0);
5046 let right = parse_assignment(tokens)?;
5047 Ok(Expr::AddAssign(Box::new(left), Box::new(right)))
5048 }
5049 Token::SubAssign => {
5050 tokens.remove(0);
5051 let right = parse_assignment(tokens)?;
5052 Ok(Expr::SubAssign(Box::new(left), Box::new(right)))
5053 }
5054 Token::MulAssign => {
5055 tokens.remove(0);
5056 let right = parse_assignment(tokens)?;
5057 Ok(Expr::MulAssign(Box::new(left), Box::new(right)))
5058 }
5059 Token::DivAssign => {
5060 tokens.remove(0);
5061 let right = parse_assignment(tokens)?;
5062 Ok(Expr::DivAssign(Box::new(left), Box::new(right)))
5063 }
5064 Token::ModAssign => {
5065 tokens.remove(0);
5066 let right = parse_assignment(tokens)?;
5067 Ok(Expr::ModAssign(Box::new(left), Box::new(right)))
5068 }
5069 _ => Ok(left),
5070 }
5071}
5072
5073fn parse_nullish(tokens: &mut Vec<Token>) -> Result<Expr, JSError> {
5074 let left = parse_comparison(tokens)?;
5075 if tokens.is_empty() {
5076 return Ok(left);
5077 }
5078 if matches!(tokens[0], Token::NullishCoalescing) {
5079 tokens.remove(0);
5080 let right = parse_nullish(tokens)?;
5081 Ok(Expr::Binary(Box::new(left), BinaryOp::NullishCoalescing, Box::new(right)))
5082 } else {
5083 Ok(left)
5084 }
5085}
5086
5087fn parse_comparison(tokens: &mut Vec<Token>) -> Result<Expr, JSError> {
5088 let left = parse_additive(tokens)?;
5089 if tokens.is_empty() {
5090 return Ok(left);
5091 }
5092 match &tokens[0] {
5093 Token::Equal => {
5094 tokens.remove(0);
5095 let right = parse_comparison(tokens)?;
5096 Ok(Expr::Binary(Box::new(left), BinaryOp::Equal, Box::new(right)))
5097 }
5098 Token::StrictEqual => {
5099 tokens.remove(0);
5100 let right = parse_comparison(tokens)?;
5101 Ok(Expr::Binary(Box::new(left), BinaryOp::StrictEqual, Box::new(right)))
5102 }
5103 Token::LessThan => {
5104 tokens.remove(0);
5105 let right = parse_comparison(tokens)?;
5106 Ok(Expr::Binary(Box::new(left), BinaryOp::LessThan, Box::new(right)))
5107 }
5108 Token::GreaterThan => {
5109 tokens.remove(0);
5110 let right = parse_comparison(tokens)?;
5111 Ok(Expr::Binary(Box::new(left), BinaryOp::GreaterThan, Box::new(right)))
5112 }
5113 Token::LessEqual => {
5114 tokens.remove(0);
5115 let right = parse_comparison(tokens)?;
5116 Ok(Expr::Binary(Box::new(left), BinaryOp::LessEqual, Box::new(right)))
5117 }
5118 Token::GreaterEqual => {
5119 tokens.remove(0);
5120 let right = parse_comparison(tokens)?;
5121 Ok(Expr::Binary(Box::new(left), BinaryOp::GreaterEqual, Box::new(right)))
5122 }
5123 Token::InstanceOf => {
5124 tokens.remove(0);
5125 let right = parse_comparison(tokens)?;
5126 Ok(Expr::Binary(Box::new(left), BinaryOp::InstanceOf, Box::new(right)))
5127 }
5128 Token::In => {
5129 tokens.remove(0);
5130 let right = parse_comparison(tokens)?;
5131 Ok(Expr::Binary(Box::new(left), BinaryOp::In, Box::new(right)))
5132 }
5133 _ => Ok(left),
5134 }
5135}
5136
5137fn parse_additive(tokens: &mut Vec<Token>) -> Result<Expr, JSError> {
5138 let left = parse_multiplicative(tokens)?;
5139 if tokens.is_empty() {
5140 return Ok(left);
5141 }
5142 match &tokens[0] {
5143 Token::Plus => {
5144 tokens.remove(0);
5145 let right = parse_additive(tokens)?;
5146 Ok(Expr::Binary(Box::new(left), BinaryOp::Add, Box::new(right)))
5147 }
5148 Token::Minus => {
5149 tokens.remove(0);
5150 let right = parse_additive(tokens)?;
5151 Ok(Expr::Binary(Box::new(left), BinaryOp::Sub, Box::new(right)))
5152 }
5153 _ => Ok(left),
5154 }
5155}
5156
5157fn parse_multiplicative(tokens: &mut Vec<Token>) -> Result<Expr, JSError> {
5158 let left = parse_primary(tokens)?;
5159 if tokens.is_empty() {
5160 return Ok(left);
5161 }
5162 match &tokens[0] {
5163 Token::Multiply => {
5164 tokens.remove(0);
5165 let right = parse_multiplicative(tokens)?;
5166 Ok(Expr::Binary(Box::new(left), BinaryOp::Mul, Box::new(right)))
5167 }
5168 Token::Divide => {
5169 tokens.remove(0);
5170 let right = parse_multiplicative(tokens)?;
5171 Ok(Expr::Binary(Box::new(left), BinaryOp::Div, Box::new(right)))
5172 }
5173 Token::Mod => {
5174 tokens.remove(0);
5175 let right = parse_multiplicative(tokens)?;
5176 Ok(Expr::Binary(Box::new(left), BinaryOp::Mod, Box::new(right)))
5177 }
5178 _ => Ok(left),
5179 }
5180}
5181
5182fn parse_primary(tokens: &mut Vec<Token>) -> Result<Expr, JSError> {
5183 if tokens.is_empty() {
5184 return Err(JSError::ParseError);
5185 }
5186 let mut expr = match tokens.remove(0) {
5187 Token::Number(n) => Expr::Number(n),
5188 Token::StringLit(s) => Expr::StringLit(s),
5189 Token::True => Expr::Boolean(true),
5190 Token::False => Expr::Boolean(false),
5191 Token::TypeOf => {
5192 let inner = parse_primary(tokens)?;
5193 Expr::TypeOf(Box::new(inner))
5194 }
5195 Token::Delete => {
5196 let inner = parse_primary(tokens)?;
5197 Expr::Delete(Box::new(inner))
5198 }
5199 Token::Void => {
5200 let inner = parse_primary(tokens)?;
5201 Expr::Void(Box::new(inner))
5202 }
5203 Token::Await => {
5204 let inner = parse_primary(tokens)?;
5205 Expr::Await(Box::new(inner))
5206 }
5207 Token::New => {
5208 let constructor = if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5210 tokens.remove(0);
5211 Expr::Var(name)
5212 } else {
5213 return Err(JSError::ParseError);
5214 };
5215 let args = if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5216 tokens.remove(0); let mut args = Vec::new();
5218 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
5219 loop {
5220 let arg = parse_expression(tokens)?;
5221 args.push(arg);
5222 if tokens.is_empty() {
5223 return Err(JSError::ParseError);
5224 }
5225 if matches!(tokens[0], Token::RParen) {
5226 break;
5227 }
5228 if !matches!(tokens[0], Token::Comma) {
5229 return Err(JSError::ParseError);
5230 }
5231 tokens.remove(0); }
5233 }
5234 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5235 return Err(JSError::ParseError);
5236 }
5237 tokens.remove(0); args
5239 } else {
5240 Vec::new()
5241 };
5242 Expr::New(Box::new(constructor), args)
5243 }
5244 Token::Minus => {
5245 let inner = parse_primary(tokens)?;
5246 Expr::UnaryNeg(Box::new(inner))
5247 }
5248 Token::Increment => {
5249 let inner = parse_primary(tokens)?;
5250 Expr::Increment(Box::new(inner))
5251 }
5252 Token::Decrement => {
5253 let inner = parse_primary(tokens)?;
5254 Expr::Decrement(Box::new(inner))
5255 }
5256 Token::Spread => {
5257 let inner = parse_primary(tokens)?;
5258 Expr::Spread(Box::new(inner))
5259 }
5260 Token::TemplateString(parts) => {
5261 if parts.is_empty() {
5262 Expr::StringLit(Vec::new())
5263 } else if parts.len() == 1 {
5264 match &parts[0] {
5265 TemplatePart::String(s) => Expr::StringLit(s.clone()),
5266 TemplatePart::Expr(expr_tokens) => {
5267 let mut expr_tokens = expr_tokens.clone();
5268 parse_expression(&mut expr_tokens)?
5269 }
5270 }
5271 } else {
5272 let mut expr = match &parts[0] {
5274 TemplatePart::String(s) => Expr::StringLit(s.clone()),
5275 TemplatePart::Expr(expr_tokens) => {
5276 let mut expr_tokens = expr_tokens.clone();
5277 parse_expression(&mut expr_tokens)?
5278 }
5279 };
5280 for part in &parts[1..] {
5281 let right = match part {
5282 TemplatePart::String(s) => Expr::StringLit(s.clone()),
5283 TemplatePart::Expr(expr_tokens) => {
5284 let mut expr_tokens = expr_tokens.clone();
5285 parse_expression(&mut expr_tokens)?
5286 }
5287 };
5288 expr = Expr::Binary(Box::new(expr), BinaryOp::Add, Box::new(right));
5289 }
5290 expr
5291 }
5292 }
5293 Token::Identifier(name) => {
5294 let mut expr = Expr::Var(name.clone());
5295 if !tokens.is_empty() && matches!(tokens[0], Token::Arrow) {
5296 tokens.remove(0);
5297 let body = parse_arrow_body(tokens)?;
5298 expr = Expr::ArrowFunction(vec![name], body);
5299 }
5300 expr
5301 }
5302 Token::This => Expr::This,
5303 Token::Super => {
5304 if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5306 tokens.remove(0); let mut args = Vec::new();
5308 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
5309 loop {
5310 let arg = parse_expression(tokens)?;
5311 args.push(arg);
5312 if tokens.is_empty() {
5313 return Err(JSError::ParseError);
5314 }
5315 if matches!(tokens[0], Token::RParen) {
5316 break;
5317 }
5318 if !matches!(tokens[0], Token::Comma) {
5319 return Err(JSError::ParseError);
5320 }
5321 tokens.remove(0); }
5323 }
5324 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5325 return Err(JSError::ParseError);
5326 }
5327 tokens.remove(0); Expr::SuperCall(args)
5329 } else if !tokens.is_empty() && matches!(tokens[0], Token::Dot) {
5330 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::Identifier(_)) {
5332 return Err(JSError::ParseError);
5333 }
5334 let prop = if let Token::Identifier(name) = tokens.remove(0) {
5335 name
5336 } else {
5337 return Err(JSError::ParseError);
5338 };
5339 if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5341 tokens.remove(0); let mut args = Vec::new();
5343 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
5344 loop {
5345 let arg = parse_expression(tokens)?;
5346 args.push(arg);
5347 if tokens.is_empty() {
5348 return Err(JSError::ParseError);
5349 }
5350 if matches!(tokens[0], Token::RParen) {
5351 break;
5352 }
5353 if !matches!(tokens[0], Token::Comma) {
5354 return Err(JSError::ParseError);
5355 }
5356 tokens.remove(0); }
5358 }
5359 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5360 return Err(JSError::ParseError);
5361 }
5362 tokens.remove(0); Expr::SuperMethod(prop, args)
5364 } else {
5365 Expr::SuperProperty(prop)
5366 }
5367 } else {
5368 Expr::Super
5369 }
5370 }
5371 Token::LBrace => {
5372 let mut properties = Vec::new();
5374 if !tokens.is_empty() && matches!(tokens[0], Token::RBrace) {
5375 tokens.remove(0); return Ok(Expr::Object(properties));
5378 }
5379 loop {
5380 if !tokens.is_empty() && matches!(tokens[0], Token::Spread) {
5382 tokens.remove(0); let expr = parse_expression(tokens)?;
5384 properties.push(("".to_string(), Expr::Spread(Box::new(expr))));
5385 } else {
5386 let is_getter = !tokens.is_empty() && matches!(tokens[0], Token::Identifier(ref id) if id == "get");
5388 let is_setter = !tokens.is_empty() && matches!(tokens[0], Token::Identifier(ref id) if id == "set");
5389
5390 if is_getter || is_setter {
5391 tokens.remove(0); }
5393
5394 let key = if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5396 tokens.remove(0);
5397 name
5398 } else if let Some(Token::StringLit(s)) = tokens.first().cloned() {
5399 tokens.remove(0);
5400 String::from_utf16_lossy(&s)
5401 } else {
5402 return Err(JSError::ParseError);
5403 };
5404
5405 if is_getter || is_setter {
5407 if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
5409 return Err(JSError::ParseError);
5410 }
5411 tokens.remove(0); let mut params = Vec::new();
5414 if is_setter {
5415 if let Some(Token::Identifier(param)) = tokens.first().cloned() {
5417 tokens.remove(0);
5418 params.push(param);
5419 } else {
5420 return Err(JSError::ParseError);
5421 }
5422 } else if is_getter {
5423 }
5425
5426 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5427 return Err(JSError::ParseError);
5428 }
5429 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
5432 return Err(JSError::ParseError);
5433 }
5434 tokens.remove(0); let body = parse_statements(tokens)?;
5437
5438 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
5439 return Err(JSError::ParseError);
5440 }
5441 tokens.remove(0); if is_getter {
5444 properties.push((key, Expr::Getter(Box::new(Expr::Function(params, body)))));
5445 } else {
5446 properties.push((key, Expr::Setter(Box::new(Expr::Function(params, body)))));
5447 }
5448 } else {
5449 if tokens.is_empty() || !matches!(tokens[0], Token::Colon) {
5451 return Err(JSError::ParseError);
5452 }
5453 tokens.remove(0); let value = parse_expression(tokens)?;
5457 properties.push((key, value));
5458 }
5459 }
5460
5461 if tokens.is_empty() {
5463 return Err(JSError::ParseError);
5464 }
5465 if matches!(tokens[0], Token::RBrace) {
5466 tokens.remove(0); break;
5468 } else if matches!(tokens[0], Token::Comma) {
5469 tokens.remove(0); } else {
5471 return Err(JSError::ParseError);
5472 }
5473 }
5474 Expr::Object(properties)
5475 }
5476 Token::LBracket => {
5477 let mut elements = Vec::new();
5479 if !tokens.is_empty() && matches!(tokens[0], Token::RBracket) {
5480 tokens.remove(0); return Ok(Expr::Array(elements));
5483 }
5484 loop {
5485 let elem = parse_expression(tokens)?;
5487 elements.push(elem);
5488
5489 if tokens.is_empty() {
5491 return Err(JSError::ParseError);
5492 }
5493 if matches!(tokens[0], Token::RBracket) {
5494 tokens.remove(0); break;
5496 } else if matches!(tokens[0], Token::Comma) {
5497 tokens.remove(0); } else {
5499 return Err(JSError::ParseError);
5500 }
5501 }
5502 Expr::Array(elements)
5503 }
5504 Token::Function => {
5505 if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5507 tokens.remove(0); let mut params = Vec::new();
5509 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
5510 loop {
5511 if let Some(Token::Identifier(param)) = tokens.first().cloned() {
5512 tokens.remove(0);
5513 params.push(param);
5514 if tokens.is_empty() {
5515 return Err(JSError::ParseError);
5516 }
5517 if matches!(tokens[0], Token::RParen) {
5518 break;
5519 }
5520 if !matches!(tokens[0], Token::Comma) {
5521 return Err(JSError::ParseError);
5522 }
5523 tokens.remove(0); } else {
5525 return Err(JSError::ParseError);
5526 }
5527 }
5528 }
5529 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5530 return Err(JSError::ParseError);
5531 }
5532 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
5534 return Err(JSError::ParseError);
5535 }
5536 tokens.remove(0); let body = parse_statements(tokens)?;
5538 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
5539 return Err(JSError::ParseError);
5540 }
5541 tokens.remove(0); Expr::Function(params, body)
5543 } else {
5544 return Err(JSError::ParseError);
5545 }
5546 }
5547 Token::Async => {
5548 if !tokens.is_empty() && matches!(tokens[0], Token::Function) {
5550 tokens.remove(0); if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5552 tokens.remove(0); let mut params = Vec::new();
5554 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
5555 loop {
5556 if let Some(Token::Identifier(param)) = tokens.first().cloned() {
5557 tokens.remove(0);
5558 params.push(param);
5559 if tokens.is_empty() {
5560 return Err(JSError::ParseError);
5561 }
5562 if matches!(tokens[0], Token::RParen) {
5563 break;
5564 }
5565 if !matches!(tokens[0], Token::Comma) {
5566 return Err(JSError::ParseError);
5567 }
5568 tokens.remove(0); } else {
5570 return Err(JSError::ParseError);
5571 }
5572 }
5573 }
5574 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5575 return Err(JSError::ParseError);
5576 }
5577 tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
5579 return Err(JSError::ParseError);
5580 }
5581 tokens.remove(0); let body = parse_statements(tokens)?;
5583 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
5584 return Err(JSError::ParseError);
5585 }
5586 tokens.remove(0); Expr::AsyncFunction(params, body)
5588 } else {
5589 return Err(JSError::ParseError);
5590 }
5591 } else if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5592 tokens.remove(0); let mut params = Vec::new();
5595 let mut is_arrow = false;
5596 if matches!(tokens.first(), Some(&Token::RParen)) {
5597 tokens.remove(0);
5598 if !tokens.is_empty() && matches!(tokens[0], Token::Arrow) {
5599 tokens.remove(0);
5600 is_arrow = true;
5601 } else {
5602 return Err(JSError::ParseError);
5603 }
5604 } else {
5605 let mut param_names = Vec::new();
5607 let mut local_consumed = Vec::new();
5608 let mut valid = true;
5609 loop {
5610 if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5611 tokens.remove(0);
5612 local_consumed.push(Token::Identifier(name.clone()));
5613 param_names.push(name);
5614 if tokens.is_empty() {
5615 valid = false;
5616 break;
5617 }
5618 if matches!(tokens[0], Token::RParen) {
5619 tokens.remove(0);
5620 local_consumed.push(Token::RParen);
5621 if !tokens.is_empty() && matches!(tokens[0], Token::Arrow) {
5622 tokens.remove(0);
5623 is_arrow = true;
5624 } else {
5625 valid = false;
5626 }
5627 break;
5628 } else if matches!(tokens[0], Token::Comma) {
5629 tokens.remove(0);
5630 local_consumed.push(Token::Comma);
5631 } else {
5632 valid = false;
5633 break;
5634 }
5635 } else {
5636 valid = false;
5637 break;
5638 }
5639 }
5640 if !valid || !is_arrow {
5641 for t in local_consumed.into_iter().rev() {
5643 tokens.insert(0, t);
5644 }
5645 return Err(JSError::ParseError);
5646 }
5647 params = param_names;
5648 }
5649 if is_arrow {
5650 Expr::ArrowFunction(params, parse_arrow_body(tokens)?)
5654 } else {
5655 return Err(JSError::ParseError);
5656 }
5657 } else {
5658 return Err(JSError::ParseError);
5659 }
5660 }
5661 Token::LParen => {
5662 let mut params = Vec::new();
5664 let mut is_arrow = false;
5665 let mut result_expr = None;
5666 if matches!(tokens.first(), Some(&Token::RParen)) {
5667 tokens.remove(0);
5668 if !tokens.is_empty() && matches!(tokens[0], Token::Arrow) {
5669 tokens.remove(0);
5670 is_arrow = true;
5671 } else {
5672 return Err(JSError::ParseError);
5673 }
5674 } else {
5675 let mut param_names = Vec::new();
5677 let mut local_consumed = Vec::new();
5678 let mut valid = true;
5679 loop {
5680 if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5681 tokens.remove(0);
5682 local_consumed.push(Token::Identifier(name.clone()));
5683 param_names.push(name);
5684 if tokens.is_empty() {
5685 valid = false;
5686 break;
5687 }
5688 if matches!(tokens[0], Token::RParen) {
5689 tokens.remove(0);
5690 local_consumed.push(Token::RParen);
5691 if !tokens.is_empty() && matches!(tokens[0], Token::Arrow) {
5692 tokens.remove(0);
5693 is_arrow = true;
5694 } else {
5695 valid = false;
5696 }
5697 break;
5698 } else if matches!(tokens[0], Token::Comma) {
5699 tokens.remove(0);
5700 local_consumed.push(Token::Comma);
5701 } else {
5702 valid = false;
5703 break;
5704 }
5705 } else {
5706 valid = false;
5707 break;
5708 }
5709 }
5710 if valid && is_arrow {
5711 params = param_names;
5712 } else {
5713 for t in local_consumed.into_iter().rev() {
5715 tokens.insert(0, t);
5716 }
5717 let expr_inner = parse_expression(tokens)?;
5719 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5720 return Err(JSError::ParseError);
5721 }
5722 tokens.remove(0);
5723 result_expr = Some(expr_inner);
5724 }
5725 }
5726 if is_arrow {
5727 Expr::ArrowFunction(params, parse_arrow_body(tokens)?)
5728 } else {
5729 result_expr.unwrap()
5730 }
5731 }
5732 _ => {
5733 return Err(JSError::EvaluationError {
5734 message: "error".to_string(),
5735 });
5736 }
5737 };
5738
5739 while !tokens.is_empty() {
5741 match &tokens[0] {
5742 Token::LBracket => {
5743 tokens.remove(0); let index_expr = parse_expression(tokens)?;
5745 if tokens.is_empty() || !matches!(tokens[0], Token::RBracket) {
5746 return Err(JSError::ParseError);
5747 }
5748 tokens.remove(0); expr = Expr::Index(Box::new(expr), Box::new(index_expr));
5750 }
5751 Token::Dot => {
5752 tokens.remove(0); if tokens.is_empty() {
5754 return Err(JSError::ParseError);
5755 }
5756 if let Some(prop) = tokens[0].as_identifier_string() {
5757 tokens.remove(0);
5758 expr = Expr::Property(Box::new(expr), prop);
5759 } else {
5760 return Err(JSError::ParseError);
5761 }
5762 }
5763 Token::OptionalChain => {
5764 tokens.remove(0); if tokens.is_empty() {
5766 return Err(JSError::ParseError);
5767 }
5768 if matches!(tokens[0], Token::LParen) {
5769 tokens.remove(0); let mut args = Vec::new();
5772 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
5773 loop {
5774 let arg = parse_expression(tokens)?;
5775 args.push(arg);
5776 if tokens.is_empty() {
5777 return Err(JSError::ParseError);
5778 }
5779 if matches!(tokens[0], Token::RParen) {
5780 break;
5781 }
5782 if !matches!(tokens[0], Token::Comma) {
5783 return Err(JSError::ParseError);
5784 }
5785 tokens.remove(0); }
5787 }
5788 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5789 return Err(JSError::ParseError);
5790 }
5791 tokens.remove(0); expr = Expr::OptionalCall(Box::new(expr), args);
5793 } else if matches!(tokens[0], Token::Identifier(_)) {
5794 if let Some(prop) = tokens[0].as_identifier_string() {
5796 tokens.remove(0);
5797 expr = Expr::OptionalProperty(Box::new(expr), prop);
5798 } else {
5799 return Err(JSError::ParseError);
5800 }
5801 } else {
5802 return Err(JSError::ParseError);
5803 }
5804 }
5805 Token::LParen => {
5806 tokens.remove(0); let mut args = Vec::new();
5808 if !tokens.is_empty() && !matches!(tokens[0], Token::RParen) {
5809 loop {
5810 let arg = parse_expression(tokens)?;
5811 args.push(arg);
5812 if tokens.is_empty() {
5813 return Err(JSError::ParseError);
5814 }
5815 if matches!(tokens[0], Token::RParen) {
5816 break;
5817 }
5818 if !matches!(tokens[0], Token::Comma) {
5819 return Err(JSError::ParseError);
5820 }
5821 tokens.remove(0); }
5823 }
5824 if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5825 return Err(JSError::ParseError);
5826 }
5827 tokens.remove(0); expr = Expr::Call(Box::new(expr), args);
5829 }
5830 Token::Increment => {
5831 tokens.remove(0);
5832 expr = Expr::PostIncrement(Box::new(expr));
5833 }
5834 Token::Decrement => {
5835 tokens.remove(0);
5836 expr = Expr::PostDecrement(Box::new(expr));
5837 }
5838 _ => break,
5839 }
5840 }
5841
5842 Ok(expr)
5843}
5844
5845fn parse_arrow_body(tokens: &mut Vec<Token>) -> Result<Vec<Statement>, JSError> {
5846 if !tokens.is_empty() && matches!(tokens[0], Token::LBrace) {
5847 tokens.remove(0);
5848 let body = parse_statements(tokens)?;
5849 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
5850 return Err(JSError::ParseError);
5851 }
5852 tokens.remove(0);
5853 Ok(body)
5854 } else {
5855 let expr = parse_expression(tokens)?;
5856 Ok(vec![Statement::Return(Some(expr))])
5857 }
5858}
5859
5860fn parse_array_destructuring_pattern(tokens: &mut Vec<Token>) -> Result<Vec<DestructuringElement>, JSError> {
5861 if tokens.is_empty() || !matches!(tokens[0], Token::LBracket) {
5862 return Err(JSError::ParseError);
5863 }
5864 tokens.remove(0); let mut pattern = Vec::new();
5867 if !tokens.is_empty() && matches!(tokens[0], Token::RBracket) {
5868 tokens.remove(0); return Ok(pattern);
5870 }
5871
5872 loop {
5873 if !tokens.is_empty() && matches!(tokens[0], Token::Spread) {
5874 tokens.remove(0); if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5876 tokens.remove(0);
5877 pattern.push(DestructuringElement::Rest(name));
5878 } else {
5879 return Err(JSError::ParseError);
5880 }
5881 if tokens.is_empty() || !matches!(tokens[0], Token::RBracket) {
5883 return Err(JSError::ParseError);
5884 }
5885 tokens.remove(0); break;
5887 } else if !tokens.is_empty() && matches!(tokens[0], Token::Comma) {
5888 tokens.remove(0); pattern.push(DestructuringElement::Empty);
5890 } else if !tokens.is_empty() && matches!(tokens[0], Token::LBracket) {
5891 let nested_pattern = parse_array_destructuring_pattern(tokens)?;
5893 pattern.push(DestructuringElement::NestedArray(nested_pattern));
5894 } else if !tokens.is_empty() && matches!(tokens[0], Token::LBrace) {
5895 let nested_pattern = parse_object_destructuring_pattern(tokens)?;
5897 pattern.push(DestructuringElement::NestedObject(nested_pattern));
5898 } else if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5899 tokens.remove(0);
5900 pattern.push(DestructuringElement::Variable(name));
5901 } else {
5902 return Err(JSError::ParseError);
5903 }
5904
5905 if tokens.is_empty() {
5906 return Err(JSError::ParseError);
5907 }
5908 if matches!(tokens[0], Token::RBracket) {
5909 tokens.remove(0); break;
5911 } else if matches!(tokens[0], Token::Comma) {
5912 tokens.remove(0); } else {
5914 return Err(JSError::ParseError);
5915 }
5916 }
5917
5918 Ok(pattern)
5919}
5920
5921fn parse_object_destructuring_pattern(tokens: &mut Vec<Token>) -> Result<Vec<ObjectDestructuringElement>, JSError> {
5922 if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
5923 return Err(JSError::ParseError);
5924 }
5925 tokens.remove(0); let mut pattern = Vec::new();
5928 if !tokens.is_empty() && matches!(tokens[0], Token::RBrace) {
5929 tokens.remove(0); return Ok(pattern);
5931 }
5932
5933 loop {
5934 if !tokens.is_empty() && matches!(tokens[0], Token::Spread) {
5935 tokens.remove(0); if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5937 tokens.remove(0);
5938 pattern.push(ObjectDestructuringElement::Rest(name));
5939 } else {
5940 return Err(JSError::ParseError);
5941 }
5942 if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
5944 return Err(JSError::ParseError);
5945 }
5946 tokens.remove(0); break;
5948 } else {
5949 let key = if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5951 tokens.remove(0);
5952 name
5953 } else {
5954 return Err(JSError::ParseError);
5955 };
5956
5957 let value = if !tokens.is_empty() && matches!(tokens[0], Token::Colon) {
5958 tokens.remove(0); if !tokens.is_empty() && matches!(tokens[0], Token::LBracket) {
5961 DestructuringElement::NestedArray(parse_array_destructuring_pattern(tokens)?)
5962 } else if !tokens.is_empty() && matches!(tokens[0], Token::LBrace) {
5963 DestructuringElement::NestedObject(parse_object_destructuring_pattern(tokens)?)
5964 } else if let Some(Token::Identifier(name)) = tokens.first().cloned() {
5965 tokens.remove(0);
5966 DestructuringElement::Variable(name)
5967 } else {
5968 return Err(JSError::ParseError);
5969 }
5970 } else {
5971 DestructuringElement::Variable(key.clone())
5973 };
5974
5975 pattern.push(ObjectDestructuringElement::Property { key, value });
5976 }
5977
5978 if tokens.is_empty() {
5979 return Err(JSError::ParseError);
5980 }
5981 if matches!(tokens[0], Token::RBrace) {
5982 tokens.remove(0); break;
5984 } else if matches!(tokens[0], Token::Comma) {
5985 tokens.remove(0); } else {
5987 return Err(JSError::ParseError);
5988 }
5989 }
5990
5991 Ok(pattern)
5992}
5993
5994pub unsafe fn JS_GetProperty(_ctx: *mut JSContext, this_obj: JSValue, prop: JSAtom) -> JSValue {
5997 unsafe {
5998 if this_obj.tag != JS_TAG_OBJECT as i64 {
5999 return JS_UNDEFINED;
6000 }
6001 let p = this_obj.u.ptr as *mut JSObject;
6002 let sh = (*p).shape;
6003 if let Some((idx, _)) = (*sh).find_own_property(prop) {
6004 let prop_val = (*(*p).prop.offset(idx as isize)).u.value;
6005 if prop_val.has_ref_count() {
6007 JS_DupValue((*_ctx).rt, prop_val);
6008 }
6009 prop_val
6010 } else {
6011 JS_UNDEFINED
6012 }
6013 }
6014}
6015
6016pub unsafe fn JS_DupValue(_rt: *mut JSRuntime, v: JSValue) {
6022 unsafe {
6023 if v.has_ref_count() {
6024 let p = v.get_ptr();
6025 if !p.is_null() {
6026 let header = p as *mut JSRefCountHeader;
6027 (*header).ref_count += 1;
6028 }
6029 }
6030 }
6031}
6032
6033pub unsafe fn JS_FreeValue(rt: *mut JSRuntime, v: JSValue) {
6036 unsafe {
6037 if v.has_ref_count() {
6038 let p = v.get_ptr();
6039 if p.is_null() {
6040 return;
6041 }
6042 let header = p as *mut JSRefCountHeader;
6043 (*header).ref_count -= 1;
6044 if (*header).ref_count > 0 {
6045 return;
6046 }
6047 match v.get_tag() {
6049 x if x == JS_TAG_STRING => {
6050 js_free_string(rt, v);
6051 }
6052 x if x == JS_TAG_OBJECT => {
6053 js_free_object(rt, v);
6054 }
6055 x if x == JS_TAG_FUNCTION_BYTECODE => {
6056 js_free_function_bytecode(rt, v);
6057 }
6058 x if x == JS_TAG_SYMBOL => {
6059 js_free_symbol(rt, v);
6060 }
6061 x if x == JS_TAG_BIG_INT => {
6062 js_free_bigint(rt, v);
6063 }
6064 x if x == JS_TAG_MODULE => {
6065 js_free_module(rt, v);
6066 }
6067 _ => {
6069 (*rt).js_free_rt(p);
6070 }
6071 }
6072 }
6073 }
6074}
6075
6076unsafe fn js_free_string(rt: *mut JSRuntime, v: JSValue) {
6077 unsafe {
6078 let p = v.get_ptr() as *mut JSString;
6079 if p.is_null() {
6080 return;
6081 }
6082 (*rt).js_free_rt(p as *mut c_void);
6084 }
6085}
6086
6087unsafe fn js_free_object(rt: *mut JSRuntime, v: JSValue) {
6088 unsafe {
6089 let p = v.get_ptr() as *mut JSObject;
6090 if p.is_null() {
6091 return;
6092 }
6093 if !(*p).prop.is_null() {
6095 (*rt).js_free_rt((*p).prop as *mut c_void);
6096 (*p).prop = std::ptr::null_mut();
6097 }
6098 if !(*p).shape.is_null() {
6100 (*rt).js_free_shape((*p).shape);
6101 (*p).shape = std::ptr::null_mut();
6102 }
6103 (*rt).js_free_rt(p as *mut c_void);
6105 }
6106}
6107
6108unsafe fn js_free_function_bytecode(rt: *mut JSRuntime, v: JSValue) {
6109 unsafe {
6110 let p = v.get_ptr() as *mut JSFunctionBytecode;
6111 if p.is_null() {
6112 return;
6113 }
6114 if !(*p).byte_code_buf.is_null() {
6116 (*rt).js_free_rt((*p).byte_code_buf as *mut c_void);
6117 (*p).byte_code_buf = std::ptr::null_mut();
6118 }
6119 if !(*p).pc2line_buf.is_null() {
6121 (*rt).js_free_rt((*p).pc2line_buf as *mut c_void);
6122 (*p).pc2line_buf = std::ptr::null_mut();
6123 }
6124 if !(*p).source.is_null() {
6126 (*rt).js_free_rt((*p).source as *mut c_void);
6127 (*p).source = std::ptr::null_mut();
6128 }
6129 if !(*p).cpool.is_null() && (*p).cpool_count > 0 {
6131 for i in 0..(*p).cpool_count as isize {
6132 let val = *(*p).cpool.offset(i);
6133 if val.has_ref_count() {
6134 JS_FreeValue(rt, val);
6135 }
6136 }
6137 (*rt).js_free_rt((*p).cpool as *mut c_void);
6138 (*p).cpool = std::ptr::null_mut();
6139 }
6140 (*rt).js_free_rt(p as *mut c_void);
6142 }
6143}
6144
6145unsafe fn js_free_symbol(rt: *mut JSRuntime, v: JSValue) {
6146 let p = v.get_ptr();
6147 if p.is_null() {
6148 return;
6149 }
6150 unsafe { (*rt).js_free_rt(p) };
6153}
6154
6155unsafe fn js_free_bigint(rt: *mut JSRuntime, v: JSValue) {
6156 let p = v.get_ptr();
6157 if p.is_null() {
6158 return;
6159 }
6160 unsafe { (*rt).js_free_rt(p) };
6162}
6163
6164unsafe fn js_free_module(rt: *mut JSRuntime, v: JSValue) {
6165 let p = v.get_ptr();
6166 if p.is_null() {
6167 return;
6168 }
6169 unsafe { (*rt).js_free_rt(p) };
6171}
6172
6173pub unsafe fn JS_SetProperty(ctx: *mut JSContext, this_obj: JSValue, prop: JSAtom, val: JSValue) -> i32 {
6176 unsafe { JS_DefinePropertyValue(ctx, this_obj, prop, val, 0) }
6177}
6178
6179impl JSRuntime {
6180 pub unsafe fn js_new_atom_len(&mut self, name: *const u8, len: usize) -> JSAtom {
6183 if len == 0 {
6184 return 0; }
6186 let mut h = 0u32;
6188 for i in 0..len {
6189 h = h.wrapping_mul(31).wrapping_add(unsafe { *name.add(i) } as u32);
6190 }
6191 let hash_index = (h % self.atom_hash_size as u32) as i32;
6193 let mut atom = unsafe { *self.atom_hash.offset(hash_index as isize) };
6194 while atom != 0 {
6195 let p = unsafe { *self.atom_array.offset((atom - 1) as isize) };
6196 if unsafe { (*p).len == len as u32 && (*p).hash == h } {
6197 let str_data = unsafe { (p as *mut u8).add(std::mem::size_of::<JSString>()) };
6199 let mut equal = true;
6200 for i in 0..len {
6201 if unsafe { *str_data.add(i) != *name.add(i) } {
6202 equal = false;
6203 break;
6204 }
6205 }
6206 if equal {
6207 return atom;
6208 }
6209 }
6210 atom = unsafe { (*p).hash_next };
6211 }
6212 if self.atom_count >= self.atom_size {
6214 let new_size = self.atom_size * 2;
6215 let new_array = unsafe {
6216 self.js_realloc_rt(
6217 self.atom_array as *mut c_void,
6218 (new_size as usize) * std::mem::size_of::<*mut JSAtomStruct>(),
6219 )
6220 } as *mut *mut JSAtomStruct;
6221 if new_array.is_null() {
6222 return 0;
6223 }
6224 self.atom_array = new_array;
6225 self.atom_size = new_size;
6226 for i in self.atom_count..new_size {
6227 unsafe { *self.atom_array.offset(i as isize) = std::ptr::null_mut() };
6228 }
6229 }
6230 let str_size = std::mem::size_of::<JSString>() + len;
6232 let p = unsafe { self.js_malloc_rt(str_size) } as *mut JSString;
6233 if p.is_null() {
6234 return 0;
6235 }
6236 unsafe { (*p).header.ref_count = 1 };
6237 unsafe { (*p).len = len as u32 };
6238 unsafe { (*p).hash = h };
6239 unsafe { (*p).hash_next = *self.atom_hash.offset(hash_index as isize) };
6240 let str_data = unsafe { (p as *mut u8).add(std::mem::size_of::<JSString>()) };
6242 for i in 0..len {
6243 unsafe { *str_data.add(i) = *name.add(i) };
6244 }
6245 let new_atom = (self.atom_count + 1) as u32;
6246 unsafe { *self.atom_array.offset(self.atom_count as isize) = p };
6247 unsafe { *self.atom_hash.offset(hash_index as isize) = new_atom };
6248 self.atom_count += 1;
6249 new_atom
6250 }
6251}
6252
6253fn filter_input_script(script: &str) -> String {
6254 let mut filtered = String::new();
6256 let chars: Vec<char> = script.chars().collect();
6257 let mut i = 0;
6258 let mut in_single = false;
6259 let mut in_double = false;
6260 let mut in_backtick = false;
6261 let mut escape = false;
6262
6263 while i < chars.len() {
6264 let ch = chars[i];
6265
6266 if escape {
6268 filtered.push(ch);
6269 escape = false;
6270 i += 1;
6271 continue;
6272 }
6273 if ch == '\\' {
6274 escape = true;
6275 filtered.push(ch);
6276 i += 1;
6277 continue;
6278 }
6279
6280 match ch {
6282 '\'' if !in_double && !in_backtick => {
6283 in_single = !in_single;
6284 filtered.push(ch);
6285 i += 1;
6286 continue;
6287 }
6288 '"' if !in_single && !in_backtick => {
6289 in_double = !in_double;
6290 filtered.push(ch);
6291 i += 1;
6292 continue;
6293 }
6294 '`' if !in_single && !in_double => {
6295 in_backtick = !in_backtick;
6296 filtered.push(ch);
6297 i += 1;
6298 continue;
6299 }
6300 _ => {}
6301 }
6302
6303 if !in_single && !in_double && !in_backtick {
6305 if i + 1 < chars.len() && ch == '/' && chars[i + 1] == '/' {
6307 while i < chars.len() && chars[i] != '\n' {
6309 i += 1;
6310 }
6311 continue;
6313 }
6314
6315 if i + 1 < chars.len() && ch == '/' && chars[i + 1] == '*' {
6317 i += 2; while i + 1 < chars.len() {
6319 if chars[i] == '*' && chars[i + 1] == '/' {
6320 i += 2; break;
6322 }
6323 i += 1;
6324 }
6325 continue;
6326 }
6327 }
6328
6329 filtered.push(ch);
6331 i += 1;
6332 }
6333
6334 let mut final_filtered = String::new();
6336 for (i, line) in filtered.lines().enumerate() {
6337 let mut current = String::new();
6339 let mut in_single = false;
6340 let mut in_double = false;
6341 let mut in_backtick = false;
6342 let mut escape = false;
6343 let mut parts: Vec<(String, bool)> = Vec::new();
6345 for ch in line.chars() {
6346 if escape {
6347 current.push(ch);
6348 escape = false;
6349 continue;
6350 }
6351 if ch == '\\' {
6352 escape = true;
6353 current.push(ch);
6354 continue;
6355 }
6356 match ch {
6357 '\'' if !in_double && !in_backtick => {
6358 in_single = !in_single;
6359 current.push(ch);
6360 continue;
6361 }
6362 '"' if !in_single && !in_backtick => {
6363 in_double = !in_double;
6364 current.push(ch);
6365 continue;
6366 }
6367 '`' if !in_single && !in_double => {
6368 in_backtick = !in_backtick;
6369 current.push(ch);
6370 continue;
6371 }
6372 _ => {}
6373 }
6374 if ch == ';' && !in_single && !in_double && !in_backtick {
6375 parts.push((current.clone(), true));
6376 current.clear();
6377 continue;
6378 }
6379 current.push(ch);
6380 }
6381 if !current.is_empty() {
6383 parts.push((current, false));
6384 }
6385
6386 for (part, had_semicolon) in parts.iter() {
6387 let p = part.trim();
6388 if p.is_empty() {
6389 continue;
6390 }
6391 log::trace!("script part[{i}]='{p}'");
6392 if p.starts_with("import * as") && p.contains("from") {
6393 log::debug!("skipping import part[{i}]: \"{p}\"");
6394 continue;
6395 }
6396 final_filtered.push_str(p);
6397 if *had_semicolon {
6399 final_filtered.push(';');
6400 }
6401 }
6402 final_filtered.push('\n');
6403 }
6404
6405 final_filtered.trim().to_string()
6408}
6409
6410fn initialize_global_constructors(env: &JSObjectDataPtr) {
6412 let mut env_borrow = env.borrow_mut();
6413
6414 env_borrow.insert("Object".to_string(), Rc::new(RefCell::new(Value::Function("Object".to_string()))));
6416
6417 env_borrow.insert("Boolean".to_string(), Rc::new(RefCell::new(Value::Function("Boolean".to_string()))));
6422
6423 env_borrow.insert("String".to_string(), Rc::new(RefCell::new(Value::Function("String".to_string()))));
6425
6426 env_borrow.insert("Array".to_string(), Rc::new(RefCell::new(Value::Function("Array".to_string()))));
6428
6429 env_borrow.insert("Date".to_string(), Rc::new(RefCell::new(Value::Function("Date".to_string()))));
6431
6432 env_borrow.insert("RegExp".to_string(), Rc::new(RefCell::new(Value::Function("RegExp".to_string()))));
6434
6435 env_borrow.insert(
6437 "__internal_resolve_promise".to_string(),
6438 Rc::new(RefCell::new(Value::Function("__internal_resolve_promise".to_string()))),
6439 );
6440 env_borrow.insert(
6441 "__internal_reject_promise".to_string(),
6442 Rc::new(RefCell::new(Value::Function("__internal_reject_promise".to_string()))),
6443 );
6444 env_borrow.insert(
6445 "__internal_allsettled_state_record_fulfilled".to_string(),
6446 Rc::new(RefCell::new(Value::Function(
6447 "__internal_allsettled_state_record_fulfilled".to_string(),
6448 ))),
6449 );
6450 env_borrow.insert(
6451 "__internal_allsettled_state_record_rejected".to_string(),
6452 Rc::new(RefCell::new(Value::Function(
6453 "__internal_allsettled_state_record_rejected".to_string(),
6454 ))),
6455 );
6456}
6457
6458fn expand_spread_in_call_args(env: &JSObjectDataPtr, args: &[Expr], evaluated_args: &mut Vec<Value>) -> Result<(), JSError> {
6460 for arg_expr in args {
6461 if let Expr::Spread(spread_expr) = arg_expr {
6462 let spread_val = evaluate_expr(env, spread_expr)?;
6463 if let Value::Object(spread_obj) = spread_val {
6464 let mut i = 0;
6466 loop {
6467 let key = i.to_string();
6468 if let Some(val) = obj_get_value(&spread_obj, &key)? {
6469 evaluated_args.push(val.borrow().clone());
6470 i += 1;
6471 } else {
6472 break;
6473 }
6474 }
6475 } else {
6476 return Err(JSError::EvaluationError {
6477 message: "Spread operator can only be applied to arrays in function calls".to_string(),
6478 });
6479 }
6480 } else {
6481 let arg_val = evaluate_expr(env, arg_expr)?;
6482 evaluated_args.push(arg_val);
6483 }
6484 }
6485 Ok(())
6486}
6487
6488fn handle_optional_method_call(
6490 obj_map: &JSObjectDataPtr,
6491 method: &str,
6492 args: &[Expr],
6493 env: &JSObjectDataPtr,
6494 obj_expr: &Expr,
6495) -> Result<Value, JSError> {
6496 match method {
6497 "log" if obj_map.borrow().contains_key("log") => js_console::handle_console_method(method, args, env),
6498 "toString" => crate::js_object::handle_to_string_method(&Value::Object(obj_map.clone()), args),
6499 "valueOf" => crate::js_object::handle_value_of_method(&Value::Object(obj_map.clone()), args),
6500 method => {
6501 if obj_map.borrow().contains_key("sprintf") {
6503 match method {
6504 "sprintf" => {
6505 log::trace!("js dispatch calling sprintf with {} args", args.len());
6506 sprintf::handle_sprintf_call(env, args)
6507 }
6508 "tmpfile" => tmpfile::create_tmpfile(),
6509 _ => Ok(Value::Undefined),
6510 }
6511 } else if obj_map.borrow().contains_key("open") {
6512 crate::js_os::handle_os_method(obj_map, method, args, env)
6514 } else if obj_map.borrow().contains_key("join") {
6515 crate::js_os::handle_os_method(obj_map, method, args, env)
6517 } else if obj_map.borrow().contains_key("__file_id") {
6518 tmpfile::handle_file_method(obj_map, method, args, env)
6520 } else if obj_map.borrow().contains_key("PI") && obj_map.borrow().contains_key("E") {
6521 js_math::handle_math_method(method, args, env)
6523 } else if obj_map.borrow().contains_key("parse") && obj_map.borrow().contains_key("stringify") {
6524 crate::js_json::handle_json_method(method, args, env)
6525 } else if obj_map.borrow().contains_key("keys") && obj_map.borrow().contains_key("values") {
6526 crate::js_object::handle_object_method(method, args, env)
6527 } else if obj_map.borrow().contains_key("__timestamp") {
6528 crate::js_date::handle_date_method(obj_map, method, args)
6530 } else if obj_map.borrow().contains_key("__regex") {
6531 crate::js_regexp::handle_regexp_method(obj_map, method, args, env)
6533 } else if is_array(obj_map) {
6534 crate::js_array::handle_array_instance_method(obj_map, method, args, env, obj_expr)
6536 } else if obj_map.borrow().contains_key("__class_def__") {
6537 call_static_method(obj_map, method, args, env)
6539 } else if is_class_instance(obj_map)? {
6540 call_class_method(obj_map, method, args, env)
6541 } else {
6542 if let Some(prop_val) = obj_get_value(obj_map, method)? {
6544 match prop_val.borrow().clone() {
6545 Value::Closure(params, body, captured_env) => {
6546 let mut evaluated_args = Vec::new();
6549 expand_spread_in_call_args(env, args, &mut evaluated_args)?;
6550 let func_env = captured_env.clone();
6552 for (i, param) in params.iter().enumerate() {
6554 if i < evaluated_args.len() {
6555 env_set(&func_env, param.as_str(), evaluated_args[i].clone())?;
6556 } else {
6557 env_set(&func_env, param.as_str(), Value::Undefined)?;
6558 }
6559 }
6560 evaluate_statements(&func_env, &body)
6562 }
6563 Value::Function(func_name) => crate::js_function::handle_global_function(&func_name, args, env),
6564 _ => Err(JSError::EvaluationError {
6565 message: format!("Property '{}' is not a function", method),
6566 }),
6567 }
6568 } else {
6569 Ok(Value::Undefined)
6570 }
6571 }
6572 }
6573 }
6574}