javascript/
core.rs

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    /// # Safety
140    /// The caller must ensure that the list_head is properly initialized and not concurrently accessed.
141    pub unsafe fn init(&mut self) {
142        self.prev = self;
143        self.next = self;
144    }
145
146    /// # Safety
147    /// The caller must ensure that `new_entry` is a valid pointer to an uninitialized list_head,
148    /// and that the list is not concurrently modified.
149    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    /// # Safety
160    /// The caller must ensure that the list_head is part of a valid linked list and not concurrently accessed.
161    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,  // len: 31, is_wide_char: 1 (packed manually)
200    pub hash: u32, // hash: 30, atom_type: 2 (packed manually)
201    pub hash_next: u32,
202    // Variable length data follows
203}
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, // JSClassFinalizer
212    pub gc_mark: *mut c_void,   // JSClassGCMark
213    pub call: *mut c_void,      // JSClassCall
214    pub exotic: *mut c_void,    // JSClassExoticMethods
215}
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, // 4 bits
253    pub mark: u8,        // 1 bit
254    pub dummy0: u8,      // 3 bits
255    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], // JS_NATIVE_ERROR_COUNT = 8 (usually)
298    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, // Packed bitfields
322    pub byte_code_buf: *mut u8,
323    pub byte_code_len: i32,
324    pub func_name: JSAtom,
325    pub vardefs: *mut c_void,     // JSBytecodeVarDef
326    pub closure_var: *mut c_void, // JSClosureVar
327    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    // debug info
337    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, // JSVarRef
351    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, // simplified for now
381}
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    /// # Safety
402    /// The caller must ensure that the JSShape and its property arrays are valid and not concurrently modified.
403    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    /// # Safety
432    /// The caller must ensure that `sh` is a valid pointer to a JSShape that is not concurrently accessed,
433    /// and that the runtime's memory allocation functions are properly set up.
434    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    /// # Safety
451    /// The caller must ensure that `sh` is a valid pointer to a JSShape that is not concurrently accessed,
452    /// and that the runtime's memory allocation functions are properly set up.
453    pub unsafe fn add_property(&mut self, sh: *mut JSShape, atom: JSAtom, flags: u8) -> i32 {
454        unsafe {
455            // Check if property already exists
456            if let Some((idx, _)) = (*sh).find_own_property(atom) {
457                // Already exists
458                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            // Enable hash if needed
469            if (*sh).prop_count >= 4 && (*sh).is_hashed == 0 {
470                (*sh).is_hashed = 1;
471                (*sh).prop_hash_mask = 15; // 16 - 1
472                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                // Fill hash table with existing properties
481                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    /// # Safety
507    /// The caller must ensure that the runtime's memory allocation functions are properly set up,
508    /// and that `ptr` is either null or a valid pointer previously returned by the allocator.
509    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    /// # Safety
520    /// The caller must ensure that the runtime's memory allocation functions are properly set up.
521    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    /// # Safety
532    /// The caller must ensure that `ptr` is either null or a valid pointer previously returned by the allocator,
533    /// and that the runtime's memory allocation functions are properly set up.
534    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    /// # Safety
543    /// The caller must ensure that the runtime's memory allocation functions are properly set up
544    /// and that the runtime is not concurrently accessed.
545    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    /// # Safety
573    /// The caller must ensure that `proto` is either null or a valid pointer to a JSObject,
574    /// and that the runtime's memory allocation functions are properly set up.
575    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; // JS_GC_OBJ_TYPE_SHAPE
583            (*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    /// # Safety
603    /// The caller must ensure that `sh` is either null or a valid pointer to a JSShape previously allocated by this runtime,
604    /// and that the runtime's memory allocation functions are properly set up.
605    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
620/// # Safety
621/// The caller must ensure that `ctx` and `this_obj` are valid pointers, and that the runtime is properly initialized.
622pub 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; // TypeError
625    }
626    let p = unsafe { this_obj.u.ptr } as *mut JSObject;
627    let sh = unsafe { (*p).shape };
628
629    // Add property to shape
630    // Note: In real QuickJS, we might need to clone shape if it is shared
631    // For now, assume shape is unique to object or we modify it in place (dangerous if shared)
632
633    let idx = unsafe { (*(*ctx).rt).add_property(sh, prop, flags as u8) };
634    if idx < 0 {
635        return -1;
636    }
637
638    // Resize object prop array if needed
639    // JSObject prop array stores JSProperty (values)
640    // JSShape prop array stores JSShapeProperty (names/flags)
641    // They must match in size/index
642
643    // TODO: Resize object prop array
644    // For now, let's assume we have enough space or implement resize logic for object prop
645
646    // Actually, we need to implement object prop resizing here
647    // But JSObject definition: pub prop: *mut JSProperty
648    // We don't store prop_size in JSObject?
649    // QuickJS stores it in JSShape? No.
650    // QuickJS: JSObject has no size field. It relies on Shape?
651    // Ah, JSObject allocates prop array based on shape->prop_size?
652    // Or maybe it reallocates when shape grows?
653
654    // Let's look at QuickJS:
655    // JS_DefinePropertyValue -> JS_DefineProperty -> add_property
656    // add_property modifies shape.
657    // If shape grows, we need to grow object's prop array too?
658    // Yes, but how do we know the current size of object's prop array?
659    // It seems we assume it matches shape's prop_count or prop_size?
660
661    // Let's implement a simple resize for object prop
662    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 the prop array was just created, zero-initialize it to avoid reading
675    // uninitialized JSProperty values later.
676    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    // Set value
682    let pr = unsafe { (*p).prop.offset(idx as isize) };
683    // If replacing an existing value, free it
684    let old_val = unsafe { (*pr).u.value };
685    if old_val.has_ref_count() {
686        unsafe { JS_FreeValue((*ctx).rt, old_val) };
687    }
688    // Duplicate incoming value if it's ref-counted
689    if val.has_ref_count() {
690        unsafe { JS_DupValue((*ctx).rt, val) };
691    }
692    unsafe { (*pr).u.value = val };
693
694    1
695}
696
697/// # Safety
698/// This function initializes a new JavaScript runtime with default memory allocation functions.
699/// The caller must ensure that the returned runtime is properly freed with JS_FreeRuntime.
700pub 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        // Initialize malloc functions
718        (*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        // Initialize atoms
733        (*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
765/// # Safety
766/// The caller must ensure that `rt` is either null or a valid pointer to a JSRuntime previously created by JS_NewRuntime,
767/// and that no contexts or objects from this runtime are still in use.
768pub unsafe fn JS_FreeRuntime(rt: *mut JSRuntime) {
769    if !rt.is_null() {
770        // Free allocated resources
771        // For now, just free the rt
772        unsafe { libc::free(rt as *mut c_void) };
773    }
774}
775
776/// # Safety
777/// The caller must ensure that `rt` is a valid pointer to a JSRuntime, and that the context is properly freed with JS_FreeContext.
778pub 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        // Initialize other fields to zero/null
794        (*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
828/// # Safety
829/// The caller must ensure that `ctx` is either null or a valid pointer to a JSContext previously created by JS_NewContext,
830/// and that no objects from this context are still in use.
831pub 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
837/// # Safety
838/// The caller must ensure that `ctx` is a valid pointer to a JSContext.
839pub 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
863/// # Safety
864/// The caller must ensure that `ctx` is a valid pointer to a JSContext.
865pub 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            // Empty string
871            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; // TODO: compute hash
881        (*p).hash_next = 0;
882        // Copy string data
883        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
891/// # Safety
892/// The caller must ensure that `ctx` is a valid pointer to a JSContext, and that `input` points to valid UTF-8 data of length `input_len`.
893pub 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        // Evaluate statements
902        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,          // For now
914            Ok(Value::Function(_)) => JS_UNDEFINED,        // For now
915            Ok(Value::Closure(_, _, _)) => JS_UNDEFINED,   // For now
916            Ok(Value::ClassDefinition(_)) => JS_UNDEFINED, // For now
917            Ok(Value::Getter(_, _)) => JS_UNDEFINED,       // For now
918            Ok(Value::Setter(_, _, _)) => JS_UNDEFINED,    // For now
919            Ok(Value::Property { .. }) => JS_UNDEFINED,    // For now
920            Ok(Value::Promise(_)) => JS_UNDEFINED,         // For now
921            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    // Inject simple host `std` / `os` shims when importing with the pattern:
953    //   import * as NAME from "std";
954    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 built-in constructors
978    initialize_global_constructors(&env);
979
980    match evaluate_statements(&env, &statements) {
981        Ok(v) => {
982            // If the result is a Promise object (wrapped in Object with __promise property), wait for it to resolve
983            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                // Run the event loop until the promise is resolved
988                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                            // Continue running the event loop
1000                        }
1001                    }
1002                }
1003            }
1004            // Run the event loop to process any queued asynchronous tasks
1005            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); // consume break
1030        if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1031            return Err(JSError::ParseError);
1032        }
1033        tokens.remove(0); // consume ;
1034        return Ok(Statement::Break);
1035    }
1036    if !tokens.is_empty() && matches!(tokens[0], Token::Continue) {
1037        tokens.remove(0); // consume continue
1038        if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1039            return Err(JSError::ParseError);
1040        }
1041        tokens.remove(0); // consume ;
1042        return Ok(Statement::Continue);
1043    }
1044    if !tokens.is_empty() && matches!(tokens[0], Token::While) {
1045        tokens.remove(0); // consume while
1046        if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1047            return Err(JSError::ParseError);
1048        }
1049        tokens.remove(0); // consume (
1050        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); // consume )
1055        if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1056            return Err(JSError::ParseError);
1057        }
1058        tokens.remove(0); // consume {
1059        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); // consume }
1064        return Ok(Statement::While(condition, body));
1065    }
1066    if !tokens.is_empty() && matches!(tokens[0], Token::Do) {
1067        tokens.remove(0); // consume do
1068        if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1069            return Err(JSError::ParseError);
1070        }
1071        tokens.remove(0); // consume {
1072        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); // consume }
1077        if tokens.is_empty() || !matches!(tokens[0], Token::While) {
1078            return Err(JSError::ParseError);
1079        }
1080        tokens.remove(0); // consume while
1081        if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1082            return Err(JSError::ParseError);
1083        }
1084        tokens.remove(0); // consume (
1085        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); // consume )
1090        if tokens.is_empty() || !matches!(tokens[0], Token::Semicolon) {
1091            return Err(JSError::ParseError);
1092        }
1093        tokens.remove(0); // consume ;
1094        return Ok(Statement::DoWhile(body, condition));
1095    }
1096    if !tokens.is_empty() && matches!(tokens[0], Token::Switch) {
1097        tokens.remove(0); // consume switch
1098        if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1099            return Err(JSError::ParseError);
1100        }
1101        tokens.remove(0); // consume (
1102        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); // consume )
1107        if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1108            return Err(JSError::ParseError);
1109        }
1110        tokens.remove(0); // consume {
1111        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); // consume case
1115                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); // consume :
1120                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); // consume default
1135                if tokens.is_empty() || !matches!(tokens[0], Token::Colon) {
1136                    return Err(JSError::ParseError);
1137                }
1138                tokens.remove(0); // consume :
1139                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); // consume }
1156        return Ok(Statement::Switch(expr, cases));
1157    }
1158    if !tokens.is_empty() && matches!(tokens[0], Token::Throw) {
1159        tokens.remove(0); // consume throw
1160        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); // consume ;
1165        return Ok(Statement::Throw(expr));
1166    }
1167    if !tokens.is_empty() && matches!(tokens[0], Token::Async) {
1168        tokens.remove(0); // consume async
1169        if !tokens.is_empty() && matches!(tokens[0], Token::Function) {
1170            tokens.remove(0); // consume function
1171            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); // consume (
1175                    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); // consume ,
1191                            } 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); // consume )
1200                    if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1201                        return Err(JSError::ParseError);
1202                    }
1203                    tokens.remove(0); // consume {
1204                    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); // consume }
1209                    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); // consume function
1217        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); // consume (
1221                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); // consume ,
1237                        } 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); // consume )
1246                if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1247                    return Err(JSError::ParseError);
1248                }
1249                tokens.remove(0); // consume {
1250                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); // consume }
1255                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); // consume if
1261        if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1262            return Err(JSError::ParseError);
1263        }
1264        tokens.remove(0); // consume (
1265        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); // consume )
1270        if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1271            return Err(JSError::ParseError);
1272        }
1273        tokens.remove(0); // consume {
1274        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); // consume }
1279
1280        let else_body = if !tokens.is_empty() && matches!(tokens[0], Token::Else) {
1281            tokens.remove(0); // consume else
1282            if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1283                return Err(JSError::ParseError);
1284            }
1285            tokens.remove(0); // consume {
1286            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); // consume }
1291            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); // consume try
1300        if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1301            return Err(JSError::ParseError);
1302        }
1303        tokens.remove(0); // consume {
1304        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); // consume }
1309
1310        // Parse optional catch
1311        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); // consume catch
1317            if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1318                return Err(JSError::ParseError);
1319            }
1320            tokens.remove(0); // consume (
1321            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); // consume )
1333            if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1334                return Err(JSError::ParseError);
1335            }
1336            tokens.remove(0); // consume {
1337            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); // consume }
1342        }
1343
1344        // Optional finally
1345        if !tokens.is_empty() && matches!(tokens[0], Token::Finally) {
1346            tokens.remove(0); // consume finally
1347            if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1348                return Err(JSError::ParseError);
1349            }
1350            tokens.remove(0); // consume {
1351            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); // consume }
1356            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); // consume for
1363        if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1364            return Err(JSError::ParseError);
1365        }
1366        tokens.remove(0); // consume (
1367
1368        // Check if this is a for-of loop
1369        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); // consume let/var/const
1372            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                    // This is a for-of loop
1377                    tokens.remove(0); // consume of
1378                    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); // consume )
1383                    if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1384                        return Err(JSError::ParseError);
1385                    }
1386                    tokens.remove(0); // consume {
1387                    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); // consume }
1392                    return Ok(Statement::ForOf(var_name, iterable, body));
1393                } else {
1394                    // This is a regular for loop with variable declaration, put tokens back
1395                    tokens.insert(0, saved_identifier_token);
1396                    tokens.insert(0, saved_declaration_token);
1397                }
1398            } else {
1399                // Not an identifier, put back the declaration token
1400                tokens.insert(0, saved_declaration_token);
1401            }
1402        }
1403
1404        // Parse initialization (regular for loop)
1405        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); // consume first ;
1417
1418        // Parse condition
1419        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); // consume second ;
1429
1430        // Parse increment
1431        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); // consume )
1441
1442        if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1443            return Err(JSError::ParseError);
1444        }
1445        tokens.remove(0); // consume {
1446
1447        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); // consume }
1453
1454        return Ok(Statement::For(init, condition, increment, body));
1455    }
1456    if !tokens.is_empty() && matches!(tokens[0], Token::Return) {
1457        tokens.remove(0); // consume return
1458        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); // consume let/var/const
1468
1469        // Check for destructuring
1470        if !tokens.is_empty() && matches!(tokens[0], Token::LBracket) {
1471            // Array destructuring
1472            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); // consume =
1477            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            // Object destructuring
1485            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); // consume =
1490            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            // Regular variable declaration
1498            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); // consume class
1522        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); // consume extends
1526                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            // Parse class body
1537            if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
1538                return Err(JSError::ParseError);
1539            }
1540            tokens.remove(0); // consume {
1541
1542            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                        // Parse constructor
1556                        if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
1557                            return Err(JSError::ParseError);
1558                        }
1559                        tokens.remove(0); // consume (
1560                        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); // consume {
1565                        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                        // Check for getter/setter
1573                        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); // consume get/set
1577                            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); // consume (
1589                            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); // consume {
1594                            let body = parse_statement_block(tokens)?;
1595                            if is_getter {
1596                                if !params.is_empty() {
1597                                    return Err(JSError::ParseError); // getters should have no parameters
1598                                }
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                                // setter
1606                                if params.len() != 1 {
1607                                    return Err(JSError::ParseError); // setters should have exactly one parameter
1608                                }
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                            // This is a method
1617                            tokens.remove(0); // consume (
1618                            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); // consume {
1623                            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                            // This is a property
1631                            tokens.remove(0); // consume =
1632                            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); // consume ;
1637                            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); // consume }
1655
1656            return Ok(Statement::Class(name, extends, members));
1657        }
1658    }
1659    let expr = parse_expression(tokens)?;
1660    // Check if this is an assignment expression
1661    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    // Hoist var declarations if this is a function scope
1692    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        // Evaluate the statement inside a closure so we can log the
1704        // statement index and AST if an error occurs while preserving
1705        // control-flow returns. The closure returns
1706        // Result<Option<ControlFlow>, JSError> where `Ok(None)` means
1707        // continue, `Ok(Some(cf))` means propagate control flow, and
1708        // `Err(e)` means an error that we log and then return.
1709        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                    // Special-case assignment expressions so we can mutate `env` or
1743                    // object properties. `parse_statement` only turns simple
1744                    // variable assignments into `Statement::Assign`, so here we
1745                    // handle expression-level assignments such as `obj.prop = val`
1746                    // and `arr[0] = val`.
1747                    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                                // set_prop_env will attempt to mutate the env-held
1757                                // object when possible, otherwise it will update
1758                                // the evaluated object and return it.
1759                                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                                // Evaluate index to a string key
1766                                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                                // Fallback: evaluate the expression normally
1784                                last_value = evaluate_expr(env, expr)?;
1785                            }
1786                        }
1787                    } else if let Expr::LogicalAndAssign(target, value_expr) = expr {
1788                        // Handle logical AND assignment: a &&= b
1789                        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                        // Handle logical OR assignment: a ||= b
1830                        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                        // Handle nullish coalescing assignment: a ??= b
1871                        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                        // create new block scope
1931                        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                    // Execute try block and handle catch/finally semantics
1951                    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                    // Finally block executes after try/catch
1990                    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                    // Execute initialization in for_env
2006                    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                            } // For now, only support let and expr in init
2028                        }
2029                    }
2030
2031                    loop {
2032                        // Check condition in for_env
2033                        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 // No condition means infinite loop
2038                        };
2039
2040                        if !should_continue {
2041                            break;
2042                        }
2043
2044                        // Execute body in block_env
2045                        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                        // Execute increment in for_env
2056                        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                                } // For now, only support expr in increment
2074                            }
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                        // Check condition
2116                        let cond_val = evaluate_expr(env, condition)?;
2117                        if !is_truthy(&cond_val) {
2118                            break Ok(None);
2119                        }
2120
2121                        // Execute body
2122                        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                        // Execute body first
2136                        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                        // Check condition
2147                        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                                    // Simple equality check for switch cases
2164                                    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                                    // Default case also falls through if a match was found before it
2192                                    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            // Handle rest element
2306            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                                // Rest in property value not supported in object destructuring
2371                                return Err(JSError::EvaluationError {
2372                                    message: "Invalid destructuring pattern".to_string(),
2373                                });
2374                            }
2375                        }
2376                    }
2377                    ObjectDestructuringElement::Rest(var) => {
2378                        // Collect remaining properties
2379                        let rest_obj = Rc::new(RefCell::new(JSObjectData::new()));
2380                        let mut assigned_keys = std::collections::HashSet::new();
2381
2382                        // Collect keys that were already assigned
2383                        for element in pattern {
2384                            if let ObjectDestructuringElement::Property { key, .. } = element {
2385                                assigned_keys.insert(key.clone());
2386                            }
2387                        }
2388
2389                        // Add remaining properties to rest object
2390                        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                    // Wait for the promise to resolve by running the event loop
2467                    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                                // Continue running the event loop
2479                            }
2480                        }
2481                    }
2482                }
2483                Value::Object(obj) => {
2484                    // Check if this is a Promise object with __promise property
2485                    if let Some(promise_rc) = obj_get_value(&obj, "__promise")?
2486                        && let Value::Promise(promise) = promise_rc.borrow().clone()
2487                    {
2488                        // Wait for the promise to resolve by running the event loop
2489                        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                                    // Continue running the event loop
2501                                }
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        // Return Object constructor function, not an object with methods
2544        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    // Assignment is handled at statement level, just evaluate the value
2599    evaluate_expr(env, value)
2600}
2601
2602fn evaluate_logical_and_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2603    // a &&= b is equivalent to a && (a = b)
2604    let left_val = evaluate_expr(env, target)?;
2605    if is_truthy(&left_val) {
2606        // Evaluate the assignment
2607        evaluate_assignment_expr(env, target, value)
2608    } else {
2609        // Return the left value without assignment
2610        Ok(left_val)
2611    }
2612}
2613
2614fn evaluate_logical_or_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2615    // a ||= b is equivalent to a || (a = b)
2616    let left_val = evaluate_expr(env, target)?;
2617    if !is_truthy(&left_val) {
2618        // Evaluate the assignment
2619        evaluate_assignment_expr(env, target, value)
2620    } else {
2621        // Return the left value without assignment
2622        Ok(left_val)
2623    }
2624}
2625
2626fn evaluate_nullish_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2627    // a ??= b is equivalent to a ?? (a = b)
2628    let left_val = evaluate_expr(env, target)?;
2629    match left_val {
2630        Value::Undefined => {
2631            // Evaluate the assignment
2632            evaluate_assignment_expr(env, target, value)
2633        }
2634        _ => {
2635            // Return the left value without assignment
2636            Ok(left_val)
2637        }
2638    }
2639}
2640
2641fn evaluate_add_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Result<Value, JSError> {
2642    // a += b is equivalent to a = a + b
2643    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    // a -= b is equivalent to a = a - b
2679    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    // a *= b is equivalent to a = a * b
2696    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    // a /= b is equivalent to a = a / b
2713    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    // a %= b is equivalent to a = a % b
2737    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    // Prefix increment: ++expr
2805    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    // Assign back
2815    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    // Prefix decrement: --expr
2859    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    // Assign back
2869    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    // Postfix increment: expr++
2913    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    // Assign back
2924    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    // Postfix decrement: expr--
2968    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    // Assign back
2979    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            // Cannot delete local variables
3054            Ok(Value::Boolean(false))
3055        }
3056        Expr::Property(obj, prop) => {
3057            // Delete property from object
3058            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            // Delete indexed property
3069            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            // Cannot delete other types of expressions
3087            Ok(Value::Boolean(false))
3088        }
3089    }
3090}
3091
3092fn evaluate_void(env: &JSObjectDataPtr, expr: &Expr) -> Result<Value, JSError> {
3093    // Evaluate the expression but always return undefined
3094    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)), // Different types are not equal
3163        },
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)), // Different types are not equal
3168        },
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            // Check if left is an instance of right (constructor)
3213            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            // Check if property exists in object
3220            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            // Nullish coalescing: return right if left is null or undefined, otherwise left
3230            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())) // or return undefined, but use empty string here
3248            }
3249        }
3250        (Value::Object(obj_map), Value::Number(n)) => {
3251            // Array-like indexing
3252            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            // Object property access with string key
3261            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        }), // other types of indexing not supported yet
3271    }
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    // Check if it's a method call first
3314    if let Expr::Property(obj_expr, method_name) = func_expr {
3315        // Special case for Array static methods
3316        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 this object looks like the `std` module (we used 'sprintf' as marker)
3332                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 this object looks like the `os` module (we used 'open' as marker)
3346                if obj_map.borrow().contains_key("open") {
3347                    return crate::js_os::handle_os_method(&obj_map, method, args, env);
3348                }
3349
3350                // If this object looks like the `os.path` module
3351                if obj_map.borrow().contains_key("join") {
3352                    return crate::js_os::handle_os_method(&obj_map, method, args, env);
3353                }
3354
3355                // If this object is a file-like object (we use '__file_id' as marker)
3356                if obj_map.borrow().contains_key("__file_id") {
3357                    return tmpfile::handle_file_method(&obj_map, method, args, env);
3358                }
3359                // Check if this is the Math object
3360                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                    // Date instance methods
3370                    crate::js_date::handle_date_method(&obj_map, method, args)
3371                } else if obj_map.borrow().contains_key("__regex") {
3372                    // RegExp instance methods
3373                    crate::js_regexp::handle_regexp_method(&obj_map, method, args, env)
3374                } else if is_array(&obj_map) {
3375                    // Array instance methods
3376                    crate::js_array::handle_array_instance_method(&obj_map, method, args, env, obj_expr)
3377                } else if obj_map.borrow().contains_key("__promise") {
3378                    // Promise instance methods
3379                    handle_promise_method(&obj_map, method, args, env)
3380                } else if obj_map.borrow().contains_key("__class_def__") {
3381                    // Class static methods
3382                    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                    // Check for user-defined method
3387                    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                                // Function call
3391                                // Collect all arguments, expanding spreads
3392                                let mut evaluated_args = Vec::new();
3393                                expand_spread_in_call_args(env, args, &mut evaluated_args)?;
3394                                // Create new environment starting with captured environment
3395                                let func_env = captured_env.clone();
3396                                // Bind parameters: assign provided args, set missing params to undefined
3397                                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                                // Execute function body
3405                                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                // Handle constructor static methods
3421                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        // Optional method call
3438        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                // Handle constructor static methods
3444                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        // Regular function call
3460        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                // Function call
3465                // Collect all arguments, expanding spreads
3466                let mut evaluated_args = Vec::new();
3467                expand_spread_in_call_args(env, args, &mut evaluated_args)?;
3468                // Create new environment starting with captured environment
3469                let func_env = captured_env.clone();
3470                // Bind parameters: provide provided args, set missing params to undefined
3471                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                // Execute function body
3479                evaluate_statements(&func_env, &body)
3480            }
3481            Value::Object(obj_map) => {
3482                // Check if this is a built-in constructor object
3483                if obj_map.borrow().contains_key("MAX_VALUE") && obj_map.borrow().contains_key("MIN_VALUE") {
3484                    // Number constructor call
3485                    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    // Check if it's a method call first
3502    if let Expr::Property(obj_expr, method_name) = func_expr {
3503        // Special case for Array static methods
3504        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 this object looks like the `std` module (we used 'sprintf' as marker)
3516                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 this object looks like the `os` module (we used 'open' as marker)
3530                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 this object looks like the `os.path` module
3535                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 this object is a file-like object (we use '__file_id' as marker)
3540                if obj_map.borrow().contains_key("__file_id") {
3541                    return tmpfile::handle_file_method(&obj_map, method_name, args, env);
3542                }
3543                // Check if this is the Math object
3544                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                    // Date instance methods
3554                    crate::js_date::handle_date_method(&obj_map, method_name, args)
3555                } else if obj_map.borrow().contains_key("__regex") {
3556                    // RegExp instance methods
3557                    crate::js_regexp::handle_regexp_method(&obj_map, method_name, args, env)
3558                } else if is_array(&obj_map) {
3559                    // Array instance methods
3560                    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                    // Promise instance methods
3563                    handle_promise_method(&obj_map, method_name, args, env)
3564                } else if obj_map.borrow().contains_key("__class_def__") {
3565                    // Class static methods
3566                    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                // Handle constructor static methods
3577                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        // Regular function call - check if base is null/undefined
3593        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                // Function call
3599                // Collect all arguments, expanding spreads
3600                let mut evaluated_args = Vec::new();
3601                expand_spread_in_call_args(env, args, &mut evaluated_args)?;
3602                // Create new environment starting with captured environment
3603                let func_env = captured_env.clone();
3604                // Bind parameters: provide provided args, set missing params to undefined
3605                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                // Execute function body
3613                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            // Spread operator: evaluate the expression and spread its properties
3627            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                    // Copy all properties from spread_obj to obj
3631                    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                        // Check if property already exists
3645                        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                                // Update getter
3655                                getter.replace((body.clone(), env.clone()));
3656                                obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(val)));
3657                            } else {
3658                                // Create new property descriptor
3659                                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                            // Create new property descriptor with getter
3668                            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                        // Check if property already exists
3684                        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                                // Update setter
3694                                setter.replace((params.clone(), body.clone(), env.clone()));
3695                                obj.borrow_mut().insert(key.to_string(), Rc::new(RefCell::new(val)));
3696                            } else {
3697                                // Create new property descriptor
3698                                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                            // Create new property descriptor with setter
3707                            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                    // Check if property already exists
3723                    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                            // Update value
3733                            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                            // Create new property descriptor
3737                            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            // Spread operator: evaluate the expression and spread its elements
3760            let spread_val = evaluate_expr(env, spread_expr)?;
3761            if let Value::Object(spread_obj) = spread_val {
3762                // Assume it's an array-like object
3763                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 length property
3786    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    // Array destructuring is handled at the statement level, not as an expression
3792    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    // Object destructuring is handled at the statement level, not as an expression
3799    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>), // UTF-16 code units
3865    Boolean(bool),
3866    Undefined,
3867    Object(JSObjectDataPtr),                               // Object with properties
3868    Function(String),                                      // Function name
3869    Closure(Vec<String>, Vec<Statement>, JSObjectDataPtr), // parameters, body, captured environment
3870    ClassDefinition(Rc<ClassDefinition>),                  // Class definition
3871    Getter(Vec<Statement>, JSObjectDataPtr),               // getter body, captured environment
3872    Setter(Vec<String>, Vec<Statement>, JSObjectDataPtr),  // setter parameter, body, captured environment
3873    Property {
3874        // Property descriptor with getter/setter/value
3875        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>>), // Promise object
3880}
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
3901// Helper functions for UTF-16 string operations
3902pub 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
3964// Helper function to compare two values for equality
3965pub 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, // Objects are not equal unless same reference
3972        _ => false,                                    // Different types are not equal
3973    }
3974}
3975
3976// Helper function to convert value to string for display
3977pub 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
3994// Helper function to convert value to string for sorting
3995pub 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
4022// Helper accessors for objects and environments
4023pub 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        // Check if this is a property descriptor
4027        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                    // Create a new environment with this bound to the object
4033                    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                // Create a new environment with this bound to the object
4047                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    // Check if there's a setter for this property
4068    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                    // Create a new environment with this bound to the object and the parameter
4074                    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, &param[0], val)?;
4078                    let _v = evaluate_statements(&setter_env, &body)?;
4079                } else {
4080                    // No setter, update value
4081                    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                // Create a new environment with this bound to the object and the parameter
4089                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, &param[0], val)?;
4093                evaluate_statements(&setter_env, &body)?;
4094                return Ok(());
4095            }
4096            _ => {}
4097        }
4098    }
4099    // No setter, just set the value normally
4100    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 // In JavaScript, delete always returns true
4111}
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(&current, key, val);
4134        }
4135        let parent_opt = current.borrow().prototype.clone();
4136        if let Some(parent) = parent_opt {
4137            current = parent;
4138        } else {
4139            // if not found, set in current env
4140            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(&current, key, val);
4150        }
4151        let parent_opt = current.borrow().prototype.clone();
4152        if let Some(parent) = parent_opt {
4153            current = parent;
4154        } else {
4155            // If no function scope found, set in current env (global)
4156            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
4211// Higher-level property API that operates on expressions + environment.
4212// `get_prop_env` evaluates `obj_expr` in `env` and returns the property's Rc if present.
4213pub 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
4221// `set_prop_env` attempts to set a property on the object referenced by `obj_expr`.
4222// Behavior:
4223// - If `obj_expr` is a variable name (Expr::Var) and that variable exists in `env`
4224//   and is an object, it mutates the stored object in-place and returns `Ok(None)`.
4225// - Otherwise it evaluates `obj_expr`, and if it yields an object, it inserts the
4226//   property into that object's map and returns `Ok(Some(Value::Object(map)))` so
4227//   the caller can decide what to do with the updated object value.
4228pub fn set_prop_env(env: &JSObjectDataPtr, obj_expr: &Expr, prop: &str, val: Value) -> Result<Option<Value>, JSError> {
4229    // Fast path: obj_expr is a variable that we can mutate in-place in env
4230    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            // Special-case `__proto__` assignment: set the prototype
4236            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                    // Non-object assigned to __proto__: ignore or set to None
4242                    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    // Fall back: evaluate the object expression and return an updated object value
4253    let obj_val = evaluate_expr(env, obj_expr)?;
4254    match obj_val {
4255        Value::Object(obj) => {
4256            // Special-case `__proto__` assignment: set the object's prototype
4257            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>), // case value, statements
4279    Default(Vec<Statement>),    // default statements
4280}
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), // array destructuring: let [a, b] = [1, 2];
4288    ConstDestructuringArray(Vec<DestructuringElement>, Expr), // const [a, b] = [1, 2];
4289    LetDestructuringObject(Vec<ObjectDestructuringElement>, Expr), // object destructuring: let {a, b} = {a: 1, b: 2};
4290    ConstDestructuringObject(Vec<ObjectDestructuringElement>, Expr), // const {a, b} = {a: 1, b: 2};
4291    Class(String, Option<String>, Vec<ClassMember>),        // name, extends, members
4292    Assign(String, Expr),                                   // variable assignment
4293    Expr(Expr),
4294    Return(Option<Expr>),
4295    If(Expr, Vec<Statement>, Option<Vec<Statement>>), // condition, then_body, else_body
4296    For(Option<Box<Statement>>, Option<Expr>, Option<Box<Statement>>, Vec<Statement>), // init, condition, increment, body
4297    ForOf(String, Expr, Vec<Statement>),              // variable, iterable, body
4298    While(Expr, Vec<Statement>),                      // condition, body
4299    DoWhile(Vec<Statement>, Expr),                    // body, condition
4300    Switch(Expr, Vec<SwitchCase>),                    // expression, cases
4301    Break,
4302    Continue,
4303    TryCatch(Vec<Statement>, String, Vec<Statement>, Option<Vec<Statement>>), // try_body, catch_param, catch_body, finally_body
4304    Throw(Expr),                                                              // throw expression
4305}
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>),           // target, value
4364    LogicalAndAssign(Box<Expr>, Box<Expr>), // target, value
4365    LogicalOrAssign(Box<Expr>, Box<Expr>),  // target, value
4366    NullishAssign(Box<Expr>, Box<Expr>),    // target, value
4367    AddAssign(Box<Expr>, Box<Expr>),        // target, value
4368    SubAssign(Box<Expr>, Box<Expr>),        // target, value
4369    MulAssign(Box<Expr>, Box<Expr>),        // target, value
4370    DivAssign(Box<Expr>, Box<Expr>),        // target, value
4371    ModAssign(Box<Expr>, Box<Expr>),        // target, value
4372    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>),                // parameters, body
4380    AsyncFunction(Vec<String>, Vec<Statement>),           // parameters, body for async functions
4381    ArrowFunction(Vec<String>, Vec<Statement>),           // parameters, body
4382    Object(Vec<(String, Expr)>),                          // object literal: key-value pairs
4383    Array(Vec<Expr>),                                     // array literal: [elem1, elem2, ...]
4384    Getter(Box<Expr>),                                    // getter function
4385    Setter(Box<Expr>),                                    // setter function
4386    Spread(Box<Expr>),                                    // spread operator: ...expr
4387    OptionalProperty(Box<Expr>, String),                  // optional property access: obj?.prop
4388    OptionalCall(Box<Expr>, Vec<Expr>),                   // optional call: obj?.method(args)
4389    Await(Box<Expr>),                                     // await expression
4390    This,                                                 // this keyword
4391    New(Box<Expr>, Vec<Expr>),                            // new expression: new Constructor(args)
4392    Super,                                                // super keyword
4393    SuperCall(Vec<Expr>),                                 // super() call in constructor
4394    SuperProperty(String),                                // super.property access
4395    SuperMethod(String, Vec<Expr>),                       // super.method() call
4396    ArrayDestructuring(Vec<DestructuringElement>),        // array destructuring: [a, b, ...rest]
4397    ObjectDestructuring(Vec<ObjectDestructuringElement>), // object destructuring: {a, b: c, ...rest}
4398    Value(Value),                                         // literal value
4399}
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),                              // a
4422    NestedArray(Vec<DestructuringElement>),        // [a, b]
4423    NestedObject(Vec<ObjectDestructuringElement>), // {a, b}
4424    Rest(String),                                  // ...rest
4425    Empty,                                         // for skipped elements: [, b] = [1, 2]
4426}
4427
4428#[derive(Debug, Clone)]
4429pub enum ObjectDestructuringElement {
4430    Property { key: String, value: DestructuringElement }, // a: b or a
4431    Rest(String),                                          // ...rest
4432}
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                    // Unicode escape sequence \uXXXX
4452                    *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; // will be incremented by 1 at the end
4458                    match u16::from_str_radix(&hex_str, 16) {
4459                        Ok(code) => {
4460                            result.push(code);
4461                        }
4462                        Err(_) => return Err(JSError::TokenizationError), // Invalid hex
4463                    }
4464                }
4465                'x' => {
4466                    // Hex escape sequence \xHH
4467                    *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; // will be incremented by 1 at the end
4473                    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                // For other escapes (regex escapes like \., \s, \], etc.) keep the backslash
4481                // so the regex engine receives the escape sequence. Push '\' then the char.
4482                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                // Recognize '??=' (nullish coalescing assignment), '??' (nullish coalescing), and '?.' (optional chaining)
4595                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                // Recognize '&&=' (logical AND assignment) and '&&' (logical AND)
4660                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                // Recognize '||=' (logical OR assignment) and '||' (logical OR)
4672                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                // integer and fractional part
4685                while i < chars.len() && (chars[i].is_ascii_digit() || chars[i] == '.') {
4686                    i += 1;
4687                }
4688                // optional exponent part
4689                if i < chars.len() && (chars[i] == 'e' || chars[i] == 'E') {
4690                    let mut j = i + 1;
4691                    // optional sign after e/E
4692                    if j < chars.len() && (chars[j] == '+' || chars[j] == '-') {
4693                        j += 1;
4694                    }
4695                    // require at least one digit in exponent
4696                    if j >= chars.len() || !chars[j].is_ascii_digit() {
4697                        return Err(JSError::TokenizationError);
4698                    }
4699                    // consume exponent digits
4700                    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; // skip opening quote
4711                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; // skip closing quote
4715            }
4716            '\'' => {
4717                i += 1; // skip opening quote
4718                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; // skip closing quote
4722            }
4723            '`' => {
4724                i += 1; // skip opening backtick
4725                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                        // Found ${, add string part before it
4730                        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; // Update i to after the parsed string
4735                        }
4736                        i += 2; // skip ${
4737                        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                        // Tokenize the expression inside ${}
4752                        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                // Add remaining string part
4763                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; // skip closing backtick
4770            }
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    /// Get the string representation of a token that can be used as an identifier/property name
4917    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); // consume ,
4993            } 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); // consume )
5002    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); // consume }
5011    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            // Constructor should be a simple identifier or property access, not a full expression
5209            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); // consume '('
5217                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); // consume ','
5232                    }
5233                }
5234                if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5235                    return Err(JSError::ParseError);
5236                }
5237                tokens.remove(0); // consume ')'
5238                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                // Build binary addition chain
5273                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            // Check if followed by ( for super() call
5305            if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5306                tokens.remove(0); // consume '('
5307                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); // consume ','
5322                    }
5323                }
5324                if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5325                    return Err(JSError::ParseError);
5326                }
5327                tokens.remove(0); // consume ')'
5328                Expr::SuperCall(args)
5329            } else if !tokens.is_empty() && matches!(tokens[0], Token::Dot) {
5330                tokens.remove(0); // consume '.'
5331                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                // Check if followed by ( for method call
5340                if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5341                    tokens.remove(0); // consume '('
5342                    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); // consume ','
5357                        }
5358                    }
5359                    if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5360                        return Err(JSError::ParseError);
5361                    }
5362                    tokens.remove(0); // consume ')'
5363                    Expr::SuperMethod(prop, args)
5364                } else {
5365                    Expr::SuperProperty(prop)
5366                }
5367            } else {
5368                Expr::Super
5369            }
5370        }
5371        Token::LBrace => {
5372            // Parse object literal
5373            let mut properties = Vec::new();
5374            if !tokens.is_empty() && matches!(tokens[0], Token::RBrace) {
5375                // Empty object {}
5376                tokens.remove(0); // consume }
5377                return Ok(Expr::Object(properties));
5378            }
5379            loop {
5380                // Check for spread
5381                if !tokens.is_empty() && matches!(tokens[0], Token::Spread) {
5382                    tokens.remove(0); // consume ...
5383                    let expr = parse_expression(tokens)?;
5384                    properties.push(("".to_string(), Expr::Spread(Box::new(expr))));
5385                } else {
5386                    // Check for getter/setter
5387                    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); // consume get/set
5392                    }
5393
5394                    // Parse key
5395                    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                    // Expect colon or parentheses for getter/setter
5406                    if is_getter || is_setter {
5407                        // Parse function for getter/setter
5408                        if tokens.is_empty() || !matches!(tokens[0], Token::LParen) {
5409                            return Err(JSError::ParseError);
5410                        }
5411                        tokens.remove(0); // consume (
5412
5413                        let mut params = Vec::new();
5414                        if is_setter {
5415                            // Setter should have exactly one parameter
5416                            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                            // Getter should have no parameters
5424                        }
5425
5426                        if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5427                            return Err(JSError::ParseError);
5428                        }
5429                        tokens.remove(0); // consume )
5430
5431                        if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
5432                            return Err(JSError::ParseError);
5433                        }
5434                        tokens.remove(0); // consume {
5435
5436                        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); // consume }
5442
5443                        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                        // Regular property
5450                        if tokens.is_empty() || !matches!(tokens[0], Token::Colon) {
5451                            return Err(JSError::ParseError);
5452                        }
5453                        tokens.remove(0); // consume :
5454
5455                        // Parse value
5456                        let value = parse_expression(tokens)?;
5457                        properties.push((key, value));
5458                    }
5459                }
5460
5461                // Check for comma or end
5462                if tokens.is_empty() {
5463                    return Err(JSError::ParseError);
5464                }
5465                if matches!(tokens[0], Token::RBrace) {
5466                    tokens.remove(0); // consume }
5467                    break;
5468                } else if matches!(tokens[0], Token::Comma) {
5469                    tokens.remove(0); // consume ,
5470                } else {
5471                    return Err(JSError::ParseError);
5472                }
5473            }
5474            Expr::Object(properties)
5475        }
5476        Token::LBracket => {
5477            // Parse array literal
5478            let mut elements = Vec::new();
5479            if !tokens.is_empty() && matches!(tokens[0], Token::RBracket) {
5480                // Empty array []
5481                tokens.remove(0); // consume ]
5482                return Ok(Expr::Array(elements));
5483            }
5484            loop {
5485                // Parse element
5486                let elem = parse_expression(tokens)?;
5487                elements.push(elem);
5488
5489                // Check for comma or end
5490                if tokens.is_empty() {
5491                    return Err(JSError::ParseError);
5492                }
5493                if matches!(tokens[0], Token::RBracket) {
5494                    tokens.remove(0); // consume ]
5495                    break;
5496                } else if matches!(tokens[0], Token::Comma) {
5497                    tokens.remove(0); // consume ,
5498                } else {
5499                    return Err(JSError::ParseError);
5500                }
5501            }
5502            Expr::Array(elements)
5503        }
5504        Token::Function => {
5505            // Parse function expression
5506            if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5507                tokens.remove(0); // consume (
5508                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); // consume ,
5524                        } 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); // consume )
5533                if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
5534                    return Err(JSError::ParseError);
5535                }
5536                tokens.remove(0); // consume {
5537                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); // consume }
5542                Expr::Function(params, body)
5543            } else {
5544                return Err(JSError::ParseError);
5545            }
5546        }
5547        Token::Async => {
5548            // Check if followed by function or arrow function parameters
5549            if !tokens.is_empty() && matches!(tokens[0], Token::Function) {
5550                tokens.remove(0); // consume function
5551                if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5552                    tokens.remove(0); // consume (
5553                    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); // consume ,
5569                            } 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); // consume )
5578                    if tokens.is_empty() || !matches!(tokens[0], Token::LBrace) {
5579                        return Err(JSError::ParseError);
5580                    }
5581                    tokens.remove(0); // consume {
5582                    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); // consume }
5587                    Expr::AsyncFunction(params, body)
5588                } else {
5589                    return Err(JSError::ParseError);
5590                }
5591            } else if !tokens.is_empty() && matches!(tokens[0], Token::LParen) {
5592                // Async arrow function
5593                tokens.remove(0); // consume (
5594                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                    // Try to parse params
5606                    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                        // Put back local_consumed
5642                        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                    // For async arrow functions, we need to create a special async closure
5651                    // For now, we'll treat them as regular arrow functions but mark them as async
5652                    // This will need to be handled in evaluation
5653                    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            // Check if it's arrow function
5663            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                // Try to parse params
5676                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                    // Put back local_consumed
5714                    for t in local_consumed.into_iter().rev() {
5715                        tokens.insert(0, t);
5716                    }
5717                    // Parse as expression
5718                    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    // Handle postfix operators like index access
5740    while !tokens.is_empty() {
5741        match &tokens[0] {
5742            Token::LBracket => {
5743                tokens.remove(0); // consume '['
5744                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); // consume ']'
5749                expr = Expr::Index(Box::new(expr), Box::new(index_expr));
5750            }
5751            Token::Dot => {
5752                tokens.remove(0); // consume '.'
5753                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); // consume '?.'
5765                if tokens.is_empty() {
5766                    return Err(JSError::ParseError);
5767                }
5768                if matches!(tokens[0], Token::LParen) {
5769                    // Optional call: obj?.method(args)
5770                    tokens.remove(0); // consume '('
5771                    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); // consume ','
5786                        }
5787                    }
5788                    if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5789                        return Err(JSError::ParseError);
5790                    }
5791                    tokens.remove(0); // consume ')'
5792                    expr = Expr::OptionalCall(Box::new(expr), args);
5793                } else if matches!(tokens[0], Token::Identifier(_)) {
5794                    // Optional property access: obj?.prop
5795                    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); // consume '('
5807                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); // consume ','
5822                    }
5823                }
5824                if tokens.is_empty() || !matches!(tokens[0], Token::RParen) {
5825                    return Err(JSError::ParseError);
5826                }
5827                tokens.remove(0); // consume ')'
5828                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); // consume [
5865
5866    let mut pattern = Vec::new();
5867    if !tokens.is_empty() && matches!(tokens[0], Token::RBracket) {
5868        tokens.remove(0); // consume ]
5869        return Ok(pattern);
5870    }
5871
5872    loop {
5873        if !tokens.is_empty() && matches!(tokens[0], Token::Spread) {
5874            tokens.remove(0); // consume ...
5875            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            // Rest must be the last element
5882            if tokens.is_empty() || !matches!(tokens[0], Token::RBracket) {
5883                return Err(JSError::ParseError);
5884            }
5885            tokens.remove(0); // consume ]
5886            break;
5887        } else if !tokens.is_empty() && matches!(tokens[0], Token::Comma) {
5888            tokens.remove(0); // consume ,
5889            pattern.push(DestructuringElement::Empty);
5890        } else if !tokens.is_empty() && matches!(tokens[0], Token::LBracket) {
5891            // Nested array destructuring
5892            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            // Nested object destructuring
5896            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); // consume ]
5910            break;
5911        } else if matches!(tokens[0], Token::Comma) {
5912            tokens.remove(0); // consume ,
5913        } 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); // consume {
5926
5927    let mut pattern = Vec::new();
5928    if !tokens.is_empty() && matches!(tokens[0], Token::RBrace) {
5929        tokens.remove(0); // consume }
5930        return Ok(pattern);
5931    }
5932
5933    loop {
5934        if !tokens.is_empty() && matches!(tokens[0], Token::Spread) {
5935            tokens.remove(0); // consume ...
5936            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            // Rest must be the last element
5943            if tokens.is_empty() || !matches!(tokens[0], Token::RBrace) {
5944                return Err(JSError::ParseError);
5945            }
5946            tokens.remove(0); // consume }
5947            break;
5948        } else {
5949            // Parse property
5950            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); // consume :
5959                // Parse the value pattern
5960                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                // Shorthand: key is the same as variable name
5972                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); // consume }
5983            break;
5984        } else if matches!(tokens[0], Token::Comma) {
5985            tokens.remove(0); // consume ,
5986        } else {
5987            return Err(JSError::ParseError);
5988        }
5989    }
5990
5991    Ok(pattern)
5992}
5993
5994/// # Safety
5995/// The caller must ensure that `_ctx` is a valid pointer to a JSContext and `this_obj` is a valid JSValue.
5996pub 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            // Duplicate returned value when it's ref-counted so caller owns a reference
6006            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
6016// Reference-count helpers: basic dup/free on objects/strings that store a ref_count
6017// NOTE: This is a minimal implementation. Proper finalizers and nested frees
6018// are not implemented here and should be added per object type.
6019/// # Safety
6020/// The caller must ensure that `v` is a valid JSValue and `_rt` is a valid JSRuntime pointer.
6021pub 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
6033/// # Safety
6034/// The caller must ensure that `rt` is a valid JSRuntime pointer and `v` is a valid JSValue.
6035pub 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            // ref_count reached zero: dispatch based on tag to proper finalizer
6048            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                // For other heap types, do a default free of the pointer
6068                _ => {
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        // The whole JSString allocation was allocated via js_malloc_rt
6083        (*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        // Free property array
6094        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        // Free shape
6099        if !(*p).shape.is_null() {
6100            (*rt).js_free_shape((*p).shape);
6101            (*p).shape = std::ptr::null_mut();
6102        }
6103        // Free object struct
6104        (*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        // Free bytecode buffer
6115        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        // Free pc2line buffer
6120        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        // Free source
6125        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        // Free cpool values
6130        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        // Finally free the struct
6141        (*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    // Symbols typically store their name as a JSString or internal struct
6151    // For now, free the pointer directly. Add type-aware finalizer later.
6152    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    // BigInt representation may be inline or heap-allocated. Here we free pointer.
6161    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    // Module structure not modelled here; free pointer for now.
6170    unsafe { (*rt).js_free_rt(p) };
6171}
6172
6173/// # Safety
6174/// The caller must ensure that `ctx` is a valid JSContext pointer, `this_obj` is a valid JSValue, and `prop` is a valid JSAtom.
6175pub 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    /// # Safety
6181    /// The caller must ensure that `name` points to a valid buffer of at least `len` bytes.
6182    pub unsafe fn js_new_atom_len(&mut self, name: *const u8, len: usize) -> JSAtom {
6183        if len == 0 {
6184            return 0; // invalid
6185        }
6186        // Compute hash
6187        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        // Find in hash table
6192        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                // Check string
6198                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        // Not found, create new
6213        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        // Allocate JSString
6231        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        // Copy string
6241        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    // Remove comments and simple import lines that we've already handled via shim injection
6255    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        // Handle escape sequences
6267        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        // Handle quote states
6281        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        // Only process comments when not inside quotes
6304        if !in_single && !in_double && !in_backtick {
6305            // Handle single-line comments: //
6306            if i + 1 < chars.len() && ch == '/' && chars[i + 1] == '/' {
6307                // Skip to end of line
6308                while i < chars.len() && chars[i] != '\n' {
6309                    i += 1;
6310                }
6311                // Don't add the newline yet, continue to next iteration
6312                continue;
6313            }
6314
6315            // Handle multi-line comments: /* */
6316            if i + 1 < chars.len() && ch == '/' && chars[i + 1] == '*' {
6317                i += 2; // Skip /*
6318                while i + 1 < chars.len() {
6319                    if chars[i] == '*' && chars[i + 1] == '/' {
6320                        i += 2; // Skip */
6321                        break;
6322                    }
6323                    i += 1;
6324                }
6325                continue;
6326            }
6327        }
6328
6329        // Handle regular characters and newlines
6330        filtered.push(ch);
6331        i += 1;
6332    }
6333
6334    // Now process the filtered script line by line for import statements
6335    let mut final_filtered = String::new();
6336    for (i, line) in filtered.lines().enumerate() {
6337        // Split line on semicolons only when not inside quotes/backticks
6338        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        // track parts along with whether they were followed by a semicolon
6344        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 there is a trailing part (possibly no trailing semicolon), add it
6382        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            // Re-add semicolon if the original part was followed by a semicolon
6398            if *had_semicolon {
6399                final_filtered.push(';');
6400            }
6401        }
6402        final_filtered.push('\n');
6403    }
6404
6405    // Remove any trailing newline(s) added during filtering to avoid an extra
6406    // empty statement at the end when tokenizing/parsing.
6407    final_filtered.trim().to_string()
6408}
6409
6410/// Initialize global built-in constructors in the environment
6411fn initialize_global_constructors(env: &JSObjectDataPtr) {
6412    let mut env_borrow = env.borrow_mut();
6413
6414    // Object constructor
6415    env_borrow.insert("Object".to_string(), Rc::new(RefCell::new(Value::Function("Object".to_string()))));
6416
6417    // Number constructor - handled by evaluate_var
6418    // env_borrow.insert("Number".to_string(), Rc::new(RefCell::new(Value::Function("Number".to_string()))));
6419
6420    // Boolean constructor
6421    env_borrow.insert("Boolean".to_string(), Rc::new(RefCell::new(Value::Function("Boolean".to_string()))));
6422
6423    // String constructor
6424    env_borrow.insert("String".to_string(), Rc::new(RefCell::new(Value::Function("String".to_string()))));
6425
6426    // Array constructor (already handled by js_array module)
6427    env_borrow.insert("Array".to_string(), Rc::new(RefCell::new(Value::Function("Array".to_string()))));
6428
6429    // Date constructor (already handled by js_date module)
6430    env_borrow.insert("Date".to_string(), Rc::new(RefCell::new(Value::Function("Date".to_string()))));
6431
6432    // RegExp constructor (already handled by js_regexp module)
6433    env_borrow.insert("RegExp".to_string(), Rc::new(RefCell::new(Value::Function("RegExp".to_string()))));
6434
6435    // Internal promise resolution functions
6436    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
6458/// Expand spread operator in function call arguments
6459fn 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                // Assume it's an array-like object
6465                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
6488/// Handle optional method call on an object, Similar logic to regular method call but for optional
6489fn 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 this object looks like the `std` module (we used 'sprintf' as marker)
6502            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                // If this object looks like the `os` module (we used 'open' as marker)
6513                crate::js_os::handle_os_method(obj_map, method, args, env)
6514            } else if obj_map.borrow().contains_key("join") {
6515                // If this object looks like the `os.path` module
6516                crate::js_os::handle_os_method(obj_map, method, args, env)
6517            } else if obj_map.borrow().contains_key("__file_id") {
6518                // If this object is a file-like object (we use '__file_id' as marker)
6519                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                // Check if this is the Math object
6522                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                // Date instance methods
6529                crate::js_date::handle_date_method(obj_map, method, args)
6530            } else if obj_map.borrow().contains_key("__regex") {
6531                // RegExp instance methods
6532                crate::js_regexp::handle_regexp_method(obj_map, method, args, env)
6533            } else if is_array(obj_map) {
6534                // Array instance methods
6535                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                // Class static methods
6538                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                // Check for user-defined method
6543                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                            // Function call
6547                            // Collect all arguments, expanding spreads
6548                            let mut evaluated_args = Vec::new();
6549                            expand_spread_in_call_args(env, args, &mut evaluated_args)?;
6550                            // Create new environment starting with captured environment
6551                            let func_env = captured_env.clone();
6552                            // Bind parameters: provide provided args, set missing params to undefined
6553                            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                            // Execute function body
6561                            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}