Skip to main content

just_engine/runner/eval/
expression.rs

1//! Expression evaluation.
2//!
3//! This module provides the core expression evaluation logic for the JavaScript interpreter.
4//! It handles all expression types defined in the AST.
5
6use crate::parser::ast::{
7    AssignmentOperator, BinaryOperator, ClassData, ExpressionOrSpreadElement, ExpressionOrSuper,
8    ExpressionType, ExpressionPatternType, FunctionData, LiteralData, LiteralType, MemberExpressionType,
9    MethodDefinitionKind, PatternOrExpression, PatternType, PropertyData, PropertyKind, UnaryOperator,
10    UpdateOperator, LogicalOperator,
11};
12use crate::runner::ds::error::JErrorType;
13use crate::runner::ds::object::{JsObject, JsObjectType, ObjectBase, ObjectType};
14use crate::runner::ds::object_property::{PropertyDescriptor, PropertyDescriptorAccessor, PropertyDescriptorData, PropertyKey};
15use crate::runner::ds::value::{JsValue, JsNumberType};
16use crate::runner::plugin::types::{EvalContext, SimpleObject};
17
18use std::cell::RefCell;
19use std::rc::Rc;
20
21use super::types::ValueResult;
22
23/// Evaluate an expression and return its value.
24pub fn evaluate_expression(
25    expr: &ExpressionType,
26    ctx: &mut EvalContext,
27) -> ValueResult {
28    match expr {
29        ExpressionType::Literal(lit) => evaluate_literal(lit),
30
31        ExpressionType::ExpressionWhichCanBePattern(pattern) => {
32            evaluate_expression_pattern(pattern, ctx)
33        }
34
35        ExpressionType::ThisExpression { .. } => {
36            Ok(ctx.global_this.clone().unwrap_or(JsValue::Undefined))
37        }
38
39        ExpressionType::ArrayExpression { elements, .. } => {
40            evaluate_array_expression(elements, ctx)
41        }
42
43        ExpressionType::ObjectExpression { properties, .. } => {
44            evaluate_object_expression(properties, ctx)
45        }
46
47        ExpressionType::FunctionOrGeneratorExpression(func_data) => {
48            create_function_object(func_data, ctx)
49        }
50
51        ExpressionType::UnaryExpression { operator, argument, .. } => {
52            evaluate_unary_expression(operator, argument, ctx)
53        }
54
55        ExpressionType::BinaryExpression { operator, left, right, .. } => {
56            evaluate_binary_expression(operator, left, right, ctx)
57        }
58
59        ExpressionType::LogicalExpression { operator, left, right, .. } => {
60            evaluate_logical_expression(operator, left, right, ctx)
61        }
62
63        ExpressionType::UpdateExpression { operator, argument, prefix, .. } => {
64            evaluate_update_expression(operator, argument, *prefix, ctx)
65        }
66
67        ExpressionType::AssignmentExpression { operator, left, right, .. } => {
68            evaluate_assignment_expression(operator, left, right, ctx)
69        }
70
71        ExpressionType::ConditionalExpression { test, consequent, alternate, .. } => {
72            evaluate_conditional_expression(test, consequent, alternate, ctx)
73        }
74
75        ExpressionType::CallExpression { callee, arguments, .. } => {
76            evaluate_call_expression(callee, arguments, ctx)
77        }
78
79        ExpressionType::NewExpression { callee, arguments, .. } => {
80            evaluate_new_expression(callee, arguments, ctx)
81        }
82
83        ExpressionType::SequenceExpression { expressions, .. } => {
84            evaluate_sequence_expression(expressions, ctx)
85        }
86
87        ExpressionType::TemplateLiteral(_) => {
88            Err(JErrorType::TypeError("Template literal not yet implemented".to_string()))
89        }
90
91        ExpressionType::TaggedTemplateExpression { .. } => {
92            Err(JErrorType::TypeError("Tagged template expression not yet implemented".to_string()))
93        }
94
95        ExpressionType::ClassExpression(class_data) => {
96            evaluate_class_expression(class_data, ctx)
97        }
98
99        ExpressionType::YieldExpression { argument, delegate, .. } => {
100            if *delegate {
101                // yield* not yet supported
102                return Err(JErrorType::TypeError("yield* not yet implemented".to_string()));
103            }
104            // Evaluate the argument
105            let value = if let Some(arg) = argument {
106                evaluate_expression(arg, ctx)?
107            } else {
108                JsValue::Undefined
109            };
110            // Return a special error that signals a yield
111            Err(JErrorType::YieldValue(value))
112        }
113
114        ExpressionType::MetaProperty { .. } => {
115            Err(JErrorType::TypeError("Meta property not yet implemented".to_string()))
116        }
117
118        ExpressionType::ArrowFunctionExpression { .. } => {
119            Err(JErrorType::TypeError("Arrow function expression not yet implemented".to_string()))
120        }
121
122        ExpressionType::MemberExpression(member_expr) => {
123            evaluate_member_expression(member_expr, ctx)
124        }
125    }
126}
127
128/// Evaluate a literal and return its value.
129fn evaluate_literal(lit: &LiteralData) -> ValueResult {
130    Ok(match &lit.value {
131        LiteralType::NullLiteral => JsValue::Null,
132        LiteralType::BooleanLiteral(b) => JsValue::Boolean(*b),
133        LiteralType::StringLiteral(s) => JsValue::String(s.clone()),
134        LiteralType::NumberLiteral(n) => {
135            use crate::parser::ast::NumberLiteralType;
136            match n {
137                NumberLiteralType::IntegerLiteral(i) => JsValue::Number(JsNumberType::Integer(*i)),
138                NumberLiteralType::FloatLiteral(f) => JsValue::Number(JsNumberType::Float(*f)),
139            }
140        }
141        LiteralType::RegExpLiteral { .. } => {
142            return Err(JErrorType::TypeError("RegExp literal not yet implemented".to_string()));
143        }
144    })
145}
146
147/// Evaluate an array expression and return an array object.
148fn evaluate_array_expression(
149    elements: &[Option<ExpressionOrSpreadElement>],
150    ctx: &mut EvalContext,
151) -> ValueResult {
152    use crate::runner::ds::object::JsObject;
153
154    // Create a new array object (tracked for heap accounting)
155    let mut array_obj = ctx.new_tracked_object()?;
156
157    let mut index = 0;
158    for element in elements {
159        if let Some(elem) = element {
160            match elem {
161                ExpressionOrSpreadElement::Expression(expr) => {
162                    let value = evaluate_expression(expr, ctx)?;
163                    // Define the element as a property
164                    let key = PropertyKey::Str(index.to_string());
165                    array_obj.get_object_base_mut().properties.insert(
166                        key,
167                        PropertyDescriptor::Data(PropertyDescriptorData {
168                            value,
169                            writable: true,
170                            enumerable: true,
171                            configurable: true,
172                        }),
173                    );
174                    index += 1;
175                }
176                ExpressionOrSpreadElement::SpreadElement(spread_expr) => {
177                    // Evaluate the spread expression
178                    let spread_value = evaluate_expression(spread_expr, ctx)?;
179
180                    // If it's an array, spread its elements
181                    if let JsValue::Object(spread_obj) = spread_value {
182                        let borrowed = spread_obj.borrow();
183                        let base = borrowed.as_js_object().get_object_base();
184
185                        // Get length property if it exists
186                        let length = if let Some(PropertyDescriptor::Data(prop)) =
187                            base.properties.get(&PropertyKey::Str("length".to_string()))
188                        {
189                            match &prop.value {
190                                JsValue::Number(JsNumberType::Integer(n)) => *n as usize,
191                                JsValue::Number(JsNumberType::Float(n)) => *n as usize,
192                                _ => 0,
193                            }
194                        } else {
195                            0
196                        };
197
198                        // Iterate over array indices
199                        for i in 0..length {
200                            let elem_key = PropertyKey::Str(i.to_string());
201                            let elem_value = if let Some(PropertyDescriptor::Data(prop)) =
202                                base.properties.get(&elem_key)
203                            {
204                                prop.value.clone()
205                            } else {
206                                JsValue::Undefined
207                            };
208
209                            // Add to result array
210                            let key = PropertyKey::Str(index.to_string());
211                            array_obj.get_object_base_mut().properties.insert(
212                                key,
213                                PropertyDescriptor::Data(PropertyDescriptorData {
214                                    value: elem_value,
215                                    writable: true,
216                                    enumerable: true,
217                                    configurable: true,
218                                }),
219                            );
220                            index += 1;
221                        }
222                    } else if let JsValue::String(s) = spread_value {
223                        // Spread string characters
224                        for ch in s.chars() {
225                            let key = PropertyKey::Str(index.to_string());
226                            array_obj.get_object_base_mut().properties.insert(
227                                key,
228                                PropertyDescriptor::Data(PropertyDescriptorData {
229                                    value: JsValue::String(ch.to_string()),
230                                    writable: true,
231                                    enumerable: true,
232                                    configurable: true,
233                                }),
234                            );
235                            index += 1;
236                        }
237                    } else {
238                        return Err(JErrorType::TypeError(
239                            "Spread requires an iterable".to_string(),
240                        ));
241                    }
242                }
243            }
244        } else {
245            // Holes (None elements) are left as undefined/missing
246            index += 1;
247        }
248    }
249
250    // Set the length property
251    array_obj.get_object_base_mut().properties.insert(
252        PropertyKey::Str("length".to_string()),
253        PropertyDescriptor::Data(PropertyDescriptorData {
254            value: JsValue::Number(JsNumberType::Integer(index as i64)),
255            writable: true,
256            enumerable: false,
257            configurable: false,
258        }),
259    );
260
261    // Wrap in JsObjectType
262    let obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(array_obj))));
263    Ok(JsValue::Object(obj))
264}
265
266/// Evaluate an object expression and return an object.
267fn evaluate_object_expression(
268    properties: &[PropertyData<Box<ExpressionType>>],
269    ctx: &mut EvalContext,
270) -> ValueResult {
271    use std::collections::HashMap;
272
273    // Create a new object (tracked for heap accounting)
274    let mut obj = ctx.new_tracked_object()?;
275
276    // Track accessor properties to merge getter/setter pairs
277    let mut accessors: HashMap<String, (Option<JsObjectType>, Option<JsObjectType>)> = HashMap::new();
278
279    for prop in properties {
280        // Get the property key
281        let key = get_object_property_key(&prop.key, prop.computed, ctx)?;
282
283        match prop.kind {
284            PropertyKind::Init => {
285                // Evaluate the value
286                let value = evaluate_expression(&prop.value, ctx)?;
287
288                // Define the property
289                obj.get_object_base_mut().properties.insert(
290                    PropertyKey::Str(key),
291                    PropertyDescriptor::Data(PropertyDescriptorData {
292                        value,
293                        writable: true,
294                        enumerable: true,
295                        configurable: true,
296                    }),
297                );
298            }
299            PropertyKind::Get => {
300                // The value should be a function expression
301                if let ExpressionType::FunctionOrGeneratorExpression(func_data) = prop.value.as_ref() {
302                    let getter_fn = create_function_object(func_data, ctx)?;
303                    if let JsValue::Object(getter_obj) = getter_fn {
304                        let entry = accessors.entry(key).or_insert((None, None));
305                        entry.0 = Some(getter_obj);
306                    }
307                } else {
308                    return Err(JErrorType::TypeError("Getter must be a function".to_string()));
309                }
310            }
311            PropertyKind::Set => {
312                // The value should be a function expression
313                if let ExpressionType::FunctionOrGeneratorExpression(func_data) = prop.value.as_ref() {
314                    let setter_fn = create_function_object(func_data, ctx)?;
315                    if let JsValue::Object(setter_obj) = setter_fn {
316                        let entry = accessors.entry(key).or_insert((None, None));
317                        entry.1 = Some(setter_obj);
318                    }
319                } else {
320                    return Err(JErrorType::TypeError("Setter must be a function".to_string()));
321                }
322            }
323        }
324    }
325
326    // Add accessor properties to the object
327    for (key, (getter, setter)) in accessors {
328        obj.get_object_base_mut().properties.insert(
329            PropertyKey::Str(key),
330            PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
331                get: getter,
332                set: setter,
333                enumerable: true,
334                configurable: true,
335            }),
336        );
337    }
338
339    // Wrap in JsObjectType
340    let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(obj))));
341    Ok(JsValue::Object(obj_ref))
342}
343
344/// Get the property key from an object literal property.
345fn get_object_property_key(
346    key_expr: &ExpressionType,
347    computed: bool,
348    ctx: &mut EvalContext,
349) -> Result<String, JErrorType> {
350    if computed {
351        // Computed property: [expr]
352        let key_value = evaluate_expression(key_expr, ctx)?;
353        Ok(to_string(&key_value))
354    } else {
355        // Static property key
356        match key_expr {
357            ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(id)) => {
358                Ok(id.name.clone())
359            }
360            ExpressionType::Literal(lit) => match &lit.value {
361                LiteralType::StringLiteral(s) => Ok(s.clone()),
362                LiteralType::NumberLiteral(n) => {
363                    use crate::parser::ast::NumberLiteralType;
364                    match n {
365                        NumberLiteralType::IntegerLiteral(i) => Ok(i.to_string()),
366                        NumberLiteralType::FloatLiteral(f) => Ok(f.to_string()),
367                    }
368                }
369                _ => Err(JErrorType::TypeError("Invalid property key".to_string())),
370            },
371            _ => Err(JErrorType::TypeError("Invalid property key".to_string())),
372        }
373    }
374}
375
376/// Evaluate an expression pattern (identifier).
377fn evaluate_expression_pattern(
378    pattern: &ExpressionPatternType,
379    ctx: &mut EvalContext,
380) -> ValueResult {
381    match pattern {
382        ExpressionPatternType::Identifier(id) => {
383            // Look up the identifier in the environment chain
384            ctx.get_binding(&id.name)
385        }
386    }
387}
388
389/// Evaluate a member expression (property access).
390fn evaluate_member_expression(
391    member_expr: &MemberExpressionType,
392    ctx: &mut EvalContext,
393) -> ValueResult {
394    match member_expr {
395        MemberExpressionType::SimpleMemberExpression { object, property, .. } => {
396            // Evaluate the object
397            let obj_value = evaluate_expression_or_super(object, ctx)?;
398            let prop_name = &property.name;
399
400            // Get the property (with getter support)
401            get_property_with_ctx(&obj_value, prop_name, ctx)
402        }
403        MemberExpressionType::ComputedMemberExpression { object, property, .. } => {
404            // Evaluate the object
405            let obj_value = evaluate_expression_or_super(object, ctx)?;
406
407            // Evaluate the property expression to get the key
408            let prop_value = evaluate_expression(property, ctx)?;
409            let prop_name = to_property_key(&prop_value);
410
411            // Get the property (with getter support)
412            get_property_with_ctx(&obj_value, &prop_name, ctx)
413        }
414    }
415}
416
417/// Evaluate an ExpressionOrSuper.
418fn evaluate_expression_or_super(
419    expr_or_super: &ExpressionOrSuper,
420    ctx: &mut EvalContext,
421) -> ValueResult {
422    match expr_or_super {
423        ExpressionOrSuper::Expression(expr) => evaluate_expression(expr, ctx),
424        ExpressionOrSuper::Super => {
425            Err(JErrorType::TypeError("super not yet supported".to_string()))
426        }
427    }
428}
429
430/// Evaluate a call expression (function call).
431fn evaluate_call_expression(
432    callee: &ExpressionOrSuper,
433    arguments: &[ExpressionOrSpreadElement],
434    ctx: &mut EvalContext,
435) -> ValueResult {
436    // Check if this is a member expression call (e.g., Math.abs, console.log)
437    // If so, try super-global method dispatch first
438    if let ExpressionOrSuper::Expression(expr) = callee {
439        if let ExpressionType::MemberExpression(member) = expr.as_ref() {
440            if let MemberExpressionType::SimpleMemberExpression { object, property, .. } = member {
441                // Try to get the object name for super-global lookup
442                if let ExpressionOrSuper::Expression(obj_expr) = object {
443                    if let ExpressionType::ExpressionWhichCanBePattern(
444                        ExpressionPatternType::Identifier(id)
445                    ) = obj_expr.as_ref() {
446                        let obj_name = &id.name;
447                        let method_name = &property.name;
448                        
449                        // Evaluate arguments first
450                        let args = evaluate_arguments(arguments, ctx)?;
451                        
452                        // Try super-global method dispatch
453                        let sg = ctx.super_global.clone();
454                        let this_value = ctx.resolve_binding(obj_name).unwrap_or(JsValue::Undefined);
455                        
456                        let sg_result = sg.borrow().call_method(
457                            obj_name,
458                            method_name,
459                            ctx,
460                            this_value.clone(),
461                            args.clone(),
462                        );
463                        
464                        if let Some(result) = sg_result {
465                            return result;
466                        }
467                        
468                        // Fall through to normal property lookup if super-global doesn't handle it
469                    }
470                }
471            }
472        }
473    }
474    
475    // Normal call path: evaluate the callee to get the function
476    let callee_value = evaluate_expression_or_super(callee, ctx)?;
477
478    // Get the 'this' value for the call
479    let this_value = get_call_this_value(callee, ctx);
480
481    // Evaluate the arguments
482    let args = evaluate_arguments(arguments, ctx)?;
483
484    // Call the function
485    call_value(&callee_value, this_value, args, ctx)
486}
487
488/// Evaluate a new expression (constructor call).
489fn evaluate_new_expression(
490    callee: &ExpressionType,
491    arguments: &[ExpressionOrSpreadElement],
492    ctx: &mut EvalContext,
493) -> ValueResult {
494    // Check if this is a simple identifier constructor (e.g., new String(), new Number())
495    // If so, try super-global constructor dispatch first
496    if let ExpressionType::ExpressionWhichCanBePattern(
497        ExpressionPatternType::Identifier(id)
498    ) = callee {
499        let ctor_name = &id.name;
500        
501        // Evaluate arguments first
502        let args = evaluate_arguments(arguments, ctx)?;
503        
504        // Try super-global constructor dispatch
505        let sg = ctx.super_global.clone();
506        let sg_result = sg.borrow().call_constructor(ctor_name, ctx, args.clone());
507        
508        if let Some(result) = sg_result {
509            return result;
510        }
511        
512        // Fall through to normal evaluation if super-global doesn't handle it
513    }
514    
515    // Normal constructor path: evaluate the callee to get the constructor function
516    let constructor = evaluate_expression(callee, ctx)?;
517
518    // Verify it's callable
519    let ctor_obj = match &constructor {
520        JsValue::Object(obj) => {
521            if !obj.borrow().is_callable() {
522                return Err(JErrorType::TypeError(format!(
523                    "{} is not a constructor",
524                    constructor
525                )));
526            }
527            obj.clone()
528        }
529        _ => {
530            return Err(JErrorType::TypeError(format!(
531                "{} is not a constructor",
532                constructor
533            )));
534        }
535    };
536
537    // Create a new object for 'this'
538    let new_obj = create_new_object_for_constructor(&ctor_obj)?;
539
540    // Evaluate the arguments
541    let args = evaluate_arguments(arguments, ctx)?;
542
543    // Call the constructor with the new object as 'this'
544    let result = call_value(&constructor, JsValue::Object(new_obj.clone()), args, ctx)?;
545
546    // If constructor returns an object, use that; otherwise use the new object
547    match result {
548        JsValue::Object(_) => Ok(result),
549        _ => Ok(JsValue::Object(new_obj)),
550    }
551}
552
553/// Create a new object for use in a constructor call.
554/// Sets up the prototype chain from the constructor's prototype property.
555fn create_new_object_for_constructor(
556    constructor: &JsObjectType,
557) -> Result<JsObjectType, JErrorType> {
558    use crate::runner::ds::object::{JsObject, ObjectType};
559    use crate::runner::ds::object_property::{PropertyDescriptor, PropertyKey};
560
561    // Create a new empty object
562    let mut new_obj = SimpleObject::new();
563
564    // Get the prototype from the constructor's 'prototype' property
565    let ctor_borrowed = constructor.borrow();
566    let prototype_key = PropertyKey::Str("prototype".to_string());
567
568    if let Some(PropertyDescriptor::Data(data)) =
569        ctor_borrowed.as_js_object().get_object_base().properties.get(&prototype_key)
570    {
571        if let JsValue::Object(proto_obj) = &data.value {
572            // Set the prototype field on ObjectBase (used by get_prototype_of)
573            new_obj.get_object_base_mut().prototype = Some(proto_obj.clone());
574        }
575    }
576
577    drop(ctor_borrowed);
578
579    let obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(new_obj))));
580    Ok(obj)
581}
582
583/// Get the 'this' value for a call expression.
584fn get_call_this_value(callee: &ExpressionOrSuper, ctx: &mut EvalContext) -> JsValue {
585    match callee {
586        ExpressionOrSuper::Expression(expr) => {
587            match expr.as_ref() {
588                // For member expressions, 'this' is the object
589                ExpressionType::MemberExpression(member) => {
590                    match member {
591                        MemberExpressionType::SimpleMemberExpression { object, .. } |
592                        MemberExpressionType::ComputedMemberExpression { object, .. } => {
593                            match object {
594                                ExpressionOrSuper::Expression(obj_expr) => {
595                                    evaluate_expression(obj_expr, ctx).unwrap_or(JsValue::Undefined)
596                                }
597                                ExpressionOrSuper::Super => JsValue::Undefined,
598                            }
599                        }
600                    }
601                }
602                // For other expressions, 'this' is undefined (or global in non-strict)
603                _ => ctx.global_this.clone().unwrap_or(JsValue::Undefined),
604            }
605        }
606        ExpressionOrSuper::Super => JsValue::Undefined,
607    }
608}
609
610/// Evaluate call arguments.
611fn evaluate_arguments(
612    arguments: &[ExpressionOrSpreadElement],
613    ctx: &mut EvalContext,
614) -> Result<Vec<JsValue>, JErrorType> {
615    let mut args = Vec::with_capacity(arguments.len());
616    for arg in arguments {
617        match arg {
618            ExpressionOrSpreadElement::Expression(expr) => {
619                args.push(evaluate_expression(expr, ctx)?);
620            }
621            ExpressionOrSpreadElement::SpreadElement(spread_expr) => {
622                // Evaluate the spread expression
623                let spread_value = evaluate_expression(spread_expr, ctx)?;
624
625                // If it's an array, spread its elements into args
626                if let JsValue::Object(spread_obj) = spread_value {
627                    let borrowed = spread_obj.borrow();
628                    let base = borrowed.as_js_object().get_object_base();
629
630                    // Get length property if it exists
631                    let length = if let Some(PropertyDescriptor::Data(prop)) =
632                        base.properties.get(&PropertyKey::Str("length".to_string()))
633                    {
634                        match &prop.value {
635                            JsValue::Number(JsNumberType::Integer(n)) => *n as usize,
636                            JsValue::Number(JsNumberType::Float(n)) => *n as usize,
637                            _ => 0,
638                        }
639                    } else {
640                        0
641                    };
642
643                    // Iterate over array indices and add each element to args
644                    for i in 0..length {
645                        let elem_key = PropertyKey::Str(i.to_string());
646                        let elem_value = if let Some(PropertyDescriptor::Data(prop)) =
647                            base.properties.get(&elem_key)
648                        {
649                            prop.value.clone()
650                        } else {
651                            JsValue::Undefined
652                        };
653                        args.push(elem_value);
654                    }
655                } else if let JsValue::String(s) = spread_value {
656                    // Spread string characters as separate args
657                    for ch in s.chars() {
658                        args.push(JsValue::String(ch.to_string()));
659                    }
660                } else {
661                    return Err(JErrorType::TypeError(
662                        "Spread requires an iterable".to_string(),
663                    ));
664                }
665            }
666        }
667    }
668    Ok(args)
669}
670
671/// Call a value as a function.
672fn call_value(
673    callee: &JsValue,
674    this_value: JsValue,
675    args: Vec<JsValue>,
676    ctx: &mut EvalContext,
677) -> ValueResult {
678    match callee {
679        JsValue::Object(obj) => {
680            let obj_ref = obj.borrow();
681            if obj_ref.is_callable() {
682                drop(obj_ref);
683                // For now, we'll call through our execution context stack
684                // This is a simplified implementation that doesn't fully support
685                // user-defined functions yet, but will work for native functions
686                // stored in NativeFunctionObject
687                call_function_object(obj, this_value, args, ctx)
688            } else {
689                Err(JErrorType::TypeError(format!(
690                    "{} is not a function",
691                    callee
692                )))
693            }
694        }
695        _ => Err(JErrorType::TypeError(format!(
696            "{} is not a function",
697            callee
698        ))),
699    }
700}
701
702/// Call a function object.
703pub fn call_function_object(
704    func: &crate::runner::ds::object::JsObjectType,
705    this_value: JsValue,
706    args: Vec<JsValue>,
707    ctx: &mut EvalContext,
708) -> ValueResult {
709    let func_ref = func.borrow();
710
711    match &*func_ref {
712        ObjectType::Function(func_obj) => {
713            // Get function metadata (these are Rc so cloning is cheap)
714            let body = func_obj.get_function_object_base().body_code.clone();
715            let params = func_obj.get_function_object_base().formal_parameters.clone();
716            let func_env = func_obj.get_function_object_base().environment.clone();
717
718            drop(func_ref);
719
720            // Create a new function scope
721            let saved_lex_env = ctx.lex_env.clone();
722            let saved_var_env = ctx.var_env.clone();
723
724            // Create new environment for the function, with the function's closure as outer
725            use crate::runner::ds::env_record::new_declarative_environment;
726            let func_scope = new_declarative_environment(Some(func_env));
727            ctx.lex_env = func_scope.clone();
728            ctx.var_env = func_scope;
729
730            // Bind parameters to arguments
731            for (i, param) in params.iter().enumerate() {
732                if let crate::parser::ast::PatternType::PatternWhichCanBeExpression(
733                    ExpressionPatternType::Identifier(id)
734                ) = param {
735                    let arg_value = args.get(i).cloned().unwrap_or(JsValue::Undefined);
736                    ctx.create_binding(&id.name, false)?;
737                    ctx.initialize_binding(&id.name, arg_value)?;
738                }
739            }
740
741            // Execute each statement in the function body
742            use super::statement::execute_statement;
743            use super::types::CompletionType;
744
745            let mut result_completion = super::types::Completion::normal();
746
747            for stmt in body.body.iter() {
748                let completion = execute_statement(stmt, ctx)?;
749
750                match completion.completion_type {
751                    CompletionType::Return => {
752                        result_completion = completion;
753                        break;
754                    }
755                    CompletionType::Throw => {
756                        // Restore environment before returning error
757                        ctx.lex_env = saved_lex_env;
758                        ctx.var_env = saved_var_env;
759                        return Err(JErrorType::TypeError(format!(
760                            "Uncaught exception: {:?}",
761                            completion.value
762                        )));
763                    }
764                    CompletionType::Break | CompletionType::Continue | CompletionType::Yield => {
765                        // These shouldn't escape function body
766                        result_completion = completion;
767                        break;
768                    }
769                    CompletionType::Normal => {
770                        result_completion = completion;
771                    }
772                }
773            }
774
775            // Restore the previous environment
776            ctx.lex_env = saved_lex_env;
777            ctx.var_env = saved_var_env;
778
779            // Return the result
780            match result_completion.completion_type {
781                CompletionType::Return => Ok(result_completion.get_value()),
782                _ => Ok(JsValue::Undefined),
783            }
784        }
785        ObjectType::Ordinary(obj) => {
786            // Check if it's a SimpleFunctionObject (has marker property)
787            let marker = PropertyKey::Str("__simple_function__".to_string());
788            let default_ctor_marker = PropertyKey::Str("__default_constructor__".to_string());
789
790            let generator_marker = PropertyKey::Str("__generator__".to_string());
791            let generator_next_marker = PropertyKey::Str("__generator_next__".to_string());
792
793            if obj.get_object_base().properties.contains_key(&default_ctor_marker) {
794                // It's a default constructor (no-op) - just return undefined
795                drop(func_ref);
796                Ok(JsValue::Undefined)
797            } else if let Some(PropertyDescriptor::Data(data)) = obj.get_object_base().properties.get(&generator_next_marker) {
798                // It's a generator next method - call the generator's .next()
799                let gen_obj = match &data.value {
800                    JsValue::Object(o) => o.clone(),
801                    _ => return Err(JErrorType::TypeError("Invalid generator reference".to_string())),
802                };
803                drop(func_ref);
804
805                // Call the generator's next method
806                let mut gen_borrowed = gen_obj.borrow_mut();
807
808                // We need to cast to GeneratorObject
809                match &mut *gen_borrowed {
810                    ObjectType::Ordinary(inner_obj) => {
811                        let gen_marker = PropertyKey::Str("__generator_object__".to_string());
812                        if inner_obj.get_object_base().properties.contains_key(&gen_marker) {
813                            // Cast to GeneratorObject
814                            let gen_ptr = inner_obj.as_mut() as *mut dyn JsObject as *mut GeneratorObject;
815                            drop(gen_borrowed);
816                            // SAFETY: We know this is a GeneratorObject
817                            unsafe {
818                                let gen = &mut *gen_ptr;
819                                gen.next(ctx)
820                            }
821                        } else {
822                            Err(JErrorType::TypeError("Not a generator object".to_string()))
823                        }
824                    }
825                    _ => Err(JErrorType::TypeError("Not a generator object".to_string())),
826                }
827            } else if obj.get_object_base().properties.contains_key(&generator_marker) {
828                // It's a generator function - create a GeneratorObject instead of executing
829                let obj_ptr = obj.as_ref() as *const dyn JsObject;
830                drop(func_ref);
831
832                // SAFETY: We know this is a SimpleFunctionObject and we've dropped func_ref
833                unsafe {
834                    let simple_func = &*(obj_ptr as *const SimpleFunctionObject);
835                    // Create generator object from function data
836                    create_generator_object(
837                        simple_func.body_ptr,
838                        simple_func.params_ptr,
839                        simple_func.environment.clone(),
840                        args,
841                        ctx,
842                    )
843                }
844            } else if obj.get_object_base().properties.contains_key(&marker) {
845                // It's a SimpleFunctionObject - use the call_with_this method
846                // Get a raw pointer to call call_with_this
847                let obj_ptr = obj.as_ref() as *const dyn JsObject;
848                drop(func_ref);
849
850                // SAFETY: We know this is a SimpleFunctionObject and we've dropped func_ref
851                unsafe {
852                    // Cast to SimpleFunctionObject
853                    let simple_func = &*(obj_ptr as *const SimpleFunctionObject);
854                    simple_func.call_with_this(this_value, args, ctx)
855                }
856            } else {
857                Err(JErrorType::TypeError("Object is not callable".to_string()))
858            }
859        }
860    }
861}
862
863
864/// Get a property from a value, calling getters if necessary.
865fn get_property_with_ctx(value: &JsValue, prop_name: &str, ctx: &mut EvalContext) -> ValueResult {
866    // Use the receiver version with value as both receiver and lookup target
867    get_property_with_receiver(value, value, prop_name, ctx)
868}
869
870/// Get a property, with separate receiver (for 'this') and lookup target.
871/// This handles prototype chain lookups where 'this' should be the original receiver.
872fn get_property_with_receiver(
873    receiver: &JsValue,
874    lookup_target: &JsValue,
875    prop_name: &str,
876    ctx: &mut EvalContext,
877) -> ValueResult {
878    match lookup_target {
879        JsValue::Object(obj) => {
880            // Check if this is a generator object and we're accessing 'next'
881            let is_gen_obj = {
882                let obj_ref = obj.borrow();
883                let marker = PropertyKey::Str("__generator_object__".to_string());
884                obj_ref.as_js_object().get_object_base().properties.contains_key(&marker)
885            };
886
887            if is_gen_obj && prop_name == "next" {
888                // Return a special "next" method that calls the generator's next
889                // We create a wrapper function that holds a reference to the generator
890                return create_generator_next_method(obj.clone());
891            }
892
893            let prop_key = PropertyKey::Str(prop_name.to_string());
894
895            // Check own property
896            let desc = {
897                let obj_ref = obj.borrow();
898                obj_ref.as_js_object().get_own_property(&prop_key)?.cloned()
899            };
900
901            if let Some(desc) = desc {
902                match desc {
903                    PropertyDescriptor::Data(data) => Ok(data.value.clone()),
904                    PropertyDescriptor::Accessor(accessor) => {
905                        // Call the getter if it exists, with RECEIVER as 'this'
906                        if let Some(getter) = accessor.get {
907                            call_accessor_function(&getter, receiver.clone(), vec![], ctx)
908                        } else {
909                            Ok(JsValue::Undefined)
910                        }
911                    }
912                }
913            } else {
914                // Check prototype chain - receiver stays the same
915                let proto = obj.borrow().as_js_object().get_prototype_of();
916                if let Some(proto) = proto {
917                    get_property_with_receiver(receiver, &JsValue::Object(proto), prop_name, ctx)
918                } else {
919                    Ok(JsValue::Undefined)
920                }
921            }
922        }
923        JsValue::String(s) => {
924            // String primitive property access
925            if prop_name == "length" {
926                Ok(JsValue::Number(JsNumberType::Integer(s.len() as i64)))
927            } else if let Ok(index) = prop_name.parse::<usize>() {
928                // Access character at index
929                if index < s.len() {
930                    Ok(JsValue::String(s.chars().nth(index).unwrap().to_string()))
931                } else {
932                    Ok(JsValue::Undefined)
933                }
934            } else {
935                // Other string methods not yet supported
936                Ok(JsValue::Undefined)
937            }
938        }
939        JsValue::Undefined => {
940            Err(JErrorType::TypeError("Cannot read property of undefined".to_string()))
941        }
942        JsValue::Null => {
943            Err(JErrorType::TypeError("Cannot read property of null".to_string()))
944        }
945        _ => {
946            // Primitive types - return undefined for now
947            Ok(JsValue::Undefined)
948        }
949    }
950}
951
952/// Call an accessor function (getter or setter).
953fn call_accessor_function(
954    func: &JsObjectType,
955    this_value: JsValue,
956    args: Vec<JsValue>,
957    ctx: &mut EvalContext,
958) -> ValueResult {
959    let func_ref = func.borrow();
960
961    // Check if it's our SimpleFunctionObject (stored as Ordinary with marker)
962    if let ObjectType::Ordinary(obj) = &*func_ref {
963        if is_simple_function_object(obj.as_ref()) {
964            // This is a SimpleFunctionObject - we need to call its method
965            // Since we can't downcast easily, we use unsafe to access it
966            // The object was created by create_function_object so we know it's a SimpleFunctionObject
967            let simple_func = unsafe {
968                // Get the raw pointer to the inner object and cast it
969                let ptr = obj.as_ref() as *const dyn JsObject as *const SimpleFunctionObject;
970                &*ptr
971            };
972            drop(func_ref);
973            return simple_func.call_with_this(this_value, args, ctx);
974        }
975
976        // Regular object - try to call as function (likely will fail)
977        drop(func_ref);
978        call_function_object(func, this_value, args, ctx)
979    } else if let ObjectType::Function(func_obj) = &*func_ref {
980        // It's a full function object
981        let body = func_obj.get_function_object_base().body_code.clone();
982        let params = func_obj.get_function_object_base().formal_parameters.clone();
983        let func_env = func_obj.get_function_object_base().environment.clone();
984
985        drop(func_ref);
986
987        call_function_with_body(body, params, func_env, this_value, args, ctx)
988    } else {
989        Err(JErrorType::TypeError("Not a function".to_string()))
990    }
991}
992
993/// Call a function given its body, parameters, and environment.
994fn call_function_with_body(
995    body: Rc<crate::parser::ast::FunctionBodyData>,
996    params: Rc<Vec<PatternType>>,
997    func_env: crate::runner::ds::lex_env::JsLexEnvironmentType,
998    this_value: JsValue,
999    args: Vec<JsValue>,
1000    ctx: &mut EvalContext,
1001) -> ValueResult {
1002    use crate::runner::ds::env_record::new_declarative_environment;
1003    use super::statement::execute_statement;
1004    use super::types::CompletionType;
1005
1006    // Save current environment
1007    let saved_lex_env = ctx.lex_env.clone();
1008    let saved_var_env = ctx.var_env.clone();
1009    let saved_this = ctx.global_this.clone();
1010
1011    // Create new environment for the function, with the function's closure as outer
1012    let func_scope = new_declarative_environment(Some(func_env));
1013    ctx.lex_env = func_scope.clone();
1014    ctx.var_env = func_scope;
1015    ctx.global_this = Some(this_value);
1016
1017    // Bind parameters to arguments
1018    for (i, param) in params.iter().enumerate() {
1019        if let PatternType::PatternWhichCanBeExpression(
1020            ExpressionPatternType::Identifier(id)
1021        ) = param {
1022            let arg_value = args.get(i).cloned().unwrap_or(JsValue::Undefined);
1023            ctx.create_binding(&id.name, false)?;
1024            ctx.initialize_binding(&id.name, arg_value)?;
1025        }
1026    }
1027
1028    // Execute each statement in the function body
1029    let mut result_completion = super::types::Completion::normal();
1030
1031    for stmt in body.body.iter() {
1032        let completion = execute_statement(stmt, ctx)?;
1033
1034        match completion.completion_type {
1035            CompletionType::Return => {
1036                result_completion = completion;
1037                break;
1038            }
1039            CompletionType::Throw => {
1040                // Restore environment before returning error
1041                ctx.lex_env = saved_lex_env;
1042                ctx.var_env = saved_var_env;
1043                ctx.global_this = saved_this;
1044                return Err(JErrorType::TypeError(format!(
1045                    "Uncaught exception: {:?}",
1046                    completion.value
1047                )));
1048            }
1049            CompletionType::Break | CompletionType::Continue | CompletionType::Yield => {
1050                result_completion = completion;
1051                break;
1052            }
1053            CompletionType::Normal => {
1054                result_completion = completion;
1055            }
1056        }
1057    }
1058
1059    // Restore the previous environment
1060    ctx.lex_env = saved_lex_env;
1061    ctx.var_env = saved_var_env;
1062    ctx.global_this = saved_this;
1063
1064    // Return the result
1065    match result_completion.completion_type {
1066        CompletionType::Return => Ok(result_completion.get_value()),
1067        _ => Ok(JsValue::Undefined),
1068    }
1069}
1070
1071/// Convert a value to a property key string.
1072fn to_property_key(value: &JsValue) -> String {
1073    to_string(value)
1074}
1075
1076/// Evaluate an assignment expression.
1077fn evaluate_assignment_expression(
1078    operator: &AssignmentOperator,
1079    left: &PatternOrExpression,
1080    right: &ExpressionType,
1081    ctx: &mut EvalContext,
1082) -> ValueResult {
1083    // Check if this is a member expression assignment
1084    if let PatternOrExpression::Expression(expr) = left {
1085        if let ExpressionType::MemberExpression(member) = expr.as_ref() {
1086            return evaluate_member_assignment(operator, member, right, ctx);
1087        }
1088    }
1089
1090    // Handle destructuring assignment patterns
1091    if let PatternOrExpression::Pattern(pattern) = left {
1092        if !matches!(
1093            &**pattern,
1094            PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(_))
1095        ) {
1096            if !matches!(operator, AssignmentOperator::Equals) {
1097                return Err(JErrorType::TypeError(
1098                    "Compound assignment not supported for destructuring patterns".to_string(),
1099                ));
1100            }
1101            let rhs_value = evaluate_expression(right, ctx)?;
1102            assign_pattern(pattern, rhs_value.clone(), ctx)?;
1103            return Ok(rhs_value);
1104        }
1105    }
1106
1107    // Get the name to assign to (simple variable assignment)
1108    let name = match left {
1109        PatternOrExpression::Pattern(pattern) => get_pattern_name(pattern)?,
1110        PatternOrExpression::Expression(expr) => get_expression_name(expr)?,
1111    };
1112
1113    // Evaluate the right-hand side
1114    let rhs_value = evaluate_expression(right, ctx)?;
1115
1116    // Compute the final value based on the operator
1117    let final_value = match operator {
1118        AssignmentOperator::Equals => rhs_value,
1119        AssignmentOperator::AddEquals => {
1120            let current = ctx.get_binding(&name)?;
1121            add_values(&current, &rhs_value)?
1122        }
1123        AssignmentOperator::SubtractEquals => {
1124            let current = ctx.get_binding(&name)?;
1125            subtract_values(&current, &rhs_value)?
1126        }
1127        AssignmentOperator::MultiplyEquals => {
1128            let current = ctx.get_binding(&name)?;
1129            multiply_values(&current, &rhs_value)?
1130        }
1131        AssignmentOperator::DivideEquals => {
1132            let current = ctx.get_binding(&name)?;
1133            divide_values(&current, &rhs_value)?
1134        }
1135        AssignmentOperator::ModuloEquals => {
1136            let current = ctx.get_binding(&name)?;
1137            modulo_values(&current, &rhs_value)?
1138        }
1139        AssignmentOperator::BitwiseLeftShiftEquals => {
1140            let current = ctx.get_binding(&name)?;
1141            left_shift(&current, &rhs_value)?
1142        }
1143        AssignmentOperator::BitwiseRightShiftEquals => {
1144            let current = ctx.get_binding(&name)?;
1145            right_shift(&current, &rhs_value)?
1146        }
1147        AssignmentOperator::BitwiseUnsignedRightShiftEquals => {
1148            let current = ctx.get_binding(&name)?;
1149            unsigned_right_shift(&current, &rhs_value)?
1150        }
1151        AssignmentOperator::BitwiseOrEquals => {
1152            let current = ctx.get_binding(&name)?;
1153            bitwise_or(&current, &rhs_value)?
1154        }
1155        AssignmentOperator::BitwiseAndEquals => {
1156            let current = ctx.get_binding(&name)?;
1157            bitwise_and(&current, &rhs_value)?
1158        }
1159        AssignmentOperator::BitwiseXorEquals => {
1160            let current = ctx.get_binding(&name)?;
1161            bitwise_xor(&current, &rhs_value)?
1162        }
1163    };
1164
1165    // Set the binding and return the value
1166    ctx.set_binding(&name, final_value.clone())?;
1167    Ok(final_value)
1168}
1169
1170/// Assign a value to a binding pattern (destructuring assignment).
1171fn assign_pattern(
1172    pattern: &PatternType,
1173    value: JsValue,
1174    ctx: &mut EvalContext,
1175) -> Result<(), JErrorType> {
1176    match pattern {
1177        PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(id)) => {
1178            ctx.set_binding(&id.name, value)?;
1179            Ok(())
1180        }
1181        PatternType::ObjectPattern { properties, .. } => {
1182            for prop in properties {
1183                let prop_data = &prop.0;
1184                let key_name = get_assignment_property_key(prop_data, ctx)?;
1185                let prop_value = get_object_property_value(&value, &key_name, ctx)?;
1186                assign_pattern(&prop_data.value, prop_value, ctx)?;
1187            }
1188            Ok(())
1189        }
1190        PatternType::ArrayPattern { elements, .. } => {
1191            for (index, element) in elements.iter().enumerate() {
1192                if let Some(elem_pattern) = element {
1193                    if let PatternType::RestElement { argument, .. } = elem_pattern.as_ref() {
1194                        let rest_value = get_rest_elements_for_assignment(&value, index, ctx)?;
1195                        assign_pattern(argument, rest_value, ctx)?;
1196                    } else {
1197                        let elem_value = get_array_element_for_assignment(&value, index, ctx)?;
1198                        assign_pattern(elem_pattern, elem_value, ctx)?;
1199                    }
1200                }
1201            }
1202            Ok(())
1203        }
1204        PatternType::AssignmentPattern { left, right, .. } => {
1205            let actual_value = if matches!(value, JsValue::Undefined) {
1206                evaluate_expression(right, ctx)?
1207            } else {
1208                value
1209            };
1210            assign_pattern(left, actual_value, ctx)
1211        }
1212        PatternType::RestElement { argument, .. } => assign_pattern(argument, value, ctx),
1213    }
1214}
1215
1216fn get_assignment_property_key(
1217    prop: &PropertyData<Box<PatternType>>,
1218    ctx: &mut EvalContext,
1219) -> Result<String, JErrorType> {
1220    if prop.computed {
1221        let key_value = evaluate_expression(prop.key.as_ref(), ctx)?;
1222        Ok(to_string(&key_value))
1223    } else {
1224        match prop.key.as_ref() {
1225            ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(id)) => {
1226                Ok(id.name.clone())
1227            }
1228            ExpressionType::Literal(lit_data) => match &lit_data.value {
1229                LiteralType::StringLiteral(s) => Ok(s.clone()),
1230                LiteralType::NumberLiteral(num) => {
1231                    use crate::parser::ast::NumberLiteralType;
1232                    match num {
1233                        NumberLiteralType::IntegerLiteral(n) => Ok(n.to_string()),
1234                        NumberLiteralType::FloatLiteral(n) => Ok(n.to_string()),
1235                    }
1236                }
1237                _ => Err(JErrorType::TypeError(
1238                    "Invalid property key in destructuring assignment".to_string(),
1239                )),
1240            },
1241            _ => Err(JErrorType::TypeError(
1242                "Invalid property key in destructuring assignment".to_string(),
1243            )),
1244        }
1245    }
1246}
1247
1248fn get_object_property_value(
1249    obj: &JsValue,
1250    key: &str,
1251    ctx: &mut EvalContext,
1252) -> Result<JsValue, JErrorType> {
1253    match obj {
1254        JsValue::Object(_) => get_property_with_ctx(obj, key, ctx),
1255        _ => Err(JErrorType::TypeError(
1256            "Cannot destructure non-object".to_string(),
1257        )),
1258    }
1259}
1260
1261fn get_array_element_for_assignment(
1262    arr: &JsValue,
1263    index: usize,
1264    ctx: &mut EvalContext,
1265) -> Result<JsValue, JErrorType> {
1266    match arr {
1267        JsValue::Object(_) => {
1268            let key = index.to_string();
1269            get_property_with_ctx(arr, &key, ctx)
1270        }
1271        _ => Err(JErrorType::TypeError(
1272            "Cannot destructure non-array".to_string(),
1273        )),
1274    }
1275}
1276
1277fn get_rest_elements_for_assignment(
1278    arr: &JsValue,
1279    start_index: usize,
1280    ctx: &mut EvalContext,
1281) -> Result<JsValue, JErrorType> {
1282    use crate::runner::ds::object::{JsObject, JsObjectType, ObjectType};
1283    use crate::runner::ds::object_property::{PropertyDescriptor, PropertyDescriptorData, PropertyKey};
1284    use std::cell::RefCell;
1285    use std::rc::Rc;
1286
1287    let length = match arr {
1288        JsValue::Object(_) => {
1289            let length_value = get_property_with_ctx(arr, "length", ctx)?;
1290            match length_value {
1291                JsValue::Number(JsNumberType::Integer(n)) => n.max(0) as usize,
1292                JsValue::Number(JsNumberType::Float(n)) => {
1293                    if n.is_nan() || n < 0.0 {
1294                        0
1295                    } else {
1296                        n as usize
1297                    }
1298                }
1299                _ => 0,
1300            }
1301        }
1302        _ => {
1303            return Err(JErrorType::TypeError(
1304                "Cannot use rest with non-array".to_string(),
1305            ))
1306        }
1307    };
1308
1309    let mut rest_obj = ctx.new_tracked_object()?;
1310    let mut rest_index = 0;
1311
1312    for i in start_index..length {
1313        let key = i.to_string();
1314        let value = get_property_with_ctx(arr, &key, ctx)?;
1315        rest_obj.get_object_base_mut().properties.insert(
1316            PropertyKey::Str(rest_index.to_string()),
1317            PropertyDescriptor::Data(PropertyDescriptorData {
1318                value,
1319                writable: true,
1320                enumerable: true,
1321                configurable: true,
1322            }),
1323        );
1324        rest_index += 1;
1325    }
1326
1327    rest_obj.get_object_base_mut().properties.insert(
1328        PropertyKey::Str("length".to_string()),
1329        PropertyDescriptor::Data(PropertyDescriptorData {
1330            value: JsValue::Number(JsNumberType::Integer(rest_index as i64)),
1331            writable: true,
1332            enumerable: false,
1333            configurable: false,
1334        }),
1335    );
1336
1337    let obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(rest_obj))));
1338    Ok(JsValue::Object(obj))
1339}
1340
1341/// Evaluate assignment to a member expression (obj.prop = value or obj[prop] = value).
1342fn evaluate_member_assignment(
1343    operator: &AssignmentOperator,
1344    member: &MemberExpressionType,
1345    right: &ExpressionType,
1346    ctx: &mut EvalContext,
1347) -> ValueResult {
1348    // Get the object and property key
1349    let (obj_value, prop_name) = match member {
1350        MemberExpressionType::SimpleMemberExpression { object, property, .. } => {
1351            let obj = evaluate_expression_or_super(object, ctx)?;
1352            (obj, property.name.clone())
1353        }
1354        MemberExpressionType::ComputedMemberExpression { object, property, .. } => {
1355            let obj = evaluate_expression_or_super(object, ctx)?;
1356            let prop_val = evaluate_expression(property.as_ref(), ctx)?;
1357            (obj, to_property_key(&prop_val))
1358        }
1359    };
1360
1361    // Evaluate the right-hand side
1362    let rhs_value = evaluate_expression(right, ctx)?;
1363
1364    // Compute the final value based on the operator
1365    let final_value = if matches!(operator, AssignmentOperator::Equals) {
1366        rhs_value
1367    } else {
1368        let current = get_property_with_ctx(&obj_value, &prop_name, ctx)?;
1369        match operator {
1370            AssignmentOperator::Equals => unreachable!(),
1371            AssignmentOperator::AddEquals => add_values(&current, &rhs_value)?,
1372            AssignmentOperator::SubtractEquals => subtract_values(&current, &rhs_value)?,
1373            AssignmentOperator::MultiplyEquals => multiply_values(&current, &rhs_value)?,
1374            AssignmentOperator::DivideEquals => divide_values(&current, &rhs_value)?,
1375            AssignmentOperator::ModuloEquals => modulo_values(&current, &rhs_value)?,
1376            AssignmentOperator::BitwiseLeftShiftEquals => left_shift(&current, &rhs_value)?,
1377            AssignmentOperator::BitwiseRightShiftEquals => right_shift(&current, &rhs_value)?,
1378            AssignmentOperator::BitwiseUnsignedRightShiftEquals => unsigned_right_shift(&current, &rhs_value)?,
1379            AssignmentOperator::BitwiseOrEquals => bitwise_or(&current, &rhs_value)?,
1380            AssignmentOperator::BitwiseAndEquals => bitwise_and(&current, &rhs_value)?,
1381            AssignmentOperator::BitwiseXorEquals => bitwise_xor(&current, &rhs_value)?,
1382        }
1383    };
1384
1385    // Set the property (with setter support)
1386    set_property_with_ctx(&obj_value, &prop_name, final_value.clone(), ctx)?;
1387    Ok(final_value)
1388}
1389
1390/// Set a property on an object, calling setters if necessary.
1391fn set_property_with_ctx(
1392    value: &JsValue,
1393    prop_name: &str,
1394    new_value: JsValue,
1395    ctx: &mut EvalContext,
1396) -> Result<(), JErrorType> {
1397    match value {
1398        JsValue::Object(obj) => {
1399            let prop_key = PropertyKey::Str(prop_name.to_string());
1400
1401            // Check for accessor property (setter)
1402            let desc = {
1403                let obj_ref = obj.borrow();
1404                obj_ref.as_js_object().get_own_property(&prop_key)?.cloned()
1405            };
1406
1407            if let Some(PropertyDescriptor::Accessor(accessor)) = desc {
1408                // Call the setter if it exists
1409                if let Some(setter) = accessor.set {
1410                    call_accessor_function(&setter, value.clone(), vec![new_value], ctx)?;
1411                    return Ok(());
1412                }
1413                // No setter - fall through to data property behavior
1414            }
1415
1416            // Set as data property
1417            let mut obj_mut = obj.borrow_mut();
1418            obj_mut.as_js_object_mut().get_object_base_mut().properties.insert(
1419                prop_key,
1420                PropertyDescriptor::Data(PropertyDescriptorData {
1421                    value: new_value,
1422                    writable: true,
1423                    enumerable: true,
1424                    configurable: true,
1425                }),
1426            );
1427            Ok(())
1428        }
1429        _ => Err(JErrorType::TypeError("Cannot set property on non-object".to_string())),
1430    }
1431}
1432
1433/// Get the name from a pattern (for simple identifier patterns).
1434fn get_pattern_name(pattern: &PatternType) -> Result<String, JErrorType> {
1435    match pattern {
1436        PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(id)) => {
1437            Ok(id.name.clone())
1438        }
1439        _ => Err(JErrorType::TypeError(
1440            "Complex patterns in assignment not yet supported".to_string(),
1441        )),
1442    }
1443}
1444
1445/// Get the name from an expression (for simple identifier expressions).
1446fn get_expression_name(expr: &ExpressionType) -> Result<String, JErrorType> {
1447    match expr {
1448        ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(id)) => {
1449            Ok(id.name.clone())
1450        }
1451        _ => Err(JErrorType::TypeError(
1452            "Assignment to non-identifier expressions not yet supported".to_string(),
1453        )),
1454    }
1455}
1456
1457/// Evaluate a unary expression.
1458fn evaluate_unary_expression(
1459    operator: &UnaryOperator,
1460    argument: &ExpressionType,
1461    ctx: &mut EvalContext,
1462) -> ValueResult {
1463    match operator {
1464        UnaryOperator::TypeOf => {
1465            let value = evaluate_expression(argument, ctx).unwrap_or(JsValue::Undefined);
1466            Ok(JsValue::String(get_typeof_string(&value)))
1467        }
1468        UnaryOperator::Void => {
1469            let _ = evaluate_expression(argument, ctx)?;
1470            Ok(JsValue::Undefined)
1471        }
1472        UnaryOperator::LogicalNot => {
1473            let value = evaluate_expression(argument, ctx)?;
1474            Ok(JsValue::Boolean(!to_boolean(&value)))
1475        }
1476        UnaryOperator::Minus => {
1477            let value = evaluate_expression(argument, ctx)?;
1478            negate_number(&value)
1479        }
1480        UnaryOperator::Plus => {
1481            let value = evaluate_expression(argument, ctx)?;
1482            to_number(&value)
1483        }
1484        UnaryOperator::BitwiseNot => {
1485            let value = evaluate_expression(argument, ctx)?;
1486            bitwise_not(&value)
1487        }
1488        UnaryOperator::Delete => {
1489            match argument {
1490                ExpressionType::MemberExpression(member) => {
1491                    match member {
1492                        MemberExpressionType::SimpleMemberExpression { object, property, .. } => {
1493                            let obj_value = evaluate_expression_or_super(object, ctx)?;
1494                            match obj_value {
1495                                JsValue::Object(obj) => {
1496                                    let prop_key = PropertyKey::Str(property.name.clone());
1497                                    let result = obj.borrow_mut().as_js_object_mut().delete(&prop_key)?;
1498                                    Ok(JsValue::Boolean(result))
1499                                }
1500                                _ => Ok(JsValue::Boolean(true))
1501                            }
1502                        }
1503                        MemberExpressionType::ComputedMemberExpression { object, property, .. } => {
1504                            let obj_value = evaluate_expression_or_super(object, ctx)?;
1505                            let prop_value = evaluate_expression(property.as_ref(), ctx)?;
1506                            let prop_name = to_property_key(&prop_value);
1507                            match obj_value {
1508                                JsValue::Object(obj) => {
1509                                    let prop_key = PropertyKey::Str(prop_name);
1510                                    let result = obj.borrow_mut().as_js_object_mut().delete(&prop_key)?;
1511                                    Ok(JsValue::Boolean(result))
1512                                }
1513                                _ => Ok(JsValue::Boolean(true))
1514                            }
1515                        }
1516                    }
1517                }
1518                _ => {
1519                    // Non-member: evaluate for side effects, return true
1520                    let _ = evaluate_expression(argument, ctx)?;
1521                    Ok(JsValue::Boolean(true))
1522                }
1523            }
1524        }
1525    }
1526}
1527
1528/// Evaluate a binary expression.
1529fn evaluate_binary_expression(
1530    operator: &BinaryOperator,
1531    left: &ExpressionType,
1532    right: &ExpressionType,
1533    ctx: &mut EvalContext,
1534) -> ValueResult {
1535    let left_val = evaluate_expression(left, ctx)?;
1536    let right_val = evaluate_expression(right, ctx)?;
1537
1538    match operator {
1539        // Arithmetic
1540        BinaryOperator::Add => add_values(&left_val, &right_val),
1541        BinaryOperator::Subtract => subtract_values(&left_val, &right_val),
1542        BinaryOperator::Multiply => multiply_values(&left_val, &right_val),
1543        BinaryOperator::Divide => divide_values(&left_val, &right_val),
1544        BinaryOperator::Modulo => modulo_values(&left_val, &right_val),
1545
1546        // Comparison
1547        BinaryOperator::LessThan => compare_values(&left_val, &right_val, |a, b| a < b),
1548        BinaryOperator::GreaterThan => compare_values(&left_val, &right_val, |a, b| a > b),
1549        BinaryOperator::LessThanEqual => compare_values(&left_val, &right_val, |a, b| a <= b),
1550        BinaryOperator::GreaterThanEqual => compare_values(&left_val, &right_val, |a, b| a >= b),
1551
1552        // Equality
1553        BinaryOperator::StrictlyEqual => Ok(JsValue::Boolean(strict_equality(&left_val, &right_val))),
1554        BinaryOperator::StrictlyUnequal => Ok(JsValue::Boolean(!strict_equality(&left_val, &right_val))),
1555        BinaryOperator::LooselyEqual => Ok(JsValue::Boolean(loose_equality(&left_val, &right_val))),
1556        BinaryOperator::LooselyUnequal => Ok(JsValue::Boolean(!loose_equality(&left_val, &right_val))),
1557
1558        // Bitwise
1559        BinaryOperator::BitwiseAnd => bitwise_and(&left_val, &right_val),
1560        BinaryOperator::BitwiseOr => bitwise_or(&left_val, &right_val),
1561        BinaryOperator::BitwiseXor => bitwise_xor(&left_val, &right_val),
1562        BinaryOperator::BitwiseLeftShift => left_shift(&left_val, &right_val),
1563        BinaryOperator::BitwiseRightShift => right_shift(&left_val, &right_val),
1564        BinaryOperator::BitwiseUnsignedRightShift => unsigned_right_shift(&left_val, &right_val),
1565
1566        // Other
1567        BinaryOperator::InstanceOf => {
1568            // Left must be an object for instanceof to potentially return true
1569            let left_obj = match &left_val {
1570                JsValue::Object(obj) => obj.clone(),
1571                _ => return Ok(JsValue::Boolean(false)),
1572            };
1573
1574            // Right must be an object (constructor function) with a prototype property
1575            let right_obj = match &right_val {
1576                JsValue::Object(obj) => obj.clone(),
1577                _ => return Err(JErrorType::TypeError("Right-hand side of 'instanceof' is not an object".to_string())),
1578            };
1579
1580            // Get the prototype property from the right-hand side (constructor.prototype)
1581            let proto_key = PropertyKey::Str("prototype".to_string());
1582            let right_prototype = {
1583                let right_borrowed = right_obj.borrow();
1584                if let Some(desc) = right_borrowed.as_js_object().get_own_property(&proto_key)? {
1585                    match desc {
1586                        PropertyDescriptor::Data(data) => {
1587                            match &data.value {
1588                                JsValue::Object(p) => Some(p.clone()),
1589                                _ => None,
1590                            }
1591                        }
1592                        _ => None,
1593                    }
1594                } else {
1595                    None
1596                }
1597            };
1598
1599            // If right.prototype is not an object, return false
1600            let target_proto = match right_prototype {
1601                Some(p) => p,
1602                None => return Ok(JsValue::Boolean(false)),
1603            };
1604
1605            // Walk the prototype chain of left
1606            let mut current_proto = left_obj.borrow().as_js_object().get_prototype_of();
1607
1608            while let Some(proto) = current_proto {
1609                // Compare by reference
1610                if Rc::ptr_eq(&proto, &target_proto) {
1611                    return Ok(JsValue::Boolean(true));
1612                }
1613                current_proto = proto.borrow().as_js_object().get_prototype_of();
1614            }
1615
1616            Ok(JsValue::Boolean(false))
1617        }
1618        BinaryOperator::In => {
1619            let prop_key = PropertyKey::Str(to_property_key(&left_val));
1620            match &right_val {
1621                JsValue::Object(obj) => {
1622                    let has = obj.borrow().as_js_object().has_property(&prop_key);
1623                    Ok(JsValue::Boolean(has))
1624                }
1625                _ => Err(JErrorType::TypeError("Cannot use 'in' operator with non-object".to_string()))
1626            }
1627        }
1628    }
1629}
1630
1631/// Evaluate a logical expression with short-circuit evaluation.
1632fn evaluate_logical_expression(
1633    operator: &LogicalOperator,
1634    left: &ExpressionType,
1635    right: &ExpressionType,
1636    ctx: &mut EvalContext,
1637) -> ValueResult {
1638    let left_val = evaluate_expression(left, ctx)?;
1639
1640    match operator {
1641        LogicalOperator::And => {
1642            if !to_boolean(&left_val) {
1643                Ok(left_val)
1644            } else {
1645                evaluate_expression(right, ctx)
1646            }
1647        }
1648        LogicalOperator::Or => {
1649            if to_boolean(&left_val) {
1650                Ok(left_val)
1651            } else {
1652                evaluate_expression(right, ctx)
1653            }
1654        }
1655    }
1656}
1657
1658/// Evaluate a conditional (ternary) expression.
1659fn evaluate_conditional_expression(
1660    test: &ExpressionType,
1661    consequent: &ExpressionType,
1662    alternate: &ExpressionType,
1663    ctx: &mut EvalContext,
1664) -> ValueResult {
1665    let test_val = evaluate_expression(test, ctx)?;
1666
1667    if to_boolean(&test_val) {
1668        evaluate_expression(consequent, ctx)
1669    } else {
1670        evaluate_expression(alternate, ctx)
1671    }
1672}
1673
1674/// Evaluate a sequence expression.
1675fn evaluate_sequence_expression(
1676    expressions: &[Box<ExpressionType>],
1677    ctx: &mut EvalContext,
1678) -> ValueResult {
1679    let mut result = JsValue::Undefined;
1680    for expr in expressions {
1681        result = evaluate_expression(expr.as_ref(), ctx)?;
1682    }
1683    Ok(result)
1684}
1685
1686/// Evaluate an update expression (++x, x++, --x, x--).
1687fn evaluate_update_expression(
1688    operator: &UpdateOperator,
1689    argument: &ExpressionType,
1690    prefix: bool,
1691    ctx: &mut EvalContext,
1692) -> ValueResult {
1693    // Get the variable name from the argument
1694    let name = get_expression_name(argument)?;
1695
1696    // Get the current value
1697    let current_value = ctx.get_binding(&name)?;
1698
1699    // Convert to number
1700    let old_number = to_number(&current_value)?;
1701    let old_f64 = match &old_number {
1702        JsValue::Number(n) => number_to_f64(n),
1703        _ => f64::NAN,
1704    };
1705
1706    // Compute the new value based on operator
1707    let new_f64 = match operator {
1708        UpdateOperator::PlusPlus => old_f64 + 1.0,
1709        UpdateOperator::MinusMinus => old_f64 - 1.0,
1710    };
1711
1712    // Create the new JsValue
1713    let new_value = if new_f64.fract() == 0.0 && new_f64.abs() < i64::MAX as f64 {
1714        JsValue::Number(JsNumberType::Integer(new_f64 as i64))
1715    } else {
1716        JsValue::Number(JsNumberType::Float(new_f64))
1717    };
1718
1719    // Store the new value
1720    ctx.set_binding(&name, new_value.clone())?;
1721
1722    // Return old value for postfix, new value for prefix
1723    if prefix {
1724        Ok(new_value)
1725    } else {
1726        Ok(old_number)
1727    }
1728}
1729
1730// ============================================================================
1731// Type conversion helpers
1732// ============================================================================
1733
1734/// Convert a value to boolean.
1735pub fn to_boolean(value: &JsValue) -> bool {
1736    match value {
1737        JsValue::Undefined => false,
1738        JsValue::Null => false,
1739        JsValue::Boolean(b) => *b,
1740        JsValue::Number(n) => match n {
1741            JsNumberType::Integer(0) => false,
1742            JsNumberType::Float(f) if *f == 0.0 || f.is_nan() => false,
1743            JsNumberType::NaN => false,
1744            _ => true,
1745        },
1746        JsValue::String(s) => !s.is_empty(),
1747        JsValue::Symbol(_) => true,
1748        JsValue::Object(_) => true,
1749    }
1750}
1751
1752/// Get the typeof string for a value.
1753fn get_typeof_string(value: &JsValue) -> String {
1754    match value {
1755        JsValue::Undefined => "undefined".to_string(),
1756        JsValue::Null => "object".to_string(),
1757        JsValue::Boolean(_) => "boolean".to_string(),
1758        JsValue::Number(_) => "number".to_string(),
1759        JsValue::String(_) => "string".to_string(),
1760        JsValue::Symbol(_) => "symbol".to_string(),
1761        JsValue::Object(_) => "object".to_string(),
1762    }
1763}
1764
1765/// Convert a value to a number.
1766fn to_number(value: &JsValue) -> ValueResult {
1767    Ok(match value {
1768        JsValue::Undefined => JsValue::Number(JsNumberType::NaN),
1769        JsValue::Null => JsValue::Number(JsNumberType::Integer(0)),
1770        JsValue::Boolean(true) => JsValue::Number(JsNumberType::Integer(1)),
1771        JsValue::Boolean(false) => JsValue::Number(JsNumberType::Integer(0)),
1772        JsValue::Number(n) => JsValue::Number(n.clone()),
1773        JsValue::String(s) => {
1774            if s.is_empty() {
1775                JsValue::Number(JsNumberType::Integer(0))
1776            } else if let Ok(i) = s.trim().parse::<i64>() {
1777                JsValue::Number(JsNumberType::Integer(i))
1778            } else if let Ok(f) = s.trim().parse::<f64>() {
1779                JsValue::Number(JsNumberType::Float(f))
1780            } else {
1781                JsValue::Number(JsNumberType::NaN)
1782            }
1783        }
1784        JsValue::Symbol(_) => {
1785            return Err(JErrorType::TypeError("Cannot convert Symbol to number".to_string()));
1786        }
1787        JsValue::Object(_) => JsValue::Number(JsNumberType::NaN),
1788    })
1789}
1790
1791/// Negate a number value.
1792fn negate_number(value: &JsValue) -> ValueResult {
1793    let num_value = to_number(value)?;
1794    Ok(match num_value {
1795        JsValue::Number(JsNumberType::Integer(i)) => JsValue::Number(JsNumberType::Integer(-i)),
1796        JsValue::Number(JsNumberType::Float(f)) => JsValue::Number(JsNumberType::Float(-f)),
1797        JsValue::Number(JsNumberType::PositiveInfinity) => JsValue::Number(JsNumberType::NegativeInfinity),
1798        JsValue::Number(JsNumberType::NegativeInfinity) => JsValue::Number(JsNumberType::PositiveInfinity),
1799        JsValue::Number(JsNumberType::NaN) => JsValue::Number(JsNumberType::NaN),
1800        _ => JsValue::Number(JsNumberType::NaN),
1801    })
1802}
1803
1804/// Bitwise NOT operation.
1805fn bitwise_not(value: &JsValue) -> ValueResult {
1806    let num = to_i32(value)?;
1807    Ok(JsValue::Number(JsNumberType::Integer(!num as i64)))
1808}
1809
1810/// Convert to i32 for bitwise operations.
1811fn to_i32(value: &JsValue) -> Result<i32, JErrorType> {
1812    match to_number(value)? {
1813        JsValue::Number(JsNumberType::Integer(i)) => Ok(i as i32),
1814        JsValue::Number(JsNumberType::Float(f)) => Ok(f as i32),
1815        JsValue::Number(JsNumberType::NaN) => Ok(0),
1816        JsValue::Number(JsNumberType::PositiveInfinity) => Ok(0),
1817        JsValue::Number(JsNumberType::NegativeInfinity) => Ok(0),
1818        _ => Ok(0),
1819    }
1820}
1821
1822/// Convert to u32 for unsigned bitwise operations.
1823fn to_u32(value: &JsValue) -> Result<u32, JErrorType> {
1824    Ok(to_i32(value)? as u32)
1825}
1826
1827// ============================================================================
1828// Arithmetic operations
1829// ============================================================================
1830
1831fn add_values(left: &JsValue, right: &JsValue) -> ValueResult {
1832    if matches!(left, JsValue::String(_)) || matches!(right, JsValue::String(_)) {
1833        let left_str = to_string(left);
1834        let right_str = to_string(right);
1835        return Ok(JsValue::String(format!("{}{}", left_str, right_str)));
1836    }
1837
1838    let left_num = to_number(left)?;
1839    let right_num = to_number(right)?;
1840    apply_numeric_op(&left_num, &right_num, |a, b| a + b, |a, b| a + b)
1841}
1842
1843fn subtract_values(left: &JsValue, right: &JsValue) -> ValueResult {
1844    let left_num = to_number(left)?;
1845    let right_num = to_number(right)?;
1846    apply_numeric_op(&left_num, &right_num, |a, b| a - b, |a, b| a - b)
1847}
1848
1849fn multiply_values(left: &JsValue, right: &JsValue) -> ValueResult {
1850    let left_num = to_number(left)?;
1851    let right_num = to_number(right)?;
1852    apply_numeric_op(&left_num, &right_num, |a, b| a * b, |a, b| a * b)
1853}
1854
1855fn divide_values(left: &JsValue, right: &JsValue) -> ValueResult {
1856    let left_num = to_number(left)?;
1857    let right_num = to_number(right)?;
1858
1859    if matches!(right_num, JsValue::Number(JsNumberType::Integer(0)))
1860        || matches!(right_num, JsValue::Number(JsNumberType::Float(f)) if f == 0.0)
1861    {
1862        let left_f = match &left_num {
1863            JsValue::Number(n) => number_to_f64(n),
1864            _ => f64::NAN,
1865        };
1866        return Ok(if left_f.is_nan() || left_f == 0.0 {
1867            JsValue::Number(JsNumberType::NaN)
1868        } else if left_f > 0.0 {
1869            JsValue::Number(JsNumberType::PositiveInfinity)
1870        } else {
1871            JsValue::Number(JsNumberType::NegativeInfinity)
1872        });
1873    }
1874
1875    apply_numeric_op(&left_num, &right_num, |a, b| a / b, |a, b| a / b)
1876}
1877
1878fn modulo_values(left: &JsValue, right: &JsValue) -> ValueResult {
1879    let left_num = to_number(left)?;
1880    let right_num = to_number(right)?;
1881    apply_numeric_op(&left_num, &right_num, |a, b| a % b, |a, b| a % b)
1882}
1883
1884fn apply_numeric_op<F, G>(left: &JsValue, right: &JsValue, int_op: F, float_op: G) -> ValueResult
1885where
1886    F: Fn(i64, i64) -> i64,
1887    G: Fn(f64, f64) -> f64,
1888{
1889    match (left, right) {
1890        (JsValue::Number(JsNumberType::NaN), _) | (_, JsValue::Number(JsNumberType::NaN)) => {
1891            Ok(JsValue::Number(JsNumberType::NaN))
1892        }
1893        (JsValue::Number(JsNumberType::Integer(a)), JsValue::Number(JsNumberType::Integer(b))) => {
1894            Ok(JsValue::Number(JsNumberType::Integer(int_op(*a, *b))))
1895        }
1896        (JsValue::Number(a), JsValue::Number(b)) => {
1897            let a_f64 = number_to_f64(a);
1898            let b_f64 = number_to_f64(b);
1899            Ok(JsValue::Number(JsNumberType::Float(float_op(a_f64, b_f64))))
1900        }
1901        _ => Ok(JsValue::Number(JsNumberType::NaN)),
1902    }
1903}
1904
1905fn number_to_f64(n: &JsNumberType) -> f64 {
1906    match n {
1907        JsNumberType::Integer(i) => *i as f64,
1908        JsNumberType::Float(f) => *f,
1909        JsNumberType::NaN => f64::NAN,
1910        JsNumberType::PositiveInfinity => f64::INFINITY,
1911        JsNumberType::NegativeInfinity => f64::NEG_INFINITY,
1912    }
1913}
1914
1915// ============================================================================
1916// Comparison operations
1917// ============================================================================
1918
1919fn compare_values<F>(left: &JsValue, right: &JsValue, cmp: F) -> ValueResult
1920where
1921    F: Fn(f64, f64) -> bool,
1922{
1923    let left_num = to_number(left)?;
1924    let right_num = to_number(right)?;
1925
1926    let result = match (&left_num, &right_num) {
1927        (JsValue::Number(JsNumberType::NaN), _) | (_, JsValue::Number(JsNumberType::NaN)) => false,
1928        (JsValue::Number(a), JsValue::Number(b)) => {
1929            cmp(number_to_f64(a), number_to_f64(b))
1930        }
1931        _ => false,
1932    };
1933
1934    Ok(JsValue::Boolean(result))
1935}
1936
1937pub fn strict_equality(left: &JsValue, right: &JsValue) -> bool {
1938    match (left, right) {
1939        (JsValue::Undefined, JsValue::Undefined) => true,
1940        (JsValue::Null, JsValue::Null) => true,
1941        (JsValue::Boolean(a), JsValue::Boolean(b)) => a == b,
1942        (JsValue::String(a), JsValue::String(b)) => a == b,
1943        (JsValue::Number(JsNumberType::NaN), _) | (_, JsValue::Number(JsNumberType::NaN)) => false,
1944        (JsValue::Number(a), JsValue::Number(b)) => {
1945            number_to_f64(a) == number_to_f64(b)
1946        }
1947        (JsValue::Object(a), JsValue::Object(b)) => std::rc::Rc::ptr_eq(a, b),
1948        _ => false,
1949    }
1950}
1951
1952fn loose_equality(left: &JsValue, right: &JsValue) -> bool {
1953    if std::mem::discriminant(left) == std::mem::discriminant(right) {
1954        return strict_equality(left, right);
1955    }
1956
1957    match (left, right) {
1958        (JsValue::Null, JsValue::Undefined) | (JsValue::Undefined, JsValue::Null) => true,
1959        (JsValue::Number(_), JsValue::String(_)) => {
1960            if let Ok(r) = to_number(right) {
1961                strict_equality(left, &r)
1962            } else {
1963                false
1964            }
1965        }
1966        (JsValue::String(_), JsValue::Number(_)) => {
1967            if let Ok(l) = to_number(left) {
1968                strict_equality(&l, right)
1969            } else {
1970                false
1971            }
1972        }
1973        (JsValue::Boolean(_), _) => {
1974            if let Ok(l) = to_number(left) {
1975                loose_equality(&l, right)
1976            } else {
1977                false
1978            }
1979        }
1980        (_, JsValue::Boolean(_)) => {
1981            if let Ok(r) = to_number(right) {
1982                loose_equality(left, &r)
1983            } else {
1984                false
1985            }
1986        }
1987        _ => false,
1988    }
1989}
1990
1991// ============================================================================
1992// Bitwise operations
1993// ============================================================================
1994
1995fn bitwise_and(left: &JsValue, right: &JsValue) -> ValueResult {
1996    let l = to_i32(left)?;
1997    let r = to_i32(right)?;
1998    Ok(JsValue::Number(JsNumberType::Integer((l & r) as i64)))
1999}
2000
2001fn bitwise_or(left: &JsValue, right: &JsValue) -> ValueResult {
2002    let l = to_i32(left)?;
2003    let r = to_i32(right)?;
2004    Ok(JsValue::Number(JsNumberType::Integer((l | r) as i64)))
2005}
2006
2007fn bitwise_xor(left: &JsValue, right: &JsValue) -> ValueResult {
2008    let l = to_i32(left)?;
2009    let r = to_i32(right)?;
2010    Ok(JsValue::Number(JsNumberType::Integer((l ^ r) as i64)))
2011}
2012
2013fn left_shift(left: &JsValue, right: &JsValue) -> ValueResult {
2014    let l = to_i32(left)?;
2015    let r = to_u32(right)? & 0x1f;
2016    Ok(JsValue::Number(JsNumberType::Integer((l << r) as i64)))
2017}
2018
2019fn right_shift(left: &JsValue, right: &JsValue) -> ValueResult {
2020    let l = to_i32(left)?;
2021    let r = to_u32(right)? & 0x1f;
2022    Ok(JsValue::Number(JsNumberType::Integer((l >> r) as i64)))
2023}
2024
2025fn unsigned_right_shift(left: &JsValue, right: &JsValue) -> ValueResult {
2026    let l = to_u32(left)?;
2027    let r = to_u32(right)? & 0x1f;
2028    Ok(JsValue::Number(JsNumberType::Integer((l >> r) as i64)))
2029}
2030
2031// ============================================================================
2032// String conversion
2033// ============================================================================
2034
2035fn to_string(value: &JsValue) -> String {
2036    match value {
2037        JsValue::Undefined => "undefined".to_string(),
2038        JsValue::Null => "null".to_string(),
2039        JsValue::Boolean(true) => "true".to_string(),
2040        JsValue::Boolean(false) => "false".to_string(),
2041        JsValue::Number(n) => n.to_string(),
2042        JsValue::String(s) => s.clone(),
2043        JsValue::Symbol(_) => "[Symbol]".to_string(),
2044        JsValue::Object(_) => "[object Object]".to_string(),
2045    }
2046}
2047
2048// ============================================================================
2049// Function object creation
2050// ============================================================================
2051
2052/// Simple function object that stores function metadata for evaluation.
2053/// Instead of storing AST data directly, we store references to the source AST.
2054pub struct SimpleFunctionObject {
2055    base: ObjectBase,
2056    /// Pointer to the function body (using raw pointer for simplicity)
2057    body_ptr: *const crate::parser::ast::FunctionBodyData,
2058    /// Pointer to the formal parameters
2059    params_ptr: *const Vec<PatternType>,
2060    /// The lexical environment at the time the function was created
2061    environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
2062}
2063
2064// Safety: SimpleFunctionObject is only used within a single thread
2065unsafe impl Send for SimpleFunctionObject {}
2066unsafe impl Sync for SimpleFunctionObject {}
2067
2068impl SimpleFunctionObject {
2069    pub fn new(
2070        body_ptr: *const crate::parser::ast::FunctionBodyData,
2071        params_ptr: *const Vec<PatternType>,
2072        environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
2073    ) -> Self {
2074        SimpleFunctionObject {
2075            base: ObjectBase::new(),
2076            body_ptr,
2077            params_ptr,
2078            environment,
2079        }
2080    }
2081
2082    /// Call this function with the given this value and arguments.
2083    /// Safety: The AST pointers must still be valid.
2084    pub fn call_with_this(
2085        &self,
2086        this_value: JsValue,
2087        args: Vec<JsValue>,
2088        ctx: &mut EvalContext,
2089    ) -> ValueResult {
2090        use crate::runner::ds::env_record::new_declarative_environment;
2091        use super::statement::execute_statement;
2092        use super::types::CompletionType;
2093
2094        // Get the body and params (unsafe dereference)
2095        let (body, params) = unsafe {
2096            (&*self.body_ptr, &*self.params_ptr)
2097        };
2098
2099        // Save current environment
2100        let saved_lex_env = ctx.lex_env.clone();
2101        let saved_var_env = ctx.var_env.clone();
2102        let saved_this = ctx.global_this.clone();
2103
2104        // Create new environment for the function, with the function's closure as outer
2105        let func_scope = new_declarative_environment(Some(self.environment.clone()));
2106        ctx.lex_env = func_scope.clone();
2107        ctx.var_env = func_scope;
2108        ctx.global_this = Some(this_value);
2109
2110        // Bind parameters to arguments
2111        for (i, param) in params.iter().enumerate() {
2112            if let PatternType::PatternWhichCanBeExpression(
2113                ExpressionPatternType::Identifier(id)
2114            ) = param {
2115                let arg_value = args.get(i).cloned().unwrap_or(JsValue::Undefined);
2116                ctx.create_binding(&id.name, false)?;
2117                ctx.initialize_binding(&id.name, arg_value)?;
2118            }
2119        }
2120
2121        // Execute each statement in the function body
2122        let mut result_completion = super::types::Completion::normal();
2123
2124        for stmt in body.body.iter() {
2125            let completion = execute_statement(stmt, ctx)?;
2126
2127            match completion.completion_type {
2128                CompletionType::Return => {
2129                    result_completion = completion;
2130                    break;
2131                }
2132                CompletionType::Throw => {
2133                    // Restore environment before returning error
2134                    ctx.lex_env = saved_lex_env;
2135                    ctx.var_env = saved_var_env;
2136                    ctx.global_this = saved_this;
2137                    return Err(JErrorType::TypeError(format!(
2138                        "Uncaught exception: {:?}",
2139                        completion.value
2140                    )));
2141                }
2142                CompletionType::Break | CompletionType::Continue | CompletionType::Yield => {
2143                    result_completion = completion;
2144                    break;
2145                }
2146                CompletionType::Normal => {
2147                    result_completion = completion;
2148                }
2149            }
2150        }
2151
2152        // Restore the previous environment
2153        ctx.lex_env = saved_lex_env;
2154        ctx.var_env = saved_var_env;
2155        ctx.global_this = saved_this;
2156
2157        // Return the result
2158        match result_completion.completion_type {
2159            CompletionType::Return => Ok(result_completion.get_value()),
2160            _ => Ok(JsValue::Undefined),
2161        }
2162    }
2163}
2164
2165impl JsObject for SimpleFunctionObject {
2166    fn get_object_base_mut(&mut self) -> &mut ObjectBase {
2167        &mut self.base
2168    }
2169
2170    fn get_object_base(&self) -> &ObjectBase {
2171        &self.base
2172    }
2173
2174    fn as_js_object(&self) -> &dyn JsObject {
2175        self
2176    }
2177
2178    fn as_js_object_mut(&mut self) -> &mut dyn JsObject {
2179        self
2180    }
2181}
2182
2183/// Type ID for SimpleFunctionObject - used for downcasting
2184pub fn is_simple_function_object(obj: &dyn JsObject) -> bool {
2185    // Check if the object has a special marker property
2186    let marker = PropertyKey::Str("__simple_function__".to_string());
2187    obj.get_object_base().properties.contains_key(&marker)
2188}
2189
2190/// Create a function object from FunctionData.
2191/// Note: This creates a closure that references the AST. The AST must remain valid
2192/// for the lifetime of the function object.
2193pub fn create_function_object(func_data: &FunctionData, ctx: &EvalContext) -> ValueResult {
2194    let body_ptr = func_data.body.as_ref() as *const _;
2195    let params_ptr = &func_data.params.list as *const _;
2196    let environment = ctx.lex_env.clone();
2197
2198    let mut func_obj = SimpleFunctionObject::new(body_ptr, params_ptr, environment);
2199
2200    // Create a prototype object for this function
2201    let prototype = SimpleObject::new();
2202    let prototype_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(prototype))));
2203
2204    // Store prototype on the function object
2205    func_obj.base.properties.insert(
2206        PropertyKey::Str("prototype".to_string()),
2207        PropertyDescriptor::Data(PropertyDescriptorData {
2208            value: JsValue::Object(prototype_ref),
2209            writable: true,
2210            enumerable: false,
2211            configurable: false,
2212        }),
2213    );
2214
2215    // Add marker property to identify this as a SimpleFunctionObject
2216    func_obj.base.properties.insert(
2217        PropertyKey::Str("__simple_function__".to_string()),
2218        PropertyDescriptor::Data(PropertyDescriptorData {
2219            value: JsValue::Boolean(true),
2220            writable: false,
2221            enumerable: false,
2222            configurable: false,
2223        }),
2224    );
2225
2226    // Mark as generator if applicable
2227    if func_data.generator {
2228        func_obj.base.properties.insert(
2229            PropertyKey::Str("__generator__".to_string()),
2230            PropertyDescriptor::Data(PropertyDescriptorData {
2231                value: JsValue::Boolean(true),
2232                writable: false,
2233                enumerable: false,
2234                configurable: false,
2235            }),
2236        );
2237    }
2238
2239    let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(func_obj))));
2240    Ok(JsValue::Object(obj_ref))
2241}
2242
2243// ============================================================================
2244// Generator object
2245// ============================================================================
2246
2247/// Generator state.
2248#[derive(Debug, Clone, PartialEq)]
2249pub enum GeneratorState {
2250    SuspendedStart,
2251    SuspendedYield,
2252    Executing,
2253    Completed,
2254}
2255
2256/// Generator object that stores the suspended state of a generator function.
2257pub struct GeneratorObject {
2258    base: ObjectBase,
2259    /// Pointer to the function body
2260    body_ptr: *const crate::parser::ast::FunctionBodyData,
2261    /// Pointer to the formal parameters
2262    _params_ptr: *const Vec<PatternType>,
2263    /// The lexical environment at the time the generator was created
2264    environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
2265    /// Current state
2266    state: GeneratorState,
2267    /// Current statement index (for resuming)
2268    current_index: usize,
2269    /// Stored local bindings (for resuming)
2270    local_bindings: std::collections::HashMap<String, JsValue>,
2271}
2272
2273// Safety: GeneratorObject is only used within a single thread
2274unsafe impl Send for GeneratorObject {}
2275unsafe impl Sync for GeneratorObject {}
2276
2277impl GeneratorObject {
2278    pub fn new(
2279        body_ptr: *const crate::parser::ast::FunctionBodyData,
2280        params_ptr: *const Vec<PatternType>,
2281        environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
2282    ) -> Self {
2283        GeneratorObject {
2284            base: ObjectBase::new(),
2285            body_ptr,
2286            _params_ptr: params_ptr,
2287            environment,
2288            state: GeneratorState::SuspendedStart,
2289            current_index: 0,
2290            local_bindings: std::collections::HashMap::new(),
2291        }
2292    }
2293
2294    /// Call .next() on this generator, executing until yield or completion.
2295    pub fn next(&mut self, ctx: &mut EvalContext) -> ValueResult {
2296        use crate::runner::ds::env_record::new_declarative_environment;
2297        use super::statement::execute_statement;
2298        use super::types::CompletionType;
2299
2300        match self.state {
2301            GeneratorState::Completed => {
2302                // Return { value: undefined, done: true }
2303                return create_iterator_result(JsValue::Undefined, true);
2304            }
2305            GeneratorState::Executing => {
2306                return Err(JErrorType::TypeError("Generator is already executing".to_string()));
2307            }
2308            GeneratorState::SuspendedStart | GeneratorState::SuspendedYield => {
2309                // Continue or start execution
2310            }
2311        }
2312
2313        self.state = GeneratorState::Executing;
2314
2315        // Get the body and params (unsafe dereference)
2316        let body = unsafe { &*self.body_ptr };
2317
2318        // Save current environment
2319        let saved_lex_env = ctx.lex_env.clone();
2320        let saved_var_env = ctx.var_env.clone();
2321
2322        // Create new environment for the generator (or restore previous)
2323        let func_scope = new_declarative_environment(Some(self.environment.clone()));
2324        ctx.lex_env = func_scope.clone();
2325        ctx.var_env = func_scope;
2326
2327        // Restore local bindings if resuming
2328        for (name, value) in &self.local_bindings {
2329            let _ = ctx.create_binding(name, false);
2330            let _ = ctx.initialize_binding(name, value.clone());
2331        }
2332
2333        // Execute statements starting from current_index
2334        let mut result_value = JsValue::Undefined;
2335        let mut yielded = false;
2336
2337        for (idx, stmt) in body.body.iter().enumerate() {
2338            if idx < self.current_index {
2339                continue; // Skip already executed statements
2340            }
2341
2342            match execute_statement(stmt, ctx) {
2343                Ok(completion) => {
2344                    match completion.completion_type {
2345                        CompletionType::Return => {
2346                            result_value = completion.get_value();
2347                            self.state = GeneratorState::Completed;
2348                            break;
2349                        }
2350                        CompletionType::Yield => {
2351                            // Yield the value and suspend
2352                            result_value = completion.get_value();
2353                            self.current_index = idx + 1;
2354                            self.state = GeneratorState::SuspendedYield;
2355
2356                            // Save current bindings
2357                            self.save_bindings(ctx);
2358
2359                            yielded = true;
2360                            break;
2361                        }
2362                        CompletionType::Throw => {
2363                            ctx.lex_env = saved_lex_env;
2364                            ctx.var_env = saved_var_env;
2365                            self.state = GeneratorState::Completed;
2366                            return Err(JErrorType::TypeError(format!(
2367                                "Uncaught exception: {:?}",
2368                                completion.value
2369                            )));
2370                        }
2371                        _ => {
2372                            result_value = completion.get_value();
2373                        }
2374                    }
2375                }
2376                Err(JErrorType::YieldValue(value)) => {
2377                    // Yield expression hit
2378                    result_value = value;
2379                    self.current_index = idx + 1;
2380                    self.state = GeneratorState::SuspendedYield;
2381
2382                    // Save current bindings
2383                    self.save_bindings(ctx);
2384
2385                    yielded = true;
2386                    break;
2387                }
2388                Err(e) => {
2389                    ctx.lex_env = saved_lex_env;
2390                    ctx.var_env = saved_var_env;
2391                    self.state = GeneratorState::Completed;
2392                    return Err(e);
2393                }
2394            }
2395        }
2396
2397        // If we didn't yield, we're done
2398        if !yielded && self.state == GeneratorState::Executing {
2399            self.state = GeneratorState::Completed;
2400        }
2401
2402        // Restore the previous environment
2403        ctx.lex_env = saved_lex_env;
2404        ctx.var_env = saved_var_env;
2405
2406        // Return { value, done }
2407        let done = self.state == GeneratorState::Completed;
2408        create_iterator_result(result_value, done)
2409    }
2410
2411    fn save_bindings(&mut self, ctx: &EvalContext) {
2412        // This is a simplified version - in a full implementation we'd need
2413        // to properly capture all bindings from the current scope
2414        self.local_bindings.clear();
2415
2416        // Get bindings from current environment
2417        let env = ctx.lex_env.borrow();
2418        if let Some(bindings) = env.inner.as_env_record().get_all_bindings() {
2419            for (name, value) in bindings {
2420                self.local_bindings.insert(name, value);
2421            }
2422        }
2423    }
2424}
2425
2426impl JsObject for GeneratorObject {
2427    fn get_object_base_mut(&mut self) -> &mut ObjectBase {
2428        &mut self.base
2429    }
2430
2431    fn get_object_base(&self) -> &ObjectBase {
2432        &self.base
2433    }
2434
2435    fn as_js_object(&self) -> &dyn JsObject {
2436        self
2437    }
2438
2439    fn as_js_object_mut(&mut self) -> &mut dyn JsObject {
2440        self
2441    }
2442}
2443
2444/// Create an iterator result object { value, done }.
2445fn create_iterator_result(value: JsValue, done: bool) -> ValueResult {
2446    let mut obj = SimpleObject::new();
2447
2448    obj.get_object_base_mut().properties.insert(
2449        PropertyKey::Str("value".to_string()),
2450        PropertyDescriptor::Data(PropertyDescriptorData {
2451            value,
2452            writable: true,
2453            enumerable: true,
2454            configurable: true,
2455        }),
2456    );
2457
2458    obj.get_object_base_mut().properties.insert(
2459        PropertyKey::Str("done".to_string()),
2460        PropertyDescriptor::Data(PropertyDescriptorData {
2461            value: JsValue::Boolean(done),
2462            writable: true,
2463            enumerable: true,
2464            configurable: true,
2465        }),
2466    );
2467
2468    let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(obj))));
2469    Ok(JsValue::Object(obj_ref))
2470}
2471
2472/// Create a generator object from a generator function.
2473fn create_generator_object(
2474    body_ptr: *const crate::parser::ast::FunctionBodyData,
2475    params_ptr: *const Vec<PatternType>,
2476    environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
2477    _args: Vec<JsValue>,
2478    _ctx: &mut EvalContext,
2479) -> ValueResult {
2480    let mut gen_obj = GeneratorObject::new(body_ptr, params_ptr, environment);
2481
2482    // Add marker property
2483    gen_obj.base.properties.insert(
2484        PropertyKey::Str("__generator_object__".to_string()),
2485        PropertyDescriptor::Data(PropertyDescriptorData {
2486            value: JsValue::Boolean(true),
2487            writable: false,
2488            enumerable: false,
2489            configurable: false,
2490        }),
2491    );
2492
2493    // Store parameter values in local_bindings for when .next() is first called
2494    let params = unsafe { &*params_ptr };
2495    for (i, param) in params.iter().enumerate() {
2496        if let PatternType::PatternWhichCanBeExpression(
2497            ExpressionPatternType::Identifier(id)
2498        ) = param {
2499            let arg_value = _args.get(i).cloned().unwrap_or(JsValue::Undefined);
2500            gen_obj.local_bindings.insert(id.name.clone(), arg_value);
2501        }
2502    }
2503
2504    // Create a 'next' method
2505    // We store a reference to the generator in a closure
2506    let gen_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(gen_obj))));
2507
2508    // Add next method - this is a bit hacky, we'll store the generator ref
2509    // Actually, we can't easily create a closure here. Let's use a different approach:
2510    // We'll mark the generator and handle .next() specially in property access.
2511
2512    // For simplicity, we mark it and handle next() calls specially
2513    Ok(JsValue::Object(gen_ref))
2514}
2515
2516/// Create a callable "next" method for a generator object.
2517fn create_generator_next_method(gen_obj: JsObjectType) -> ValueResult {
2518    // Create a simple object that holds the generator reference
2519    // and is marked as a generator-next-method
2520    let mut next_obj = SimpleObject::new();
2521
2522    next_obj.get_object_base_mut().properties.insert(
2523        PropertyKey::Str("__generator_next__".to_string()),
2524        PropertyDescriptor::Data(PropertyDescriptorData {
2525            value: JsValue::Object(gen_obj),
2526            writable: false,
2527            enumerable: false,
2528            configurable: false,
2529        }),
2530    );
2531
2532    // Mark it as callable
2533    next_obj.get_object_base_mut().properties.insert(
2534        PropertyKey::Str("__simple_function__".to_string()),
2535        PropertyDescriptor::Data(PropertyDescriptorData {
2536            value: JsValue::Boolean(true),
2537            writable: false,
2538            enumerable: false,
2539            configurable: false,
2540        }),
2541    );
2542
2543    let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(next_obj))));
2544    Ok(JsValue::Object(obj_ref))
2545}
2546
2547// ============================================================================
2548// Class evaluation
2549// ============================================================================
2550
2551/// Evaluate a class expression and return the constructor function.
2552pub fn evaluate_class_expression(class_data: &ClassData, ctx: &mut EvalContext) -> ValueResult {
2553    evaluate_class(class_data, ctx)
2554}
2555
2556/// Evaluate a class definition (used by both ClassExpression and ClassDeclaration).
2557/// Returns a constructor function object with methods on its prototype.
2558pub fn evaluate_class(class_data: &ClassData, ctx: &mut EvalContext) -> ValueResult {
2559    // 1. Evaluate the super class if present
2560    let parent_proto = if let Some(super_class) = &class_data.super_class {
2561        let parent = evaluate_expression(super_class, ctx)?;
2562        match &parent {
2563            JsValue::Object(parent_obj) => {
2564                // Get parent.prototype
2565                let borrowed = parent_obj.borrow();
2566                let proto_key = PropertyKey::Str("prototype".to_string());
2567                if let Some(PropertyDescriptor::Data(data)) = borrowed.as_js_object().get_object_base().properties.get(&proto_key) {
2568                    if let JsValue::Object(proto) = &data.value {
2569                        Some((parent_obj.clone(), proto.clone()))
2570                    } else {
2571                        return Err(JErrorType::TypeError("Parent class prototype is not an object".to_string()));
2572                    }
2573                } else {
2574                    return Err(JErrorType::TypeError("Parent class has no prototype".to_string()));
2575                }
2576            }
2577            _ => return Err(JErrorType::TypeError("Class extends value is not a constructor".to_string())),
2578        }
2579    } else {
2580        None
2581    };
2582
2583    // 2. Find the constructor method
2584    let constructor_method = class_data.body.body.iter()
2585        .find(|m| matches!(m.kind, MethodDefinitionKind::Constructor));
2586
2587    // 3. Create the constructor function
2588    let constructor_obj = if let Some(ctor_method) = constructor_method {
2589        // Use the defined constructor
2590        create_class_constructor(&ctor_method.value, parent_proto.as_ref().map(|(p, _)| p.clone()), ctx)?
2591    } else {
2592        // Create a default constructor
2593        create_default_constructor(parent_proto.as_ref().map(|(p, _)| p.clone()), ctx)?
2594    };
2595
2596    // 4. Create the prototype object
2597    let prototype = if let Some((_, parent_proto)) = &parent_proto {
2598        // Derived class: prototype inherits from parent prototype
2599        let mut proto_obj = SimpleObject::new();
2600        proto_obj.get_object_base_mut().prototype = Some(parent_proto.clone());
2601        Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(proto_obj))))
2602    } else {
2603        // Base class: regular prototype object
2604        Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(SimpleObject::new()))))
2605    };
2606
2607    // 5. Add methods to prototype (and static methods to constructor)
2608    for method in &class_data.body.body {
2609        if matches!(method.kind, MethodDefinitionKind::Constructor) {
2610            continue; // Skip constructor, already handled
2611        }
2612
2613        // Get the method key
2614        let key = get_object_property_key(&method.key, method.computed, ctx)?;
2615
2616        // Create the method function
2617        let method_fn = create_function_object(&method.value, ctx)?;
2618
2619        // Determine target: prototype for instance methods, constructor for static
2620        let target = if method.static_flag {
2621            &constructor_obj
2622        } else {
2623            &JsValue::Object(prototype.clone())
2624        };
2625
2626        match method.kind {
2627            MethodDefinitionKind::Method => {
2628                // Regular method
2629                if let JsValue::Object(target_obj) = target {
2630                    target_obj.borrow_mut().as_js_object_mut().get_object_base_mut().properties.insert(
2631                        PropertyKey::Str(key),
2632                        PropertyDescriptor::Data(PropertyDescriptorData {
2633                            value: method_fn,
2634                            writable: true,
2635                            enumerable: false,
2636                            configurable: true,
2637                        }),
2638                    );
2639                }
2640            }
2641            MethodDefinitionKind::Get => {
2642                // Getter
2643                if let JsValue::Object(getter_fn) = method_fn {
2644                    if let JsValue::Object(target_obj) = target {
2645                        let mut borrowed = target_obj.borrow_mut();
2646                        let prop_key = PropertyKey::Str(key.clone());
2647                        let existing = borrowed.as_js_object().get_object_base().properties.get(&prop_key).cloned();
2648
2649                        let setter = if let Some(PropertyDescriptor::Accessor(acc)) = existing {
2650                            acc.set
2651                        } else {
2652                            None
2653                        };
2654
2655                        borrowed.as_js_object_mut().get_object_base_mut().properties.insert(
2656                            prop_key,
2657                            PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
2658                                get: Some(getter_fn),
2659                                set: setter,
2660                                enumerable: false,
2661                                configurable: true,
2662                            }),
2663                        );
2664                    }
2665                }
2666            }
2667            MethodDefinitionKind::Set => {
2668                // Setter
2669                if let JsValue::Object(setter_fn) = method_fn {
2670                    if let JsValue::Object(target_obj) = target {
2671                        let mut borrowed = target_obj.borrow_mut();
2672                        let prop_key = PropertyKey::Str(key.clone());
2673                        let existing = borrowed.as_js_object().get_object_base().properties.get(&prop_key).cloned();
2674
2675                        let getter = if let Some(PropertyDescriptor::Accessor(acc)) = existing {
2676                            acc.get
2677                        } else {
2678                            None
2679                        };
2680
2681                        borrowed.as_js_object_mut().get_object_base_mut().properties.insert(
2682                            prop_key,
2683                            PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
2684                                get: getter,
2685                                set: Some(setter_fn),
2686                                enumerable: false,
2687                                configurable: true,
2688                            }),
2689                        );
2690                    }
2691                }
2692            }
2693            MethodDefinitionKind::Constructor => unreachable!(),
2694        }
2695    }
2696
2697    // 6. Wire up constructor.prototype
2698    if let JsValue::Object(ctor_obj) = &constructor_obj {
2699        ctor_obj.borrow_mut().as_js_object_mut().get_object_base_mut().properties.insert(
2700            PropertyKey::Str("prototype".to_string()),
2701            PropertyDescriptor::Data(PropertyDescriptorData {
2702                value: JsValue::Object(prototype.clone()),
2703                writable: false,
2704                enumerable: false,
2705                configurable: false,
2706            }),
2707        );
2708
2709        // Set prototype.constructor to point back to constructor
2710        prototype.borrow_mut().as_js_object_mut().get_object_base_mut().properties.insert(
2711            PropertyKey::Str("constructor".to_string()),
2712            PropertyDescriptor::Data(PropertyDescriptorData {
2713                value: constructor_obj.clone(),
2714                writable: true,
2715                enumerable: false,
2716                configurable: true,
2717            }),
2718        );
2719    }
2720
2721    Ok(constructor_obj)
2722}
2723
2724/// Create a constructor function from a method definition.
2725fn create_class_constructor(
2726    func_data: &FunctionData,
2727    _parent_ctor: Option<JsObjectType>,
2728    ctx: &EvalContext,
2729) -> ValueResult {
2730    let body_ptr = func_data.body.as_ref() as *const _;
2731    let params_ptr = &func_data.params.list as *const _;
2732    let environment = ctx.lex_env.clone();
2733
2734    let mut func_obj = SimpleFunctionObject::new(body_ptr, params_ptr, environment);
2735
2736    // Add marker property to identify this as a SimpleFunctionObject (callable)
2737    func_obj.base.properties.insert(
2738        PropertyKey::Str("__simple_function__".to_string()),
2739        PropertyDescriptor::Data(PropertyDescriptorData {
2740            value: JsValue::Boolean(true),
2741            writable: false,
2742            enumerable: false,
2743            configurable: false,
2744        }),
2745    );
2746
2747    // Mark as class constructor
2748    func_obj.base.properties.insert(
2749        PropertyKey::Str("__class_constructor__".to_string()),
2750        PropertyDescriptor::Data(PropertyDescriptorData {
2751            value: JsValue::Boolean(true),
2752            writable: false,
2753            enumerable: false,
2754            configurable: false,
2755        }),
2756    );
2757
2758    let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(func_obj))));
2759    Ok(JsValue::Object(obj_ref))
2760}
2761
2762/// Create a default constructor for a class.
2763fn create_default_constructor(
2764    _parent_ctor: Option<JsObjectType>,
2765    _ctx: &EvalContext,
2766) -> ValueResult {
2767    // Create a simple empty function that does nothing
2768    // For derived classes, it should call super(), but we'll simplify for now
2769    let mut func_obj = SimpleObject::new();
2770
2771    // Add marker property to identify this as callable
2772    func_obj.get_object_base_mut().properties.insert(
2773        PropertyKey::Str("__simple_function__".to_string()),
2774        PropertyDescriptor::Data(PropertyDescriptorData {
2775            value: JsValue::Boolean(true),
2776            writable: false,
2777            enumerable: false,
2778            configurable: false,
2779        }),
2780    );
2781
2782    // Mark as class constructor
2783    func_obj.get_object_base_mut().properties.insert(
2784        PropertyKey::Str("__class_constructor__".to_string()),
2785        PropertyDescriptor::Data(PropertyDescriptorData {
2786            value: JsValue::Boolean(true),
2787            writable: false,
2788            enumerable: false,
2789            configurable: false,
2790        }),
2791    );
2792
2793    // Mark as default constructor (no-op)
2794    func_obj.get_object_base_mut().properties.insert(
2795        PropertyKey::Str("__default_constructor__".to_string()),
2796        PropertyDescriptor::Data(PropertyDescriptorData {
2797            value: JsValue::Boolean(true),
2798            writable: false,
2799            enumerable: false,
2800            configurable: false,
2801        }),
2802    );
2803
2804    let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(func_obj))));
2805    Ok(JsValue::Object(obj_ref))
2806}
2807