we (web engine): Experimental web browser project to understand the limits of Claude
at encoding-sniffing 7936 lines 298 kB view raw
1//! Register-based JavaScript virtual machine. 2//! 3//! Executes bytecode produced by the compiler. Each call frame has a register 4//! file, and the VM dispatches instructions in a loop. Heap-allocated objects 5//! (plain objects and functions) are managed by a tri-color mark-and-sweep 6//! garbage collector. 7 8use crate::bytecode::{Constant, Function, Op, Reg}; 9use crate::gc::{Gc, GcRef, Traceable}; 10use std::cell::RefCell; 11use std::collections::HashMap; 12use std::fmt; 13use std::rc::Rc; 14use we_dom::Document; 15 16// ── Heap objects (GC-managed) ──────────────────────────────── 17 18/// A GC-managed heap object: a plain object, a function, a closure cell, or a generator. 19pub enum HeapObject { 20 Object(ObjectData), 21 Function(Box<FunctionData>), 22 /// A mutable cell holding one Value — used for closure-captured variables. 23 Cell(Value), 24 /// A suspended generator function instance. 25 Generator(Box<GeneratorData>), 26} 27 28/// State of a generator object. 29#[derive(Debug, Clone, Copy, PartialEq, Eq)] 30pub enum GeneratorState { 31 /// Created but next() not yet called. 32 NotStarted, 33 /// Suspended at a yield point. 34 Suspended, 35 /// Currently executing (re-entrancy guard). 36 Executing, 37 /// Completed (returned or threw). 38 Completed, 39} 40 41/// Data for a suspended generator function. 42pub struct GeneratorData { 43 pub state: GeneratorState, 44 /// The generator function's bytecode. 45 pub func: Function, 46 /// Captured upvalues. 47 pub upvalues: Vec<GcRef>, 48 /// Saved register file for this generator's frame. 49 pub registers: Vec<Value>, 50 /// Saved instruction pointer (where to resume). 51 pub ip: usize, 52 /// The GcRef of the result prototype (for {value, done} objects). 53 pub prototype: Option<GcRef>, 54 /// Saved exception handlers (for try/catch across await/yield points). 55 pub exception_handlers: Vec<(usize, Reg)>, 56} 57 58impl Traceable for HeapObject { 59 fn trace(&self, visitor: &mut dyn FnMut(GcRef)) { 60 match self { 61 HeapObject::Object(data) => { 62 for prop in data.properties.values() { 63 if let Some(r) = prop.value.gc_ref() { 64 visitor(r); 65 } 66 } 67 if let Some(proto) = data.prototype { 68 visitor(proto); 69 } 70 } 71 HeapObject::Function(fdata) => { 72 if let Some(proto) = fdata.prototype_obj { 73 visitor(proto); 74 } 75 for prop in fdata.properties.values() { 76 if let Some(r) = prop.value.gc_ref() { 77 visitor(r); 78 } 79 } 80 for &uv in &fdata.upvalues { 81 visitor(uv); 82 } 83 } 84 HeapObject::Cell(val) => { 85 if let Some(r) = val.gc_ref() { 86 visitor(r); 87 } 88 } 89 HeapObject::Generator(gen) => { 90 for &uv in &gen.upvalues { 91 visitor(uv); 92 } 93 for val in &gen.registers { 94 if let Some(r) = val.gc_ref() { 95 visitor(r); 96 } 97 } 98 if let Some(proto) = gen.prototype { 99 visitor(proto); 100 } 101 } 102 } 103 } 104} 105 106/// A property descriptor stored in an object's property map. 107#[derive(Clone)] 108pub struct Property { 109 /// The property's value (for data properties). 110 pub value: Value, 111 /// Whether the value can be changed via assignment. 112 pub writable: bool, 113 /// Whether the property shows up in `for...in` and `Object.keys`. 114 pub enumerable: bool, 115 /// Whether the property can be deleted or its attributes changed. 116 pub configurable: bool, 117} 118 119impl Property { 120 /// Create a new data property with all flags set to true (the JS default for 121 /// properties created by assignment). 122 pub fn data(value: Value) -> Self { 123 Self { 124 value, 125 writable: true, 126 enumerable: true, 127 configurable: true, 128 } 129 } 130 131 /// Create a non-enumerable, non-configurable property (e.g. built-in methods). 132 pub fn builtin(value: Value) -> Self { 133 Self { 134 value, 135 writable: true, 136 enumerable: false, 137 configurable: false, 138 } 139 } 140} 141 142/// A JS plain object (properties stored as a HashMap with descriptors). 143pub struct ObjectData { 144 pub properties: HashMap<String, Property>, 145 pub prototype: Option<GcRef>, 146 /// Whether new properties can be added (Object.preventExtensions). 147 pub extensible: bool, 148} 149 150impl ObjectData { 151 pub fn new() -> Self { 152 Self { 153 properties: HashMap::new(), 154 prototype: None, 155 extensible: true, 156 } 157 } 158} 159 160impl Default for ObjectData { 161 fn default() -> Self { 162 Self::new() 163 } 164} 165 166/// A runtime function value: either bytecode or native. 167/// 168/// In JavaScript, functions are objects and can have arbitrary properties 169/// (e.g. `assert.sameValue = function() {}`). 170pub struct FunctionData { 171 pub name: String, 172 pub kind: FunctionKind, 173 /// The `.prototype` property object (for use as a constructor with `instanceof`). 174 pub prototype_obj: Option<GcRef>, 175 /// Arbitrary properties set on this function (functions are objects in JS). 176 pub properties: HashMap<String, Property>, 177 /// Captured upvalue cells (GcRefs to HeapObject::Cell values). 178 pub upvalues: Vec<GcRef>, 179} 180 181#[derive(Clone)] 182pub enum FunctionKind { 183 /// Bytecode function. 184 Bytecode(BytecodeFunc), 185 /// Native (Rust) function. 186 Native(NativeFunc), 187} 188 189#[derive(Clone)] 190pub struct BytecodeFunc { 191 pub func: Function, 192} 193 194/// A native function callable from JS. 195#[derive(Clone)] 196pub struct NativeFunc { 197 pub callback: fn(&[Value], &mut NativeContext) -> Result<Value, RuntimeError>, 198} 199 200/// Trait for console output. Allows redirecting console output to a dev tools 201/// panel or capturing it in tests. The default implementation writes to 202/// stdout/stderr. 203pub trait ConsoleOutput { 204 fn log(&self, message: &str); 205 fn error(&self, message: &str); 206 fn warn(&self, message: &str); 207} 208 209/// Default console output that writes to stdout/stderr. 210pub struct StdConsoleOutput; 211 212impl ConsoleOutput for StdConsoleOutput { 213 fn log(&self, message: &str) { 214 println!("{}", message); 215 } 216 fn error(&self, message: &str) { 217 eprintln!("{}", message); 218 } 219 fn warn(&self, message: &str) { 220 eprintln!("{}", message); 221 } 222} 223 224/// A single event listener registered on a DOM node. 225pub struct EventListener { 226 pub event_type: String, 227 pub callback: GcRef, 228 pub capture: bool, 229 pub once: bool, 230} 231 232/// Bridge between JS and the DOM. Holds a shared document and a cache 233/// mapping `NodeId` indices to their JS wrapper `GcRef` so that the same 234/// DOM node always returns the same JS object (identity). 235pub struct DomBridge { 236 pub document: RefCell<Document>, 237 pub node_wrappers: RefCell<HashMap<usize, GcRef>>, 238 /// Event listeners keyed by NodeId index. 239 pub event_listeners: RefCell<HashMap<usize, Vec<EventListener>>>, 240} 241 242/// Context passed to native functions, providing GC access and `this` binding. 243pub struct NativeContext<'a> { 244 pub gc: &'a mut Gc<HeapObject>, 245 pub this: Value, 246 pub console_output: &'a dyn ConsoleOutput, 247 pub dom_bridge: Option<&'a DomBridge>, 248} 249 250// ── JS Value ────────────────────────────────────────────────── 251 252/// A JavaScript runtime value. 253/// 254/// Primitive types (Undefined, Null, Boolean, Number, String) are stored 255/// inline. Objects and Functions are heap-allocated via the GC and referenced 256/// by a [`GcRef`] handle. 257#[derive(Clone)] 258pub enum Value { 259 Undefined, 260 Null, 261 Boolean(bool), 262 Number(f64), 263 String(String), 264 /// A GC-managed plain object. 265 Object(GcRef), 266 /// A GC-managed function. 267 Function(GcRef), 268} 269 270impl fmt::Debug for Value { 271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 272 match self { 273 Value::Undefined => write!(f, "undefined"), 274 Value::Null => write!(f, "null"), 275 Value::Boolean(b) => write!(f, "{b}"), 276 Value::Number(n) => write!(f, "{n}"), 277 Value::String(s) => write!(f, "\"{}\"", s), 278 Value::Object(_) => write!(f, "[object Object]"), 279 Value::Function(_) => write!(f, "[Function]"), 280 } 281 } 282} 283 284impl fmt::Display for Value { 285 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 286 match self { 287 Value::Undefined => write!(f, "undefined"), 288 Value::Null => write!(f, "null"), 289 Value::Boolean(b) => write!(f, "{b}"), 290 Value::Number(n) => format_number(*n, f), 291 Value::String(s) => write!(f, "{s}"), 292 Value::Object(_) => write!(f, "[object Object]"), 293 Value::Function(_) => write!(f, "function() {{ [native code] }}"), 294 } 295 } 296} 297 298/// Format a number following JS conventions (no trailing .0 for integers). 299fn format_number(n: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result { 300 if n.is_nan() { 301 write!(f, "NaN") 302 } else if n.is_infinite() { 303 if n.is_sign_positive() { 304 write!(f, "Infinity") 305 } else { 306 write!(f, "-Infinity") 307 } 308 } else if n == 0.0 { 309 write!(f, "0") 310 } else if n.fract() == 0.0 && n.abs() < 1e20 { 311 write!(f, "{}", n as i64) 312 } else { 313 write!(f, "{n}") 314 } 315} 316 317impl Value { 318 /// Abstract `ToBoolean` (ECMA-262 §7.1.2). 319 pub fn to_boolean(&self) -> bool { 320 match self { 321 Value::Undefined | Value::Null => false, 322 Value::Boolean(b) => *b, 323 Value::Number(n) => *n != 0.0 && !n.is_nan(), 324 Value::String(s) => !s.is_empty(), 325 Value::Object(_) | Value::Function(_) => true, 326 } 327 } 328 329 /// Abstract `ToNumber` (ECMA-262 §7.1.3). 330 pub fn to_number(&self) -> f64 { 331 match self { 332 Value::Undefined => f64::NAN, 333 Value::Null => 0.0, 334 Value::Boolean(true) => 1.0, 335 Value::Boolean(false) => 0.0, 336 Value::Number(n) => *n, 337 Value::String(s) => { 338 let s = s.trim(); 339 if s.is_empty() { 340 0.0 341 } else if s == "Infinity" || s == "+Infinity" { 342 f64::INFINITY 343 } else if s == "-Infinity" { 344 f64::NEG_INFINITY 345 } else { 346 s.parse::<f64>().unwrap_or(f64::NAN) 347 } 348 } 349 Value::Object(_) | Value::Function(_) => f64::NAN, 350 } 351 } 352 353 /// Abstract `ToString` (ECMA-262 §7.1.12). 354 /// 355 /// Requires `&Gc` to look up function names for `Value::Function`. 356 pub fn to_js_string(&self, gc: &Gc<HeapObject>) -> String { 357 match self { 358 Value::Undefined => "undefined".to_string(), 359 Value::Null => "null".to_string(), 360 Value::Boolean(true) => "true".to_string(), 361 Value::Boolean(false) => "false".to_string(), 362 Value::Number(n) => js_number_to_string(*n), 363 Value::String(s) => s.clone(), 364 Value::Object(_) => "[object Object]".to_string(), 365 Value::Function(gc_ref) => gc 366 .get(*gc_ref) 367 .and_then(|obj| match obj { 368 HeapObject::Function(f) => { 369 Some(format!("function {}() {{ [native code] }}", f.name)) 370 } 371 _ => None, 372 }) 373 .unwrap_or_else(|| "function() { [native code] }".to_string()), 374 } 375 } 376 377 /// `typeof` operator result. 378 pub fn type_of(&self) -> &'static str { 379 match self { 380 Value::Undefined => "undefined", 381 Value::Null => "object", // yes, this is the spec 382 Value::Boolean(_) => "boolean", 383 Value::Number(_) => "number", 384 Value::String(_) => "string", 385 Value::Object(_) => "object", 386 Value::Function(_) => "function", 387 } 388 } 389 390 /// Is this value nullish (null or undefined)? 391 pub fn is_nullish(&self) -> bool { 392 matches!(self, Value::Undefined | Value::Null) 393 } 394 395 /// Extract the `GcRef` if this value is an Object or Function. 396 pub fn gc_ref(&self) -> Option<GcRef> { 397 match self { 398 Value::Object(r) | Value::Function(r) => Some(*r), 399 _ => None, 400 } 401 } 402} 403 404/// Format a number as JS would. 405pub(crate) fn js_number_to_string(n: f64) -> String { 406 if n.is_nan() { 407 "NaN".to_string() 408 } else if n.is_infinite() { 409 if n.is_sign_positive() { 410 "Infinity".to_string() 411 } else { 412 "-Infinity".to_string() 413 } 414 } else if n == 0.0 { 415 "0".to_string() 416 } else if n.fract() == 0.0 && n.abs() < 1e20 { 417 format!("{}", n as i64) 418 } else { 419 format!("{n}") 420 } 421} 422 423// ── Runtime errors ──────────────────────────────────────────── 424 425/// JavaScript runtime error types. 426#[derive(Debug, Clone)] 427pub struct RuntimeError { 428 pub kind: ErrorKind, 429 pub message: String, 430} 431 432#[derive(Debug, Clone, Copy, PartialEq, Eq)] 433pub enum ErrorKind { 434 TypeError, 435 ReferenceError, 436 RangeError, 437 SyntaxError, 438 Error, 439} 440 441impl fmt::Display for RuntimeError { 442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 443 let name = match self.kind { 444 ErrorKind::TypeError => "TypeError", 445 ErrorKind::ReferenceError => "ReferenceError", 446 ErrorKind::RangeError => "RangeError", 447 ErrorKind::SyntaxError => "SyntaxError", 448 ErrorKind::Error => "Error", 449 }; 450 write!(f, "{name}: {}", self.message) 451 } 452} 453 454impl RuntimeError { 455 pub fn type_error(msg: impl Into<String>) -> Self { 456 Self { 457 kind: ErrorKind::TypeError, 458 message: msg.into(), 459 } 460 } 461 462 pub fn reference_error(msg: impl Into<String>) -> Self { 463 Self { 464 kind: ErrorKind::ReferenceError, 465 message: msg.into(), 466 } 467 } 468 469 pub fn range_error(msg: impl Into<String>) -> Self { 470 Self { 471 kind: ErrorKind::RangeError, 472 message: msg.into(), 473 } 474 } 475 476 pub fn syntax_error(msg: impl Into<String>) -> Self { 477 Self { 478 kind: ErrorKind::SyntaxError, 479 message: msg.into(), 480 } 481 } 482 483 /// Convert to a JS Value (an error object). Allocates through the GC. 484 pub fn to_value(&self, gc: &mut Gc<HeapObject>) -> Value { 485 let mut obj = ObjectData::new(); 486 obj.properties.insert( 487 "message".to_string(), 488 Property::data(Value::String(self.message.clone())), 489 ); 490 let name = match self.kind { 491 ErrorKind::TypeError => "TypeError", 492 ErrorKind::ReferenceError => "ReferenceError", 493 ErrorKind::RangeError => "RangeError", 494 ErrorKind::SyntaxError => "SyntaxError", 495 ErrorKind::Error => "Error", 496 }; 497 obj.properties.insert( 498 "name".to_string(), 499 Property::data(Value::String(name.to_string())), 500 ); 501 Value::Object(gc.alloc(HeapObject::Object(obj))) 502 } 503} 504 505// ── Property access helpers ────────────────────────────────── 506 507/// Get a property from an object, walking the prototype chain. 508fn gc_get_property(gc: &Gc<HeapObject>, obj_ref: GcRef, key: &str) -> Value { 509 let proto = { 510 match gc.get(obj_ref) { 511 Some(HeapObject::Object(data)) => { 512 if let Some(prop) = data.properties.get(key) { 513 return prop.value.clone(); 514 } 515 data.prototype 516 } 517 Some(HeapObject::Function(fdata)) => { 518 // Check user-defined properties first. 519 if let Some(prop) = fdata.properties.get(key) { 520 return prop.value.clone(); 521 } 522 // Functions have a `.prototype` property. 523 if key == "prototype" { 524 if let Some(proto_ref) = fdata.prototype_obj { 525 return Value::Object(proto_ref); 526 } 527 return Value::Undefined; 528 } 529 None 530 } 531 _ => return Value::Undefined, 532 } 533 }; 534 if let Some(proto_ref) = proto { 535 gc_get_property(gc, proto_ref, key) 536 } else { 537 Value::Undefined 538 } 539} 540 541/// Check if an object has a property (own or inherited). 542fn gc_has_property(gc: &Gc<HeapObject>, obj_ref: GcRef, key: &str) -> bool { 543 let proto = { 544 match gc.get(obj_ref) { 545 Some(HeapObject::Object(data)) => { 546 if data.properties.contains_key(key) { 547 return true; 548 } 549 data.prototype 550 } 551 Some(HeapObject::Function(fdata)) => { 552 if fdata.properties.contains_key(key) || key == "prototype" { 553 return true; 554 } 555 None 556 } 557 _ => return false, 558 } 559 }; 560 if let Some(proto_ref) = proto { 561 gc_has_property(gc, proto_ref, key) 562 } else { 563 false 564 } 565} 566 567/// Collect all enumerable string keys of an object (own + inherited), in proper order. 568/// Integer indices first (sorted numerically), then string keys in insertion order. 569fn gc_enumerate_keys(gc: &Gc<HeapObject>, obj_ref: GcRef) -> Vec<String> { 570 let mut seen = std::collections::HashSet::new(); 571 let mut integer_keys: Vec<(u32, String)> = Vec::new(); 572 let mut string_keys: Vec<String> = Vec::new(); 573 let mut current = Some(obj_ref); 574 575 while let Some(cur_ref) = current { 576 match gc.get(cur_ref) { 577 Some(HeapObject::Object(data)) => { 578 for (key, prop) in &data.properties { 579 if prop.enumerable && seen.insert(key.clone()) { 580 if let Ok(idx) = key.parse::<u32>() { 581 integer_keys.push((idx, key.clone())); 582 } else { 583 string_keys.push(key.clone()); 584 } 585 } 586 } 587 current = data.prototype; 588 } 589 _ => break, 590 } 591 } 592 593 // Integer indices sorted numerically first, then string keys in collected order. 594 integer_keys.sort_by_key(|(idx, _)| *idx); 595 let mut result: Vec<String> = integer_keys.into_iter().map(|(_, k)| k).collect(); 596 result.extend(string_keys); 597 result 598} 599 600/// Check if `obj_ref` is an instance of the constructor at `ctor_ref`. 601/// Walks the prototype chain of `obj_ref` looking for `ctor.prototype`. 602fn gc_instanceof(gc: &Gc<HeapObject>, obj_ref: GcRef, ctor_ref: GcRef) -> bool { 603 // Get the constructor's .prototype object. 604 let ctor_proto = match gc.get(ctor_ref) { 605 Some(HeapObject::Function(fdata)) => match fdata.prototype_obj { 606 Some(p) => p, 607 None => return false, 608 }, 609 _ => return false, 610 }; 611 612 // Walk the prototype chain of obj_ref. 613 let mut current = match gc.get(obj_ref) { 614 Some(HeapObject::Object(data)) => data.prototype, 615 _ => None, 616 }; 617 618 while let Some(proto_ref) = current { 619 if proto_ref == ctor_proto { 620 return true; 621 } 622 current = match gc.get(proto_ref) { 623 Some(HeapObject::Object(data)) => data.prototype, 624 _ => None, 625 }; 626 } 627 false 628} 629 630/// Get a string property (length, index access). 631fn string_get_property(s: &str, key: &str) -> Value { 632 if key == "length" { 633 Value::Number(s.len() as f64) 634 } else if let Ok(idx) = key.parse::<usize>() { 635 s.chars() 636 .nth(idx) 637 .map(|c| Value::String(c.to_string())) 638 .unwrap_or(Value::Undefined) 639 } else { 640 Value::Undefined 641 } 642} 643 644// ── Type conversion helpers ────────────────────────────────── 645 646/// ToInt32 (ECMA-262 §7.1.5). 647fn to_int32(val: &Value) -> i32 { 648 let n = val.to_number(); 649 if n.is_nan() || n.is_infinite() || n == 0.0 { 650 return 0; 651 } 652 let i = n.trunc() as i64; 653 (i & 0xFFFF_FFFF) as i32 654} 655 656/// ToUint32 (ECMA-262 §7.1.6). 657fn to_uint32(val: &Value) -> u32 { 658 let n = val.to_number(); 659 if n.is_nan() || n.is_infinite() || n == 0.0 { 660 return 0; 661 } 662 let i = n.trunc() as i64; 663 (i & 0xFFFF_FFFF) as u32 664} 665 666// ── Equality ───────────────────────────────────────────────── 667 668/// Abstract equality comparison (==) per ECMA-262 §7.2.14. 669fn abstract_eq(x: &Value, y: &Value) -> bool { 670 match (x, y) { 671 (Value::Undefined, Value::Undefined) => true, 672 (Value::Null, Value::Null) => true, 673 (Value::Undefined, Value::Null) | (Value::Null, Value::Undefined) => true, 674 (Value::Number(a), Value::Number(b)) => a == b, 675 (Value::String(a), Value::String(b)) => a == b, 676 (Value::Boolean(a), Value::Boolean(b)) => a == b, 677 // Number / String → convert String to Number. 678 (Value::Number(_), Value::String(_)) => abstract_eq(x, &Value::Number(y.to_number())), 679 (Value::String(_), Value::Number(_)) => abstract_eq(&Value::Number(x.to_number()), y), 680 // Boolean → Number. 681 (Value::Boolean(_), _) => abstract_eq(&Value::Number(x.to_number()), y), 682 (_, Value::Boolean(_)) => abstract_eq(x, &Value::Number(y.to_number())), 683 // Same GcRef → equal. 684 (Value::Object(a), Value::Object(b)) => a == b, 685 (Value::Function(a), Value::Function(b)) => a == b, 686 _ => false, 687 } 688} 689 690/// Strict equality comparison (===) per ECMA-262 §7.2.15. 691fn strict_eq(x: &Value, y: &Value) -> bool { 692 match (x, y) { 693 (Value::Undefined, Value::Undefined) => true, 694 (Value::Null, Value::Null) => true, 695 (Value::Number(a), Value::Number(b)) => a == b, 696 (Value::String(a), Value::String(b)) => a == b, 697 (Value::Boolean(a), Value::Boolean(b)) => a == b, 698 // Reference identity for heap objects. 699 (Value::Object(a), Value::Object(b)) => a == b, 700 (Value::Function(a), Value::Function(b)) => a == b, 701 _ => false, 702 } 703} 704 705// ── Relational comparison ──────────────────────────────────── 706 707/// Abstract relational comparison. Returns false for NaN comparisons. 708fn abstract_relational( 709 lhs: &Value, 710 rhs: &Value, 711 predicate: fn(std::cmp::Ordering) -> bool, 712) -> bool { 713 // If both are strings, compare lexicographically. 714 if let (Value::String(a), Value::String(b)) = (lhs, rhs) { 715 return predicate(a.cmp(b)); 716 } 717 // Otherwise, compare as numbers. 718 let a = lhs.to_number(); 719 let b = rhs.to_number(); 720 if a.is_nan() || b.is_nan() { 721 return false; 722 } 723 predicate(a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)) 724} 725 726// ── Addition ───────────────────────────────────────────────── 727 728/// The + operator: string concat if either operand is a string, else numeric add. 729fn add_values(lhs: &Value, rhs: &Value, gc: &Gc<HeapObject>) -> Value { 730 match (lhs, rhs) { 731 (Value::String(a), _) => Value::String(format!("{a}{}", rhs.to_js_string(gc))), 732 (_, Value::String(b)) => Value::String(format!("{}{b}", lhs.to_js_string(gc))), 733 _ => Value::Number(lhs.to_number() + rhs.to_number()), 734 } 735} 736 737// ── Call frame ──────────────────────────────────────────────── 738 739/// A single call frame on the VM's call stack. 740struct CallFrame { 741 /// The function being executed. 742 func: Function, 743 /// Instruction pointer (byte offset into func.code). 744 ip: usize, 745 /// Base register index in the VM's register file. 746 base: usize, 747 /// Register to write the return value into (absolute index in register file). 748 return_reg: usize, 749 /// Exception handler stack for this frame. 750 exception_handlers: Vec<ExceptionHandler>, 751 /// Captured upvalue cells from the closure that created this call frame. 752 upvalues: Vec<GcRef>, 753} 754 755/// An exception handler entry (for try/catch). 756struct ExceptionHandler { 757 /// IP to jump to on exception (the catch block start). 758 catch_ip: usize, 759 /// Register to store the caught exception value. 760 catch_reg: Reg, 761} 762 763// ── VM ─────────────────────────────────────────────────────── 764 765/// The JavaScript virtual machine. 766pub struct Vm { 767 /// Register file (flat array shared across frames via base offsets). 768 registers: Vec<Value>, 769 /// Call stack. 770 frames: Vec<CallFrame>, 771 /// Global variables. 772 globals: HashMap<String, Value>, 773 /// Garbage collector managing heap objects. 774 pub gc: Gc<HeapObject>, 775 /// Optional instruction limit. If set, the VM will return an error after 776 /// executing this many instructions (prevents infinite loops). 777 instruction_limit: Option<u64>, 778 /// Number of instructions executed so far. 779 instructions_executed: u64, 780 /// Built-in Object.prototype (root of the prototype chain). 781 pub object_prototype: Option<GcRef>, 782 /// Built-in Array.prototype (set on newly created arrays). 783 pub array_prototype: Option<GcRef>, 784 /// Built-in String.prototype (for primitive auto-boxing). 785 pub string_prototype: Option<GcRef>, 786 /// Built-in Number.prototype (for primitive auto-boxing). 787 pub number_prototype: Option<GcRef>, 788 /// Built-in Boolean.prototype (for primitive auto-boxing). 789 pub boolean_prototype: Option<GcRef>, 790 /// Built-in Date.prototype (for Date constructor objects). 791 pub date_prototype: Option<GcRef>, 792 /// Built-in RegExp.prototype (for RegExp constructor objects). 793 pub regexp_prototype: Option<GcRef>, 794 /// Built-in Promise.prototype (for Promise objects). 795 pub promise_prototype: Option<GcRef>, 796 /// Console output sink (configurable for dev tools or testing). 797 console_output: Box<dyn ConsoleOutput>, 798 /// DOM bridge for JS-DOM interop (set via `attach_document`). 799 pub(crate) dom_bridge: Option<Rc<DomBridge>>, 800} 801 802/// Maximum register file size. 803const MAX_REGISTERS: usize = 4096; 804/// Maximum call depth. 805const MAX_CALL_DEPTH: usize = 512; 806 807impl Vm { 808 pub fn new() -> Self { 809 let mut vm = Self { 810 registers: vec![Value::Undefined; 256], 811 frames: Vec::new(), 812 globals: HashMap::new(), 813 gc: Gc::new(), 814 instruction_limit: None, 815 instructions_executed: 0, 816 object_prototype: None, 817 array_prototype: None, 818 string_prototype: None, 819 number_prototype: None, 820 boolean_prototype: None, 821 date_prototype: None, 822 regexp_prototype: None, 823 promise_prototype: None, 824 console_output: Box::new(StdConsoleOutput), 825 dom_bridge: None, 826 }; 827 crate::builtins::init_builtins(&mut vm); 828 vm 829 } 830 831 /// Replace the console output sink (e.g. for dev tools or testing). 832 pub fn set_console_output(&mut self, output: Box<dyn ConsoleOutput>) { 833 self.console_output = output; 834 } 835 836 /// Attach a DOM document to this VM, registering the `document` global 837 /// and enabling DOM-JS interop. 838 pub fn attach_document(&mut self, doc: Document) { 839 let bridge = Rc::new(DomBridge { 840 document: RefCell::new(doc), 841 node_wrappers: RefCell::new(HashMap::new()), 842 event_listeners: RefCell::new(HashMap::new()), 843 }); 844 self.dom_bridge = Some(bridge); 845 crate::dom_bridge::init_document_object(self); 846 crate::dom_bridge::init_event_system(self); 847 } 848 849 /// Detach the DOM document from the VM, returning it. 850 /// 851 /// This removes the `document` global and disconnects the DOM bridge. 852 /// Returns `None` if no document was attached or if there are outstanding 853 /// references to the bridge. 854 pub fn detach_document(&mut self) -> Option<Document> { 855 let bridge = self.dom_bridge.take()?; 856 self.globals.remove("document"); 857 match Rc::try_unwrap(bridge) { 858 Ok(bridge) => Some(bridge.document.into_inner()), 859 Err(rc) => { 860 // Something still holds a reference — reattach. 861 self.dom_bridge = Some(rc); 862 None 863 } 864 } 865 } 866 867 /// Set an instruction limit. The VM will return a RuntimeError after 868 /// executing this many instructions. 869 pub fn set_instruction_limit(&mut self, limit: u64) { 870 self.instruction_limit = Some(limit); 871 } 872 873 /// Execute a compiled top-level function and return the completion value. 874 pub fn execute(&mut self, func: &Function) -> Result<Value, RuntimeError> { 875 let reg_count = func.register_count as usize; 876 self.ensure_registers(reg_count); 877 878 self.frames.push(CallFrame { 879 func: func.clone(), 880 ip: 0, 881 base: 0, 882 return_reg: 0, 883 exception_handlers: Vec::new(), 884 upvalues: Vec::new(), 885 }); 886 887 let result = self.run()?; 888 self.drain_microtasks()?; 889 Ok(result) 890 } 891 892 /// Call a function (native or bytecode) from outside the execution loop. 893 /// Used by the microtask drain to execute promise callbacks. 894 pub fn call_function( 895 &mut self, 896 func_ref: GcRef, 897 args: &[Value], 898 ) -> Result<Value, RuntimeError> { 899 let (kind, upvalues) = match self.gc.get(func_ref) { 900 Some(HeapObject::Function(f)) => (f.kind.clone(), f.upvalues.clone()), 901 _ => return Err(RuntimeError::type_error("not a function")), 902 }; 903 904 match kind { 905 FunctionKind::Native(native) => { 906 // Set async resume data if this function has it. 907 if let Some(HeapObject::Function(f)) = self.gc.get(func_ref) { 908 if let Some(prop) = f.properties.get("__async_data__") { 909 if let Value::Object(data_ref) = &prop.value { 910 ASYNC_RESUME_DATA.with(|cell| cell.set(Some(*data_ref))); 911 } 912 } 913 } 914 915 let this = self 916 .globals 917 .get("this") 918 .cloned() 919 .unwrap_or(Value::Undefined); 920 let dom_ref = self.dom_bridge.as_deref(); 921 let mut ctx = NativeContext { 922 gc: &mut self.gc, 923 this, 924 console_output: &*self.console_output, 925 dom_bridge: dom_ref, 926 }; 927 let result = (native.callback)(args, &mut ctx)?; 928 929 // Check for generator resume marker. 930 if let Value::Object(r) = &result { 931 let is_gen_resume = matches!( 932 gc_get_property(&self.gc, *r, "__generator_resume__"), 933 Value::Boolean(true) 934 ); 935 if is_gen_resume { 936 let gen_ref = match gc_get_property(&self.gc, *r, "__gen_ref__") { 937 Value::Object(gr) => gr, 938 _ => return Ok(Value::Undefined), 939 }; 940 let send_val = gc_get_property(&self.gc, *r, "__send_value__"); 941 let kind_str = match gc_get_property(&self.gc, *r, "__resume_kind__") { 942 Value::String(s) => s, 943 _ => "next".to_string(), 944 }; 945 return match kind_str.as_str() { 946 "next" => self.run_generator(gen_ref, send_val), 947 "return" => { 948 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 949 gen.state = GeneratorState::Completed; 950 } 951 Ok(self.make_iterator_result(send_val, true)) 952 } 953 "throw" => { 954 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 955 gen.state = GeneratorState::Completed; 956 } 957 Err(RuntimeError::type_error("Generator throw")) 958 } 959 _ => Ok(Value::Undefined), 960 }; 961 } 962 963 // Check for async resume marker. 964 let is_async_resume = matches!( 965 gc_get_property(&self.gc, *r, "__async_resume__"), 966 Value::Boolean(true) 967 ); 968 if is_async_resume { 969 let gen_ref = match gc_get_property(&self.gc, *r, "__gen_ref__") { 970 Value::Object(gr) => gr, 971 _ => return Ok(Value::Undefined), 972 }; 973 let result_promise = 974 match gc_get_property(&self.gc, *r, "__result_promise__") { 975 Value::Object(pr) => pr, 976 _ => return Ok(Value::Undefined), 977 }; 978 let is_throw = matches!( 979 gc_get_property(&self.gc, *r, "__is_throw__"), 980 Value::Boolean(true) 981 ); 982 let value = gc_get_property(&self.gc, *r, "__value__"); 983 self.drive_async_step(gen_ref, result_promise, value, is_throw); 984 return Ok(Value::Undefined); 985 } 986 987 // Check for async generator resume marker. 988 let is_ag_resume = matches!( 989 gc_get_property(&self.gc, *r, "__async_generator_resume__"), 990 Value::Boolean(true) 991 ); 992 if is_ag_resume { 993 let gen_ref = match gc_get_property(&self.gc, *r, "__gen_ref__") { 994 Value::Object(gr) => gr, 995 _ => return Ok(Value::Undefined), 996 }; 997 let send_val = gc_get_property(&self.gc, *r, "__send_value__"); 998 let kind_str = match gc_get_property(&self.gc, *r, "__resume_kind__") { 999 Value::String(s) => s, 1000 _ => "next".to_string(), 1001 }; 1002 1003 // Create a promise for the result. 1004 let promise = crate::builtins::create_promise_object_pub(&mut self.gc); 1005 1006 match kind_str.as_str() { 1007 "next" => match self.run_generator(gen_ref, send_val) { 1008 Ok(iter_result) => { 1009 crate::builtins::resolve_promise_internal( 1010 &mut self.gc, 1011 promise, 1012 iter_result, 1013 ); 1014 } 1015 Err(err) => { 1016 let reason = err.to_value(&mut self.gc); 1017 crate::builtins::reject_promise_internal( 1018 &mut self.gc, 1019 promise, 1020 reason, 1021 ); 1022 } 1023 }, 1024 "return" => { 1025 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 1026 gen.state = GeneratorState::Completed; 1027 } 1028 let result = self.make_iterator_result(send_val, true); 1029 crate::builtins::resolve_promise_internal( 1030 &mut self.gc, 1031 promise, 1032 result, 1033 ); 1034 } 1035 _ => {} 1036 } 1037 return Ok(Value::Object(promise)); 1038 } 1039 1040 // Check for event dispatch marker. 1041 let is_event_dispatch = matches!( 1042 gc_get_property(&self.gc, *r, "__event_dispatch__"), 1043 Value::Boolean(true) 1044 ); 1045 if is_event_dispatch { 1046 let target_idx = match gc_get_property(&self.gc, *r, "__target_id__") { 1047 Value::Number(n) => n as usize, 1048 _ => return Ok(Value::Boolean(true)), 1049 }; 1050 let evt_ref = match gc_get_property(&self.gc, *r, "__event_ref__") { 1051 Value::Object(er) => er, 1052 _ => return Ok(Value::Boolean(true)), 1053 }; 1054 return Ok(crate::dom_bridge::run_event_dispatch( 1055 self, target_idx, evt_ref, 1056 )); 1057 } 1058 } 1059 1060 Ok(result) 1061 } 1062 FunctionKind::Bytecode(bc) => { 1063 let callee_func = bc.func; 1064 1065 // Async function: create generator + promise, drive async. 1066 if callee_func.is_async && !callee_func.is_generator { 1067 let gen_ref = self.create_raw_generator(callee_func, upvalues, args); 1068 let result_promise = crate::builtins::create_promise_object_pub(&mut self.gc); 1069 self.drive_async_step(gen_ref, result_promise, Value::Undefined, false); 1070 return Ok(Value::Object(result_promise)); 1071 } 1072 1073 // Async generator function: create async generator wrapper. 1074 if callee_func.is_async && callee_func.is_generator { 1075 let gen_ref = self.create_raw_generator(callee_func, upvalues, args); 1076 let wrapper = self.create_async_generator_wrapper(gen_ref); 1077 return Ok(Value::Object(wrapper)); 1078 } 1079 1080 // Generator function: create a generator object instead of executing. 1081 if callee_func.is_generator { 1082 let gen_obj = self.create_generator_object(callee_func, upvalues, args); 1083 return Ok(Value::Object(gen_obj)); 1084 } 1085 1086 // Save current frames and run the function in isolation. 1087 let saved_frames = std::mem::take(&mut self.frames); 1088 1089 // Compute base after any existing register usage. 1090 let base = saved_frames 1091 .last() 1092 .map(|f| f.base + f.func.register_count as usize) 1093 .unwrap_or(0); 1094 1095 let reg_count = callee_func.register_count as usize; 1096 self.ensure_registers(base + reg_count); 1097 1098 // Copy arguments. 1099 let param_count = callee_func.param_count as usize; 1100 for (i, arg) in args.iter().enumerate() { 1101 if i < param_count { 1102 self.registers[base + i] = arg.clone(); 1103 } 1104 } 1105 for i in args.len()..param_count { 1106 self.registers[base + i] = Value::Undefined; 1107 } 1108 1109 self.frames.push(CallFrame { 1110 func: callee_func, 1111 ip: 0, 1112 base, 1113 return_reg: base, 1114 exception_handlers: Vec::new(), 1115 upvalues, 1116 }); 1117 1118 let result = self.run(); 1119 1120 // Restore saved frames. 1121 self.frames = saved_frames; 1122 1123 result 1124 } 1125 } 1126 } 1127 1128 /// Drain the microtask queue. Called after execute() and recursively 1129 /// until no more microtasks are pending. 1130 fn drain_microtasks(&mut self) -> Result<(), RuntimeError> { 1131 loop { 1132 let tasks = crate::builtins::take_microtasks(); 1133 if tasks.is_empty() { 1134 break; 1135 } 1136 1137 for task in tasks { 1138 match task.handler { 1139 Some(handler_ref) => { 1140 // Call the handler with the value. 1141 let result = 1142 self.call_function(handler_ref, std::slice::from_ref(&task.value)); 1143 if let Some(chained) = task.chained_promise { 1144 // Check if this is a "finally" chain. 1145 let is_finally = matches!( 1146 crate::builtins::promise_get_prop_pub( 1147 &self.gc, 1148 chained, 1149 "__finally__" 1150 ), 1151 Value::Boolean(true) 1152 ); 1153 1154 if is_finally { 1155 // finally: ignore handler result, propagate parent's result. 1156 match result { 1157 Ok(_) => { 1158 let parent = crate::builtins::promise_get_prop_pub( 1159 &self.gc, 1160 chained, 1161 "__finally_parent__", 1162 ); 1163 if let Some(parent_ref) = parent.gc_ref() { 1164 let parent_state = crate::builtins::promise_state_pub( 1165 &self.gc, parent_ref, 1166 ); 1167 let parent_result = 1168 crate::builtins::promise_get_prop_pub( 1169 &self.gc, 1170 parent_ref, 1171 crate::builtins::PROMISE_RESULT_KEY, 1172 ); 1173 if parent_state == crate::builtins::PROMISE_FULFILLED { 1174 crate::builtins::resolve_promise_internal( 1175 &mut self.gc, 1176 chained, 1177 parent_result, 1178 ); 1179 } else { 1180 crate::builtins::reject_promise_internal( 1181 &mut self.gc, 1182 chained, 1183 parent_result, 1184 ); 1185 } 1186 } 1187 } 1188 Err(err) => { 1189 let err_val = err.to_value(&mut self.gc); 1190 crate::builtins::reject_promise_internal( 1191 &mut self.gc, 1192 chained, 1193 err_val, 1194 ); 1195 } 1196 } 1197 } else { 1198 match result { 1199 Ok(val) => { 1200 // If result is a promise, chain it. 1201 if crate::builtins::is_promise_pub(&self.gc, &val) { 1202 if let Some(val_ref) = val.gc_ref() { 1203 let state = crate::builtins::promise_state_pub( 1204 &self.gc, val_ref, 1205 ); 1206 if state == crate::builtins::PROMISE_FULFILLED { 1207 let r = crate::builtins::promise_get_prop_pub( 1208 &self.gc, 1209 val_ref, 1210 crate::builtins::PROMISE_RESULT_KEY, 1211 ); 1212 crate::builtins::resolve_promise_internal( 1213 &mut self.gc, 1214 chained, 1215 r, 1216 ); 1217 } else if state == crate::builtins::PROMISE_REJECTED 1218 { 1219 let r = crate::builtins::promise_get_prop_pub( 1220 &self.gc, 1221 val_ref, 1222 crate::builtins::PROMISE_RESULT_KEY, 1223 ); 1224 crate::builtins::reject_promise_internal( 1225 &mut self.gc, 1226 chained, 1227 r, 1228 ); 1229 } else { 1230 crate::builtins::chain_promise_pub( 1231 &mut self.gc, 1232 val_ref, 1233 chained, 1234 ); 1235 } 1236 } 1237 } else { 1238 crate::builtins::resolve_promise_internal( 1239 &mut self.gc, 1240 chained, 1241 val, 1242 ); 1243 } 1244 } 1245 Err(err) => { 1246 let err_val = err.to_value(&mut self.gc); 1247 crate::builtins::reject_promise_internal( 1248 &mut self.gc, 1249 chained, 1250 err_val, 1251 ); 1252 } 1253 } 1254 } 1255 } 1256 } 1257 None => { 1258 // No handler: identity for fulfillment, thrower for rejection. 1259 if let Some(chained) = task.chained_promise { 1260 if task.is_fulfillment { 1261 crate::builtins::resolve_promise_internal( 1262 &mut self.gc, 1263 chained, 1264 task.value, 1265 ); 1266 } else { 1267 crate::builtins::reject_promise_internal( 1268 &mut self.gc, 1269 chained, 1270 task.value, 1271 ); 1272 } 1273 } 1274 } 1275 } 1276 } 1277 } 1278 Ok(()) 1279 } 1280 1281 /// Execute all due timer callbacks. After each callback, drain the 1282 /// microtask queue (so Promise `.then()` runs between timer callbacks). 1283 fn drain_due_timers(&mut self) -> Result<(), RuntimeError> { 1284 let due = crate::timers::take_due_timers(); 1285 for timer in due { 1286 // For requestAnimationFrame, pass the timestamp as an argument. 1287 let args = match timer.raf_timestamp { 1288 Some(ts) => vec![Value::Number(ts)], 1289 None => vec![], 1290 }; 1291 let _ = self.call_function(timer.callback, &args); 1292 self.drain_microtasks()?; 1293 } 1294 Ok(()) 1295 } 1296 1297 /// Resolve/reject promises for completed fetch() requests. 1298 fn drain_completed_fetches(&mut self) -> Result<(), RuntimeError> { 1299 let completed = crate::fetch::take_completed_fetches(); 1300 for fetch in completed { 1301 match fetch.result { 1302 Ok(result) => { 1303 let response = crate::fetch::create_response_object(&mut self.gc, &result); 1304 crate::builtins::resolve_promise_internal( 1305 &mut self.gc, 1306 fetch.promise, 1307 Value::Object(response), 1308 ); 1309 } 1310 Err(err_msg) => { 1311 let err_val = Value::String(err_msg); 1312 crate::builtins::reject_promise_internal(&mut self.gc, fetch.promise, err_val); 1313 } 1314 } 1315 self.drain_microtasks()?; 1316 } 1317 Ok(()) 1318 } 1319 1320 /// Pump the event loop: drain due timers, completed fetches, and microtasks. 1321 /// 1322 /// Call this from tests or the platform event loop to fire any pending 1323 /// timers whose delay has elapsed. Each timer callback is followed by 1324 /// a microtask drain. 1325 pub fn pump_event_loop(&mut self) -> Result<(), RuntimeError> { 1326 self.drain_due_timers()?; 1327 self.drain_completed_fetches() 1328 } 1329 1330 /// Run the event loop until all pending timers and fetches have completed. 1331 /// Useful in tests to deterministically execute all scheduled work. 1332 /// `max_iterations` caps the loop to prevent infinite loops with 1333 /// recurring intervals; pass 0 for unlimited. 1334 pub fn run_event_loop(&mut self, max_iterations: usize) -> Result<(), RuntimeError> { 1335 let mut iterations = 0; 1336 while crate::timers::has_pending_timers() || crate::fetch::has_pending_fetches() { 1337 if max_iterations > 0 && iterations >= max_iterations { 1338 break; 1339 } 1340 self.drain_due_timers()?; 1341 self.drain_completed_fetches()?; 1342 iterations += 1; 1343 // If work is still pending, sleep briefly to avoid spinning. 1344 if crate::timers::has_pending_timers() || crate::fetch::has_pending_fetches() { 1345 std::thread::sleep(std::time::Duration::from_millis(1)); 1346 } 1347 } 1348 Ok(()) 1349 } 1350 1351 /// Ensure the register file has at least `needed` slots. 1352 fn ensure_registers(&mut self, needed: usize) { 1353 if needed > self.registers.len() { 1354 if needed > MAX_REGISTERS { 1355 return; 1356 } 1357 self.registers.resize(needed, Value::Undefined); 1358 } 1359 } 1360 1361 /// Read a u8 from the current frame's bytecode and advance IP. 1362 #[inline] 1363 fn read_u8(frame: &mut CallFrame) -> u8 { 1364 let b = frame.func.code[frame.ip]; 1365 frame.ip += 1; 1366 b 1367 } 1368 1369 /// Read a u16 (little-endian) from the current frame's bytecode and advance IP. 1370 #[inline] 1371 fn read_u16(frame: &mut CallFrame) -> u16 { 1372 let lo = frame.func.code[frame.ip]; 1373 let hi = frame.func.code[frame.ip + 1]; 1374 frame.ip += 2; 1375 u16::from_le_bytes([lo, hi]) 1376 } 1377 1378 /// Read an i32 (little-endian) from the current frame's bytecode and advance IP. 1379 #[inline] 1380 fn read_i32(frame: &mut CallFrame) -> i32 { 1381 let bytes = [ 1382 frame.func.code[frame.ip], 1383 frame.func.code[frame.ip + 1], 1384 frame.func.code[frame.ip + 2], 1385 frame.func.code[frame.ip + 3], 1386 ]; 1387 frame.ip += 4; 1388 i32::from_le_bytes(bytes) 1389 } 1390 1391 // ── Generator helpers ────────────────────────────────────── 1392 1393 /// Create a generator object from a generator function. 1394 fn create_generator_object( 1395 &mut self, 1396 func: Function, 1397 upvalues: Vec<GcRef>, 1398 args: &[Value], 1399 ) -> GcRef { 1400 // Pre-fill registers with arguments. 1401 let reg_count = func.register_count as usize; 1402 let mut regs = vec![Value::Undefined; reg_count]; 1403 for (i, arg) in args.iter().enumerate() { 1404 if i < func.param_count as usize { 1405 regs[i] = arg.clone(); 1406 } 1407 } 1408 1409 let gen_data = GeneratorData { 1410 state: GeneratorState::NotStarted, 1411 func, 1412 upvalues, 1413 registers: regs, 1414 ip: 0, 1415 prototype: self.object_prototype, 1416 exception_handlers: Vec::new(), 1417 }; 1418 1419 let gen_ref = self.gc.alloc(HeapObject::Generator(Box::new(gen_data))); 1420 1421 // Wrap in an object with next/return/throw methods. 1422 let mut obj = ObjectData::new(); 1423 obj.prototype = self.object_prototype; 1424 1425 // Store the generator GcRef so methods can find it. 1426 obj.properties.insert( 1427 "__gen__".to_string(), 1428 Property { 1429 value: Value::Object(gen_ref), 1430 writable: false, 1431 enumerable: false, 1432 configurable: false, 1433 }, 1434 ); 1435 1436 // next() method 1437 let next_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1438 name: "next".to_string(), 1439 kind: FunctionKind::Native(NativeFunc { 1440 callback: generator_next, 1441 }), 1442 prototype_obj: None, 1443 properties: HashMap::new(), 1444 upvalues: Vec::new(), 1445 }))); 1446 obj.properties.insert( 1447 "next".to_string(), 1448 Property::builtin(Value::Function(next_fn)), 1449 ); 1450 1451 // return() method 1452 let return_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1453 name: "return".to_string(), 1454 kind: FunctionKind::Native(NativeFunc { 1455 callback: generator_return, 1456 }), 1457 prototype_obj: None, 1458 properties: HashMap::new(), 1459 upvalues: Vec::new(), 1460 }))); 1461 obj.properties.insert( 1462 "return".to_string(), 1463 Property::builtin(Value::Function(return_fn)), 1464 ); 1465 1466 // throw() method 1467 let throw_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1468 name: "throw".to_string(), 1469 kind: FunctionKind::Native(NativeFunc { 1470 callback: generator_throw, 1471 }), 1472 prototype_obj: None, 1473 properties: HashMap::new(), 1474 upvalues: Vec::new(), 1475 }))); 1476 obj.properties.insert( 1477 "throw".to_string(), 1478 Property::builtin(Value::Function(throw_fn)), 1479 ); 1480 1481 // @@iterator method (generators are iterable - returns self) 1482 let iter_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1483 name: "[Symbol.iterator]".to_string(), 1484 kind: FunctionKind::Native(NativeFunc { 1485 callback: generator_symbol_iterator, 1486 }), 1487 prototype_obj: None, 1488 properties: HashMap::new(), 1489 upvalues: Vec::new(), 1490 }))); 1491 obj.properties.insert( 1492 "@@iterator".to_string(), 1493 Property::builtin(Value::Function(iter_fn)), 1494 ); 1495 1496 self.gc.alloc(HeapObject::Object(obj)) 1497 } 1498 1499 /// Create a raw GeneratorData (HeapObject::Generator) without the wrapper object. 1500 /// Used by async functions which manage their own driving logic. 1501 fn create_raw_generator( 1502 &mut self, 1503 func: Function, 1504 upvalues: Vec<GcRef>, 1505 args: &[Value], 1506 ) -> GcRef { 1507 let reg_count = func.register_count as usize; 1508 let mut regs = vec![Value::Undefined; reg_count]; 1509 for (i, arg) in args.iter().enumerate() { 1510 if i < func.param_count as usize { 1511 regs[i] = arg.clone(); 1512 } 1513 } 1514 1515 let gen_data = GeneratorData { 1516 state: GeneratorState::NotStarted, 1517 func, 1518 upvalues, 1519 registers: regs, 1520 ip: 0, 1521 prototype: self.object_prototype, 1522 exception_handlers: Vec::new(), 1523 }; 1524 1525 self.gc.alloc(HeapObject::Generator(Box::new(gen_data))) 1526 } 1527 1528 /// Create a {value, done} iterator result object. 1529 fn make_iterator_result(&mut self, value: Value, done: bool) -> Value { 1530 let mut obj = ObjectData::new(); 1531 obj.prototype = self.object_prototype; 1532 obj.properties 1533 .insert("value".to_string(), Property::data(value)); 1534 obj.properties 1535 .insert("done".to_string(), Property::data(Value::Boolean(done))); 1536 let gc_ref = self.gc.alloc(HeapObject::Object(obj)); 1537 Value::Object(gc_ref) 1538 } 1539 1540 /// Run a generator until its next yield/return. 1541 /// Returns the yielded/returned value. 1542 pub fn run_generator( 1543 &mut self, 1544 gen_ref: GcRef, 1545 send_value: Value, 1546 ) -> Result<Value, RuntimeError> { 1547 // Extract generator data. 1548 let (func, upvalues, mut regs, ip, state, saved_exc_handlers) = match self.gc.get(gen_ref) { 1549 Some(HeapObject::Generator(gen)) => { 1550 if gen.state == GeneratorState::Completed { 1551 return Ok(self.make_iterator_result(Value::Undefined, true)); 1552 } 1553 if gen.state == GeneratorState::Executing { 1554 return Err(RuntimeError::type_error("Generator is already executing")); 1555 } 1556 ( 1557 gen.func.clone(), 1558 gen.upvalues.clone(), 1559 gen.registers.clone(), 1560 gen.ip, 1561 gen.state, 1562 gen.exception_handlers.clone(), 1563 ) 1564 } 1565 _ => return Err(RuntimeError::type_error("not a generator")), 1566 }; 1567 1568 // Mark as executing. 1569 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 1570 gen.state = GeneratorState::Executing; 1571 } 1572 1573 // If resuming from a yield/await, write the sent value into the dst register. 1574 if state == GeneratorState::Suspended && ip >= 3 { 1575 // The Yield/Await instruction was: op dst, src (3 bytes total: op + dst + src) 1576 // After executing, ip points past it. The dst byte is at ip - 2. 1577 let dst_reg = func.code[ip - 2] as usize; 1578 regs[dst_reg] = send_value; 1579 } 1580 1581 // Save current VM state. 1582 let saved_frames = std::mem::take(&mut self.frames); 1583 let saved_instructions = self.instructions_executed; 1584 1585 // Use a base past any existing register usage to avoid clobbering 1586 // the caller's register file. 1587 let base = saved_frames 1588 .last() 1589 .map(|f| f.base + f.func.register_count as usize) 1590 .unwrap_or(0); 1591 1592 let reg_count = func.register_count as usize; 1593 self.ensure_registers(base + reg_count + 1); 1594 1595 // Set up registers for the generator. 1596 for (i, val) in regs.iter().enumerate() { 1597 self.registers[base + i] = val.clone(); 1598 } 1599 1600 // Push frame. return_reg points to a slot that holds the generator ref 1601 // so Yield can find it. We use a slot just past the registers. 1602 self.registers[base + reg_count] = Value::Object(gen_ref); 1603 1604 // Restore exception handlers. 1605 let exception_handlers = saved_exc_handlers 1606 .iter() 1607 .map(|&(catch_ip, catch_reg)| ExceptionHandler { 1608 catch_ip, 1609 catch_reg, 1610 }) 1611 .collect(); 1612 1613 self.frames.push(CallFrame { 1614 func, 1615 ip, 1616 base, 1617 return_reg: base + reg_count, 1618 exception_handlers, 1619 upvalues, 1620 }); 1621 1622 let result = self.run(); 1623 1624 // Restore VM state. 1625 self.frames = saved_frames; 1626 self.instructions_executed = saved_instructions; 1627 1628 match result { 1629 Ok(val) => { 1630 // Normal return from generator (either via Return or end of function). 1631 // Check if it was a Yield (state == Suspended) or a Return (state stays Executing). 1632 let gen_state = match self.gc.get(gen_ref) { 1633 Some(HeapObject::Generator(gen)) => gen.state, 1634 _ => GeneratorState::Completed, 1635 }; 1636 if gen_state == GeneratorState::Suspended { 1637 // Yield already created the result; `val` is the {value, done} object. 1638 Ok(val) 1639 } else { 1640 // Return: mark completed and wrap result. 1641 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 1642 gen.state = GeneratorState::Completed; 1643 } 1644 Ok(self.make_iterator_result(val, true)) 1645 } 1646 } 1647 Err(err) => { 1648 // Generator threw: mark completed. 1649 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 1650 gen.state = GeneratorState::Completed; 1651 } 1652 Err(err) 1653 } 1654 } 1655 } 1656 1657 /// Throw a value into a suspended generator. Used for `await` on rejected 1658 /// promises — the rejection reason is thrown so that try/catch can handle it. 1659 fn throw_into_generator( 1660 &mut self, 1661 gen_ref: GcRef, 1662 throw_value: Value, 1663 ) -> Result<Value, RuntimeError> { 1664 // Extract generator data. 1665 let (func, upvalues, regs, ip, state, saved_exc_handlers) = match self.gc.get(gen_ref) { 1666 Some(HeapObject::Generator(gen)) => { 1667 if gen.state == GeneratorState::Completed { 1668 return Ok(self.make_iterator_result(Value::Undefined, true)); 1669 } 1670 if gen.state == GeneratorState::Executing { 1671 return Err(RuntimeError::type_error("Generator is already executing")); 1672 } 1673 ( 1674 gen.func.clone(), 1675 gen.upvalues.clone(), 1676 gen.registers.clone(), 1677 gen.ip, 1678 gen.state, 1679 gen.exception_handlers.clone(), 1680 ) 1681 } 1682 _ => return Err(RuntimeError::type_error("not a generator")), 1683 }; 1684 1685 if state == GeneratorState::NotStarted { 1686 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 1687 gen.state = GeneratorState::Completed; 1688 } 1689 return Err(RuntimeError { 1690 kind: ErrorKind::Error, 1691 message: throw_value.to_js_string(&self.gc), 1692 }); 1693 } 1694 1695 // Mark as executing. 1696 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 1697 gen.state = GeneratorState::Executing; 1698 } 1699 1700 // Save current VM state. 1701 let saved_frames = std::mem::take(&mut self.frames); 1702 let saved_instructions = self.instructions_executed; 1703 1704 let base = saved_frames 1705 .last() 1706 .map(|f| f.base + f.func.register_count as usize) 1707 .unwrap_or(0); 1708 1709 let reg_count = func.register_count as usize; 1710 self.ensure_registers(base + reg_count + 1); 1711 1712 for (i, val) in regs.iter().enumerate() { 1713 self.registers[base + i] = val.clone(); 1714 } 1715 1716 self.registers[base + reg_count] = Value::Object(gen_ref); 1717 1718 // Restore exception handlers from the generator. 1719 let exception_handlers = saved_exc_handlers 1720 .iter() 1721 .map(|&(catch_ip, catch_reg)| ExceptionHandler { 1722 catch_ip, 1723 catch_reg, 1724 }) 1725 .collect(); 1726 1727 self.frames.push(CallFrame { 1728 func, 1729 ip, 1730 base, 1731 return_reg: base + reg_count, 1732 exception_handlers, 1733 upvalues, 1734 }); 1735 1736 // Instead of writing send_value to dst register, throw the value. 1737 let caught = self.handle_exception(throw_value); 1738 let result = if caught { 1739 self.run() 1740 } else { 1741 let msg = "Uncaught (in async)".to_string(); 1742 Err(RuntimeError { 1743 kind: ErrorKind::Error, 1744 message: msg, 1745 }) 1746 }; 1747 1748 // Restore VM state. 1749 self.frames = saved_frames; 1750 self.instructions_executed = saved_instructions; 1751 1752 match result { 1753 Ok(val) => { 1754 let gen_state = match self.gc.get(gen_ref) { 1755 Some(HeapObject::Generator(gen)) => gen.state, 1756 _ => GeneratorState::Completed, 1757 }; 1758 if gen_state == GeneratorState::Suspended { 1759 Ok(val) 1760 } else { 1761 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 1762 gen.state = GeneratorState::Completed; 1763 } 1764 Ok(self.make_iterator_result(val, true)) 1765 } 1766 } 1767 Err(err) => { 1768 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 1769 gen.state = GeneratorState::Completed; 1770 } 1771 Err(err) 1772 } 1773 } 1774 } 1775 1776 // ── Iterator protocol helpers ──────────────────────────────── 1777 1778 /// Get an iterator from a value by calling its [Symbol.iterator]() method. 1779 pub fn get_iterator(&mut self, iterable: &Value) -> Result<Value, RuntimeError> { 1780 // Get the @@iterator property. 1781 let iter_fn = match iterable { 1782 Value::Object(gc_ref) | Value::Function(gc_ref) => { 1783 gc_get_property(&self.gc, *gc_ref, "@@iterator") 1784 } 1785 Value::String(_) => { 1786 // Strings have @@iterator on their prototype. 1787 self.string_prototype 1788 .map(|p| gc_get_property(&self.gc, p, "@@iterator")) 1789 .unwrap_or(Value::Undefined) 1790 } 1791 _ => Value::Undefined, 1792 }; 1793 1794 let iter_fn_ref = match iter_fn { 1795 Value::Function(r) => r, 1796 _ => { 1797 return Err(RuntimeError::type_error( 1798 "object is not iterable (no Symbol.iterator)", 1799 )); 1800 } 1801 }; 1802 1803 // Call [Symbol.iterator]() with `this` set to the iterable. 1804 // We temporarily set `this` in globals for the native call. 1805 let old_this = self.globals.get("this").cloned(); 1806 self.globals.insert("this".to_string(), iterable.clone()); 1807 let result = self.call_function(iter_fn_ref, &[]); 1808 match old_this { 1809 Some(v) => self.globals.insert("this".to_string(), v), 1810 None => self.globals.remove("this"), 1811 }; 1812 result 1813 } 1814 1815 /// Call iterator.next() and return (value, done). 1816 pub fn iterator_next(&mut self, iterator: &Value) -> Result<(Value, bool), RuntimeError> { 1817 let iter_ref = match iterator { 1818 Value::Object(r) | Value::Function(r) => *r, 1819 _ => return Err(RuntimeError::type_error("iterator is not an object")), 1820 }; 1821 1822 let next_fn = gc_get_property(&self.gc, iter_ref, "next"); 1823 let next_fn_ref = match next_fn { 1824 Value::Function(r) => r, 1825 _ => return Err(RuntimeError::type_error("iterator.next is not a function")), 1826 }; 1827 1828 // Call next() with `this` = iterator. 1829 let old_this = self.globals.get("this").cloned(); 1830 self.globals.insert("this".to_string(), iterator.clone()); 1831 let result = self.call_function(next_fn_ref, &[])?; 1832 match old_this { 1833 Some(v) => self.globals.insert("this".to_string(), v), 1834 None => self.globals.remove("this"), 1835 }; 1836 1837 // Extract value and done from the result object. 1838 let (value, done) = match result { 1839 Value::Object(r) => { 1840 let val = gc_get_property(&self.gc, r, "value"); 1841 let d = gc_get_property(&self.gc, r, "done"); 1842 (val, d.to_boolean()) 1843 } 1844 _ => (Value::Undefined, true), 1845 }; 1846 1847 Ok((value, done)) 1848 } 1849 1850 // ── Async function helpers ────────────────────────────────── 1851 1852 /// Drive one step of an async function. Runs the internal generator until 1853 /// it yields (await) or returns, then wires up promise reactions for the 1854 /// next step. 1855 fn drive_async_step( 1856 &mut self, 1857 gen_ref: GcRef, 1858 result_promise: GcRef, 1859 send_value: Value, 1860 is_throw: bool, 1861 ) { 1862 let result = if is_throw { 1863 // Throw into the generator — resume it and throw so that 1864 // try/catch inside the async function can handle it. 1865 self.throw_into_generator(gen_ref, send_value) 1866 } else { 1867 self.run_generator(gen_ref, send_value) 1868 }; 1869 1870 match result { 1871 Ok(iter_result) => { 1872 // Extract {value, done} from the iterator result. 1873 let (value, done) = match &iter_result { 1874 Value::Object(r) => { 1875 let val = gc_get_property(&self.gc, *r, "value"); 1876 let d = gc_get_property(&self.gc, *r, "done"); 1877 (val, d.to_boolean()) 1878 } 1879 _ => (Value::Undefined, true), 1880 }; 1881 1882 if done { 1883 // Async function returned — resolve the result promise. 1884 crate::builtins::resolve_promise_internal(&mut self.gc, result_promise, value); 1885 } else { 1886 // Async function awaited — set up promise chain to resume. 1887 self.setup_async_resume(gen_ref, result_promise, value); 1888 } 1889 } 1890 Err(err) => { 1891 // Async function threw — reject the result promise. 1892 // For plain `throw expr` (ErrorKind::Error), reject with the 1893 // message string to preserve the original thrown value. 1894 // For typed errors (TypeError, etc.), wrap in an error object. 1895 let reason = if err.kind == ErrorKind::Error { 1896 Value::String(err.message.clone()) 1897 } else { 1898 err.to_value(&mut self.gc) 1899 }; 1900 crate::builtins::reject_promise_internal(&mut self.gc, result_promise, reason); 1901 } 1902 } 1903 } 1904 1905 /// Set up promise reactions so that when the awaited value settles, the 1906 /// async function resumes. 1907 fn setup_async_resume(&mut self, gen_ref: GcRef, result_promise: GcRef, awaited_value: Value) { 1908 // Create the fulfill callback. 1909 let fulfill_data = self.make_async_resume_data(gen_ref, result_promise, false); 1910 let fulfill_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1911 name: "__async_fulfill__".to_string(), 1912 kind: FunctionKind::Native(NativeFunc { 1913 callback: async_resume_callback, 1914 }), 1915 prototype_obj: None, 1916 properties: { 1917 let mut m = HashMap::new(); 1918 m.insert( 1919 "__async_data__".to_string(), 1920 Property::builtin(Value::Object(fulfill_data)), 1921 ); 1922 m 1923 }, 1924 upvalues: Vec::new(), 1925 }))); 1926 1927 // Create the reject callback. 1928 let reject_data = self.make_async_resume_data(gen_ref, result_promise, true); 1929 let reject_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1930 name: "__async_reject__".to_string(), 1931 kind: FunctionKind::Native(NativeFunc { 1932 callback: async_resume_callback, 1933 }), 1934 prototype_obj: None, 1935 properties: { 1936 let mut m = HashMap::new(); 1937 m.insert( 1938 "__async_data__".to_string(), 1939 Property::builtin(Value::Object(reject_data)), 1940 ); 1941 m 1942 }, 1943 upvalues: Vec::new(), 1944 }))); 1945 1946 // If the awaited value is a promise, react to it. 1947 if crate::builtins::is_promise_pub(&self.gc, &awaited_value) { 1948 let val_ref = awaited_value.gc_ref().unwrap(); 1949 let state = crate::builtins::promise_state_pub(&self.gc, val_ref); 1950 if state == crate::builtins::PROMISE_FULFILLED { 1951 let r = crate::builtins::promise_get_prop_pub( 1952 &self.gc, 1953 val_ref, 1954 crate::builtins::PROMISE_RESULT_KEY, 1955 ); 1956 crate::builtins::enqueue_microtask_pub(crate::builtins::Microtask { 1957 handler: Some(fulfill_fn), 1958 value: r, 1959 chained_promise: None, 1960 is_fulfillment: true, 1961 }); 1962 } else if state == crate::builtins::PROMISE_REJECTED { 1963 let r = crate::builtins::promise_get_prop_pub( 1964 &self.gc, 1965 val_ref, 1966 crate::builtins::PROMISE_RESULT_KEY, 1967 ); 1968 crate::builtins::enqueue_microtask_pub(crate::builtins::Microtask { 1969 handler: Some(reject_fn), 1970 value: r, 1971 chained_promise: None, 1972 is_fulfillment: false, 1973 }); 1974 } else { 1975 // Pending promise: add reactions. 1976 crate::builtins::add_reaction_pub( 1977 &mut self.gc, 1978 val_ref, 1979 Value::Function(fulfill_fn), 1980 Value::Function(reject_fn), 1981 ); 1982 } 1983 } else { 1984 // Not a promise: resume immediately via microtask with the value. 1985 crate::builtins::enqueue_microtask_pub(crate::builtins::Microtask { 1986 handler: Some(fulfill_fn), 1987 value: awaited_value, 1988 chained_promise: None, 1989 is_fulfillment: true, 1990 }); 1991 } 1992 } 1993 1994 /// Create an object holding the data needed to resume an async function. 1995 fn make_async_resume_data( 1996 &mut self, 1997 gen_ref: GcRef, 1998 result_promise: GcRef, 1999 is_throw: bool, 2000 ) -> GcRef { 2001 let mut data = ObjectData::new(); 2002 data.properties.insert( 2003 "__gen_ref__".to_string(), 2004 Property::builtin(Value::Object(gen_ref)), 2005 ); 2006 data.properties.insert( 2007 "__result_promise__".to_string(), 2008 Property::builtin(Value::Object(result_promise)), 2009 ); 2010 data.properties.insert( 2011 "__is_throw__".to_string(), 2012 Property::builtin(Value::Boolean(is_throw)), 2013 ); 2014 self.gc.alloc(HeapObject::Object(data)) 2015 } 2016 2017 /// Create an async generator wrapper object (for `async function*`). 2018 fn create_async_generator_wrapper(&mut self, gen_ref: GcRef) -> GcRef { 2019 let mut obj = ObjectData::new(); 2020 obj.prototype = self.object_prototype; 2021 2022 obj.properties.insert( 2023 "__gen__".to_string(), 2024 Property { 2025 value: Value::Object(gen_ref), 2026 writable: false, 2027 enumerable: false, 2028 configurable: false, 2029 }, 2030 ); 2031 2032 // next() method — returns a Promise for the next iteration result. 2033 let next_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 2034 name: "next".to_string(), 2035 kind: FunctionKind::Native(NativeFunc { 2036 callback: async_generator_next, 2037 }), 2038 prototype_obj: None, 2039 properties: HashMap::new(), 2040 upvalues: Vec::new(), 2041 }))); 2042 obj.properties.insert( 2043 "next".to_string(), 2044 Property::builtin(Value::Function(next_fn)), 2045 ); 2046 2047 // return() method 2048 let return_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 2049 name: "return".to_string(), 2050 kind: FunctionKind::Native(NativeFunc { 2051 callback: async_generator_return, 2052 }), 2053 prototype_obj: None, 2054 properties: HashMap::new(), 2055 upvalues: Vec::new(), 2056 }))); 2057 obj.properties.insert( 2058 "return".to_string(), 2059 Property::builtin(Value::Function(return_fn)), 2060 ); 2061 2062 // @@asyncIterator method — returns self. 2063 let iter_fn = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 2064 name: "[Symbol.asyncIterator]".to_string(), 2065 kind: FunctionKind::Native(NativeFunc { 2066 callback: generator_symbol_iterator, 2067 }), 2068 prototype_obj: None, 2069 properties: HashMap::new(), 2070 upvalues: Vec::new(), 2071 }))); 2072 obj.properties.insert( 2073 "@@asyncIterator".to_string(), 2074 Property::builtin(Value::Function(iter_fn)), 2075 ); 2076 2077 self.gc.alloc(HeapObject::Object(obj)) 2078 } 2079 2080 /// Resolve a dynamic DOM property for a wrapper object. 2081 /// Returns `Some(value)` if the key is a recognized DOM property, `None` otherwise. 2082 fn resolve_dom_property(&mut self, gc_ref: GcRef, key: &str) -> Option<Value> { 2083 let bridge = Rc::clone(self.dom_bridge.as_ref()?); 2084 crate::dom_bridge::resolve_dom_get(&mut self.gc, &bridge, gc_ref, key) 2085 } 2086 2087 /// Handle a DOM property set on a wrapper object. 2088 /// Returns `true` if the property was handled (caller should skip normal set). 2089 fn handle_dom_property_set(&mut self, gc_ref: GcRef, key: &str, val: &Value) -> bool { 2090 if let Some(bridge) = self.dom_bridge.clone() { 2091 // Check for style proxy objects first. 2092 if crate::dom_bridge::handle_style_set(&mut self.gc, &bridge, gc_ref, key, val) { 2093 return true; 2094 } 2095 // Then check for DOM node wrapper sets. 2096 crate::dom_bridge::handle_dom_set(&mut self.gc, &bridge, gc_ref, key, val) 2097 } else { 2098 false 2099 } 2100 } 2101 2102 /// Collect all GcRef values reachable from the mutator (roots for GC). 2103 fn collect_roots(&self) -> Vec<GcRef> { 2104 let mut roots = Vec::new(); 2105 for val in &self.registers { 2106 if let Some(r) = val.gc_ref() { 2107 roots.push(r); 2108 } 2109 } 2110 for val in self.globals.values() { 2111 if let Some(r) = val.gc_ref() { 2112 roots.push(r); 2113 } 2114 } 2115 for frame in &self.frames { 2116 for &uv in &frame.upvalues { 2117 roots.push(uv); 2118 } 2119 } 2120 // Built-in prototype roots. 2121 if let Some(r) = self.object_prototype { 2122 roots.push(r); 2123 } 2124 if let Some(r) = self.array_prototype { 2125 roots.push(r); 2126 } 2127 if let Some(r) = self.string_prototype { 2128 roots.push(r); 2129 } 2130 if let Some(r) = self.number_prototype { 2131 roots.push(r); 2132 } 2133 if let Some(r) = self.boolean_prototype { 2134 roots.push(r); 2135 } 2136 if let Some(r) = self.promise_prototype { 2137 roots.push(r); 2138 } 2139 // DOM wrapper identity cache: keep cached wrappers alive so that the 2140 // same DOM node always returns the same JS object. 2141 if let Some(bridge) = &self.dom_bridge { 2142 for &wrapper_ref in bridge.node_wrappers.borrow().values() { 2143 roots.push(wrapper_ref); 2144 } 2145 // Event listener callbacks must also be GC roots. 2146 for listeners in bridge.event_listeners.borrow().values() { 2147 for listener in listeners { 2148 roots.push(listener.callback); 2149 } 2150 } 2151 } 2152 // Pending timer callbacks must be GC roots. 2153 roots.extend(crate::timers::timer_gc_roots()); 2154 // Pending fetch promises must be GC roots. 2155 roots.extend(crate::fetch::fetch_gc_roots()); 2156 roots 2157 } 2158 2159 /// Main dispatch loop. 2160 fn run(&mut self) -> Result<Value, RuntimeError> { 2161 loop { 2162 let fi = self.frames.len() - 1; 2163 2164 // Check if we've reached the end of bytecode. 2165 if self.frames[fi].ip >= self.frames[fi].func.code.len() { 2166 if self.frames.len() == 1 { 2167 self.frames.pop(); 2168 return Ok(Value::Undefined); 2169 } 2170 let old = self.frames.pop().unwrap(); 2171 self.registers[old.return_reg] = Value::Undefined; 2172 continue; 2173 } 2174 2175 // Instruction limit check (for test harnesses). 2176 if let Some(limit) = self.instruction_limit { 2177 self.instructions_executed += 1; 2178 if self.instructions_executed > limit { 2179 return Err(RuntimeError { 2180 kind: ErrorKind::Error, 2181 message: "instruction limit exceeded".into(), 2182 }); 2183 } 2184 } 2185 2186 let opcode_byte = self.frames[fi].func.code[self.frames[fi].ip]; 2187 self.frames[fi].ip += 1; 2188 2189 let Some(op) = Op::from_byte(opcode_byte) else { 2190 return Err(RuntimeError { 2191 kind: ErrorKind::Error, 2192 message: format!("unknown opcode: 0x{opcode_byte:02X}"), 2193 }); 2194 }; 2195 2196 match op { 2197 // ── Register loads ────────────────────────────── 2198 Op::LoadConst => { 2199 let dst = Self::read_u8(&mut self.frames[fi]); 2200 let idx = Self::read_u16(&mut self.frames[fi]) as usize; 2201 let base = self.frames[fi].base; 2202 let val = match &self.frames[fi].func.constants[idx] { 2203 Constant::Number(n) => Value::Number(*n), 2204 Constant::String(s) => Value::String(s.clone()), 2205 }; 2206 self.registers[base + dst as usize] = val; 2207 } 2208 Op::LoadNull => { 2209 let dst = Self::read_u8(&mut self.frames[fi]); 2210 let base = self.frames[fi].base; 2211 self.registers[base + dst as usize] = Value::Null; 2212 } 2213 Op::LoadUndefined => { 2214 let dst = Self::read_u8(&mut self.frames[fi]); 2215 let base = self.frames[fi].base; 2216 self.registers[base + dst as usize] = Value::Undefined; 2217 } 2218 Op::LoadTrue => { 2219 let dst = Self::read_u8(&mut self.frames[fi]); 2220 let base = self.frames[fi].base; 2221 self.registers[base + dst as usize] = Value::Boolean(true); 2222 } 2223 Op::LoadFalse => { 2224 let dst = Self::read_u8(&mut self.frames[fi]); 2225 let base = self.frames[fi].base; 2226 self.registers[base + dst as usize] = Value::Boolean(false); 2227 } 2228 Op::LoadInt8 => { 2229 let dst = Self::read_u8(&mut self.frames[fi]); 2230 let val = Self::read_u8(&mut self.frames[fi]) as i8; 2231 let base = self.frames[fi].base; 2232 self.registers[base + dst as usize] = Value::Number(val as f64); 2233 } 2234 Op::Move => { 2235 let dst = Self::read_u8(&mut self.frames[fi]); 2236 let src = Self::read_u8(&mut self.frames[fi]); 2237 let base = self.frames[fi].base; 2238 let val = self.registers[base + src as usize].clone(); 2239 self.registers[base + dst as usize] = val; 2240 } 2241 2242 // ── Global access ────────────────────────────── 2243 Op::LoadGlobal => { 2244 let dst = Self::read_u8(&mut self.frames[fi]); 2245 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 2246 let base = self.frames[fi].base; 2247 let name = &self.frames[fi].func.names[name_idx]; 2248 let val = self.globals.get(name).cloned().unwrap_or(Value::Undefined); 2249 self.registers[base + dst as usize] = val; 2250 } 2251 Op::StoreGlobal => { 2252 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 2253 let src = Self::read_u8(&mut self.frames[fi]); 2254 let base = self.frames[fi].base; 2255 let name = self.frames[fi].func.names[name_idx].clone(); 2256 let val = self.registers[base + src as usize].clone(); 2257 self.globals.insert(name, val); 2258 } 2259 2260 // ── Arithmetic ───────────────────────────────── 2261 Op::Add => { 2262 let dst = Self::read_u8(&mut self.frames[fi]); 2263 let lhs_r = Self::read_u8(&mut self.frames[fi]); 2264 let rhs_r = Self::read_u8(&mut self.frames[fi]); 2265 let base = self.frames[fi].base; 2266 let result = add_values( 2267 &self.registers[base + lhs_r as usize], 2268 &self.registers[base + rhs_r as usize], 2269 &self.gc, 2270 ); 2271 self.registers[base + dst as usize] = result; 2272 } 2273 Op::Sub => { 2274 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2275 let result = self.registers[base + lhs_r].to_number() 2276 - self.registers[base + rhs_r].to_number(); 2277 self.registers[base + dst] = Value::Number(result); 2278 } 2279 Op::Mul => { 2280 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2281 let result = self.registers[base + lhs_r].to_number() 2282 * self.registers[base + rhs_r].to_number(); 2283 self.registers[base + dst] = Value::Number(result); 2284 } 2285 Op::Div => { 2286 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2287 let result = self.registers[base + lhs_r].to_number() 2288 / self.registers[base + rhs_r].to_number(); 2289 self.registers[base + dst] = Value::Number(result); 2290 } 2291 Op::Rem => { 2292 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2293 let result = self.registers[base + lhs_r].to_number() 2294 % self.registers[base + rhs_r].to_number(); 2295 self.registers[base + dst] = Value::Number(result); 2296 } 2297 Op::Exp => { 2298 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2299 let result = self.registers[base + lhs_r] 2300 .to_number() 2301 .powf(self.registers[base + rhs_r].to_number()); 2302 self.registers[base + dst] = Value::Number(result); 2303 } 2304 Op::Neg => { 2305 let dst = Self::read_u8(&mut self.frames[fi]); 2306 let src = Self::read_u8(&mut self.frames[fi]); 2307 let base = self.frames[fi].base; 2308 let result = -self.registers[base + src as usize].to_number(); 2309 self.registers[base + dst as usize] = Value::Number(result); 2310 } 2311 2312 // ── Bitwise ──────────────────────────────────── 2313 Op::BitAnd => { 2314 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2315 let a = to_int32(&self.registers[base + lhs_r]); 2316 let b = to_int32(&self.registers[base + rhs_r]); 2317 self.registers[base + dst] = Value::Number((a & b) as f64); 2318 } 2319 Op::BitOr => { 2320 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2321 let a = to_int32(&self.registers[base + lhs_r]); 2322 let b = to_int32(&self.registers[base + rhs_r]); 2323 self.registers[base + dst] = Value::Number((a | b) as f64); 2324 } 2325 Op::BitXor => { 2326 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2327 let a = to_int32(&self.registers[base + lhs_r]); 2328 let b = to_int32(&self.registers[base + rhs_r]); 2329 self.registers[base + dst] = Value::Number((a ^ b) as f64); 2330 } 2331 Op::ShiftLeft => { 2332 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2333 let a = to_int32(&self.registers[base + lhs_r]); 2334 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 2335 self.registers[base + dst] = Value::Number((a << b) as f64); 2336 } 2337 Op::ShiftRight => { 2338 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2339 let a = to_int32(&self.registers[base + lhs_r]); 2340 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 2341 self.registers[base + dst] = Value::Number((a >> b) as f64); 2342 } 2343 Op::UShiftRight => { 2344 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2345 let a = to_uint32(&self.registers[base + lhs_r]); 2346 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 2347 self.registers[base + dst] = Value::Number((a >> b) as f64); 2348 } 2349 Op::BitNot => { 2350 let dst = Self::read_u8(&mut self.frames[fi]); 2351 let src = Self::read_u8(&mut self.frames[fi]); 2352 let base = self.frames[fi].base; 2353 let result = !to_int32(&self.registers[base + src as usize]); 2354 self.registers[base + dst as usize] = Value::Number(result as f64); 2355 } 2356 2357 // ── Comparison ───────────────────────────────── 2358 Op::Eq => { 2359 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2360 let result = 2361 abstract_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 2362 self.registers[base + dst] = Value::Boolean(result); 2363 } 2364 Op::StrictEq => { 2365 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2366 let result = 2367 strict_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 2368 self.registers[base + dst] = Value::Boolean(result); 2369 } 2370 Op::NotEq => { 2371 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2372 let result = 2373 !abstract_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 2374 self.registers[base + dst] = Value::Boolean(result); 2375 } 2376 Op::StrictNotEq => { 2377 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2378 let result = 2379 !strict_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 2380 self.registers[base + dst] = Value::Boolean(result); 2381 } 2382 Op::LessThan => { 2383 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2384 let result = abstract_relational( 2385 &self.registers[base + lhs_r], 2386 &self.registers[base + rhs_r], 2387 |ord| ord == std::cmp::Ordering::Less, 2388 ); 2389 self.registers[base + dst] = Value::Boolean(result); 2390 } 2391 Op::LessEq => { 2392 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2393 let result = abstract_relational( 2394 &self.registers[base + lhs_r], 2395 &self.registers[base + rhs_r], 2396 |ord| ord != std::cmp::Ordering::Greater, 2397 ); 2398 self.registers[base + dst] = Value::Boolean(result); 2399 } 2400 Op::GreaterThan => { 2401 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2402 let result = abstract_relational( 2403 &self.registers[base + lhs_r], 2404 &self.registers[base + rhs_r], 2405 |ord| ord == std::cmp::Ordering::Greater, 2406 ); 2407 self.registers[base + dst] = Value::Boolean(result); 2408 } 2409 Op::GreaterEq => { 2410 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 2411 let result = abstract_relational( 2412 &self.registers[base + lhs_r], 2413 &self.registers[base + rhs_r], 2414 |ord| ord != std::cmp::Ordering::Less, 2415 ); 2416 self.registers[base + dst] = Value::Boolean(result); 2417 } 2418 2419 // ── Logical / unary ──────────────────────────── 2420 Op::LogicalNot => { 2421 let dst = Self::read_u8(&mut self.frames[fi]); 2422 let src = Self::read_u8(&mut self.frames[fi]); 2423 let base = self.frames[fi].base; 2424 let result = !self.registers[base + src as usize].to_boolean(); 2425 self.registers[base + dst as usize] = Value::Boolean(result); 2426 } 2427 Op::TypeOf => { 2428 let dst = Self::read_u8(&mut self.frames[fi]); 2429 let src = Self::read_u8(&mut self.frames[fi]); 2430 let base = self.frames[fi].base; 2431 let t = self.registers[base + src as usize].type_of(); 2432 self.registers[base + dst as usize] = Value::String(t.to_string()); 2433 } 2434 Op::InstanceOf => { 2435 let dst = Self::read_u8(&mut self.frames[fi]); 2436 let lhs_r = Self::read_u8(&mut self.frames[fi]); 2437 let rhs_r = Self::read_u8(&mut self.frames[fi]); 2438 let base = self.frames[fi].base; 2439 let result = match ( 2440 &self.registers[base + lhs_r as usize], 2441 &self.registers[base + rhs_r as usize], 2442 ) { 2443 (Value::Object(obj_ref), Value::Function(ctor_ref)) => { 2444 gc_instanceof(&self.gc, *obj_ref, *ctor_ref) 2445 } 2446 (_, Value::Function(_)) => false, 2447 _ => { 2448 return Err(RuntimeError::type_error( 2449 "Right-hand side of instanceof is not callable", 2450 )); 2451 } 2452 }; 2453 self.registers[base + dst as usize] = Value::Boolean(result); 2454 } 2455 Op::In => { 2456 let dst = Self::read_u8(&mut self.frames[fi]); 2457 let key_r = Self::read_u8(&mut self.frames[fi]); 2458 let obj_r = Self::read_u8(&mut self.frames[fi]); 2459 let base = self.frames[fi].base; 2460 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 2461 let result = match self.registers[base + obj_r as usize] { 2462 Value::Object(gc_ref) => { 2463 Value::Boolean(gc_has_property(&self.gc, gc_ref, &key)) 2464 } 2465 _ => { 2466 return Err(RuntimeError::type_error( 2467 "Cannot use 'in' operator to search for property in non-object", 2468 )); 2469 } 2470 }; 2471 self.registers[base + dst as usize] = result; 2472 } 2473 Op::Void => { 2474 let dst = Self::read_u8(&mut self.frames[fi]); 2475 let _src = Self::read_u8(&mut self.frames[fi]); 2476 let base = self.frames[fi].base; 2477 self.registers[base + dst as usize] = Value::Undefined; 2478 } 2479 2480 // ── Control flow ─────────────────────────────── 2481 Op::Jump => { 2482 let offset = Self::read_i32(&mut self.frames[fi]); 2483 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 2484 } 2485 Op::JumpIfTrue => { 2486 let reg = Self::read_u8(&mut self.frames[fi]); 2487 let offset = Self::read_i32(&mut self.frames[fi]); 2488 let base = self.frames[fi].base; 2489 if self.registers[base + reg as usize].to_boolean() { 2490 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 2491 } 2492 } 2493 Op::JumpIfFalse => { 2494 let reg = Self::read_u8(&mut self.frames[fi]); 2495 let offset = Self::read_i32(&mut self.frames[fi]); 2496 let base = self.frames[fi].base; 2497 if !self.registers[base + reg as usize].to_boolean() { 2498 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 2499 } 2500 } 2501 Op::JumpIfNullish => { 2502 let reg = Self::read_u8(&mut self.frames[fi]); 2503 let offset = Self::read_i32(&mut self.frames[fi]); 2504 let base = self.frames[fi].base; 2505 if self.registers[base + reg as usize].is_nullish() { 2506 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 2507 } 2508 } 2509 2510 // ── Functions / calls ────────────────────────── 2511 Op::Call => { 2512 let dst = Self::read_u8(&mut self.frames[fi]); 2513 let func_r = Self::read_u8(&mut self.frames[fi]); 2514 let args_start = Self::read_u8(&mut self.frames[fi]); 2515 let arg_count = Self::read_u8(&mut self.frames[fi]); 2516 let base = self.frames[fi].base; 2517 2518 // Extract function GcRef. 2519 let func_gc_ref = match self.registers[base + func_r as usize] { 2520 Value::Function(r) => r, 2521 _ => { 2522 let desc = 2523 self.registers[base + func_r as usize].to_js_string(&self.gc); 2524 let err = RuntimeError::type_error(format!("{desc} is not a function")); 2525 let err_val = err.to_value(&mut self.gc); 2526 if !self.handle_exception(err_val) { 2527 return Err(err); 2528 } 2529 continue; 2530 } 2531 }; 2532 2533 // Collect arguments. 2534 let mut args = Vec::with_capacity(arg_count as usize); 2535 for i in 0..arg_count { 2536 args.push(self.registers[base + (args_start + i) as usize].clone()); 2537 } 2538 2539 // Read function data from GC (scoped borrow). 2540 let call_info = { 2541 match self.gc.get(func_gc_ref) { 2542 Some(HeapObject::Function(fdata)) => match &fdata.kind { 2543 FunctionKind::Native(n) => CallInfo::Native(n.callback), 2544 FunctionKind::Bytecode(bc) => { 2545 CallInfo::Bytecode(bc.func.clone(), fdata.upvalues.clone()) 2546 } 2547 }, 2548 _ => { 2549 let err = RuntimeError::type_error("not a function"); 2550 let err_val = err.to_value(&mut self.gc); 2551 if !self.handle_exception(err_val) { 2552 return Err(err); 2553 } 2554 continue; 2555 } 2556 } 2557 }; 2558 2559 match call_info { 2560 CallInfo::Native(callback) => { 2561 // Set async resume data if the function has it. 2562 if let Some(HeapObject::Function(f)) = self.gc.get(func_gc_ref) { 2563 if let Some(prop) = f.properties.get("__async_data__") { 2564 if let Value::Object(data_ref) = &prop.value { 2565 ASYNC_RESUME_DATA.with(|cell| cell.set(Some(*data_ref))); 2566 } 2567 } 2568 } 2569 2570 let this = self 2571 .globals 2572 .get("this") 2573 .cloned() 2574 .unwrap_or(Value::Undefined); 2575 let dom_ref = self.dom_bridge.as_deref(); 2576 let mut ctx = NativeContext { 2577 gc: &mut self.gc, 2578 this, 2579 console_output: &*self.console_output, 2580 dom_bridge: dom_ref, 2581 }; 2582 match callback(&args, &mut ctx) { 2583 Ok(val) => { 2584 // Check if this is a generator resume request. 2585 if let Value::Object(r) = &val { 2586 let is_resume = matches!( 2587 gc_get_property(&self.gc, *r, "__generator_resume__"), 2588 Value::Boolean(true) 2589 ); 2590 if is_resume { 2591 let gen_ref = match gc_get_property( 2592 &self.gc, 2593 *r, 2594 "__gen_ref__", 2595 ) { 2596 Value::Object(gr) => gr, 2597 _ => { 2598 self.registers[base + dst as usize] = 2599 Value::Undefined; 2600 continue; 2601 } 2602 }; 2603 let send_val = 2604 gc_get_property(&self.gc, *r, "__send_value__"); 2605 let kind = match gc_get_property( 2606 &self.gc, 2607 *r, 2608 "__resume_kind__", 2609 ) { 2610 Value::String(s) => s, 2611 _ => "next".to_string(), 2612 }; 2613 2614 match kind.as_str() { 2615 "next" => { 2616 match self.run_generator(gen_ref, send_val) { 2617 Ok(result) => { 2618 self.registers[base + dst as usize] = 2619 result; 2620 } 2621 Err(err) => { 2622 let err_val = 2623 err.to_value(&mut self.gc); 2624 if !self.handle_exception(err_val) { 2625 return Err(err); 2626 } 2627 } 2628 } 2629 } 2630 "return" => { 2631 // Force the generator to complete. 2632 if let Some(HeapObject::Generator(gen)) = 2633 self.gc.get_mut(gen_ref) 2634 { 2635 gen.state = GeneratorState::Completed; 2636 } 2637 let result = 2638 self.make_iterator_result(send_val, true); 2639 self.registers[base + dst as usize] = result; 2640 } 2641 "throw" => { 2642 // Mark generator as completed and throw. 2643 if let Some(HeapObject::Generator(gen)) = 2644 self.gc.get_mut(gen_ref) 2645 { 2646 gen.state = GeneratorState::Completed; 2647 } 2648 if !self.handle_exception(send_val) { 2649 return Err(RuntimeError::type_error( 2650 "Generator throw", 2651 )); 2652 } 2653 } 2654 _ => { 2655 self.registers[base + dst as usize] = 2656 Value::Undefined; 2657 } 2658 } 2659 continue; 2660 } 2661 2662 // Check for async resume marker. 2663 let is_async_resume = matches!( 2664 gc_get_property(&self.gc, *r, "__async_resume__"), 2665 Value::Boolean(true) 2666 ); 2667 if is_async_resume { 2668 let ar_gen = match gc_get_property( 2669 &self.gc, 2670 *r, 2671 "__gen_ref__", 2672 ) { 2673 Value::Object(gr) => gr, 2674 _ => { 2675 self.registers[base + dst as usize] = 2676 Value::Undefined; 2677 continue; 2678 } 2679 }; 2680 let ar_promise = match gc_get_property( 2681 &self.gc, 2682 *r, 2683 "__result_promise__", 2684 ) { 2685 Value::Object(pr) => pr, 2686 _ => { 2687 self.registers[base + dst as usize] = 2688 Value::Undefined; 2689 continue; 2690 } 2691 }; 2692 let ar_throw = matches!( 2693 gc_get_property(&self.gc, *r, "__is_throw__"), 2694 Value::Boolean(true) 2695 ); 2696 let ar_value = 2697 gc_get_property(&self.gc, *r, "__value__"); 2698 self.drive_async_step( 2699 ar_gen, ar_promise, ar_value, ar_throw, 2700 ); 2701 self.registers[base + dst as usize] = Value::Undefined; 2702 continue; 2703 } 2704 2705 // Check for async generator resume marker. 2706 let is_ag_resume = matches!( 2707 gc_get_property( 2708 &self.gc, 2709 *r, 2710 "__async_generator_resume__" 2711 ), 2712 Value::Boolean(true) 2713 ); 2714 if is_ag_resume { 2715 let ag_gen = match gc_get_property( 2716 &self.gc, 2717 *r, 2718 "__gen_ref__", 2719 ) { 2720 Value::Object(gr) => gr, 2721 _ => { 2722 self.registers[base + dst as usize] = 2723 Value::Undefined; 2724 continue; 2725 } 2726 }; 2727 let ag_send = 2728 gc_get_property(&self.gc, *r, "__send_value__"); 2729 let ag_kind = match gc_get_property( 2730 &self.gc, 2731 *r, 2732 "__resume_kind__", 2733 ) { 2734 Value::String(s) => s, 2735 _ => "next".to_string(), 2736 }; 2737 2738 let promise = 2739 crate::builtins::create_promise_object_pub( 2740 &mut self.gc, 2741 ); 2742 2743 match ag_kind.as_str() { 2744 "next" => { 2745 match self.run_generator(ag_gen, ag_send) { 2746 Ok(iter_result) => { 2747 crate::builtins::resolve_promise_internal( 2748 &mut self.gc, 2749 promise, 2750 iter_result, 2751 ); 2752 } 2753 Err(err) => { 2754 let reason = err.to_value(&mut self.gc); 2755 crate::builtins::reject_promise_internal( 2756 &mut self.gc, 2757 promise, 2758 reason, 2759 ); 2760 } 2761 } 2762 } 2763 "return" => { 2764 if let Some(HeapObject::Generator(gen)) = 2765 self.gc.get_mut(ag_gen) 2766 { 2767 gen.state = GeneratorState::Completed; 2768 } 2769 let result = 2770 self.make_iterator_result(ag_send, true); 2771 crate::builtins::resolve_promise_internal( 2772 &mut self.gc, 2773 promise, 2774 result, 2775 ); 2776 } 2777 _ => {} 2778 } 2779 self.registers[base + dst as usize] = 2780 Value::Object(promise); 2781 continue; 2782 } 2783 2784 // Check for event dispatch marker. 2785 let is_event_dispatch = matches!( 2786 gc_get_property(&self.gc, *r, "__event_dispatch__"), 2787 Value::Boolean(true) 2788 ); 2789 if is_event_dispatch { 2790 let target_idx = match gc_get_property( 2791 &self.gc, 2792 *r, 2793 "__target_id__", 2794 ) { 2795 Value::Number(n) => n as usize, 2796 _ => { 2797 self.registers[base + dst as usize] = 2798 Value::Boolean(true); 2799 continue; 2800 } 2801 }; 2802 let evt_ref = match gc_get_property( 2803 &self.gc, 2804 *r, 2805 "__event_ref__", 2806 ) { 2807 Value::Object(er) => er, 2808 _ => { 2809 self.registers[base + dst as usize] = 2810 Value::Boolean(true); 2811 continue; 2812 } 2813 }; 2814 let result = crate::dom_bridge::run_event_dispatch( 2815 self, target_idx, evt_ref, 2816 ); 2817 self.registers[base + dst as usize] = result; 2818 continue; 2819 } 2820 } 2821 self.registers[base + dst as usize] = val; 2822 } 2823 Err(err) => { 2824 let err_val = err.to_value(&mut self.gc); 2825 if !self.handle_exception(err_val) { 2826 return Err(err); 2827 } 2828 } 2829 } 2830 } 2831 CallInfo::Bytecode(callee_func, callee_upvalues) => { 2832 // Async function: create generator + promise, drive async. 2833 if callee_func.is_async && !callee_func.is_generator { 2834 let gen_ref = 2835 self.create_raw_generator(callee_func, callee_upvalues, &args); 2836 let result_promise = 2837 crate::builtins::create_promise_object_pub(&mut self.gc); 2838 self.drive_async_step( 2839 gen_ref, 2840 result_promise, 2841 Value::Undefined, 2842 false, 2843 ); 2844 self.registers[base + dst as usize] = Value::Object(result_promise); 2845 continue; 2846 } 2847 2848 // Async generator function: create async generator wrapper. 2849 if callee_func.is_async && callee_func.is_generator { 2850 let gen_ref = 2851 self.create_raw_generator(callee_func, callee_upvalues, &args); 2852 let wrapper = self.create_async_generator_wrapper(gen_ref); 2853 self.registers[base + dst as usize] = Value::Object(wrapper); 2854 continue; 2855 } 2856 2857 // Generator function: create a generator object instead of executing. 2858 if callee_func.is_generator { 2859 let gen_obj = self.create_generator_object( 2860 callee_func, 2861 callee_upvalues, 2862 &args, 2863 ); 2864 self.registers[base + dst as usize] = Value::Object(gen_obj); 2865 continue; 2866 } 2867 2868 if self.frames.len() >= MAX_CALL_DEPTH { 2869 let err = 2870 RuntimeError::range_error("Maximum call stack size exceeded"); 2871 let err_val = err.to_value(&mut self.gc); 2872 if !self.handle_exception(err_val) { 2873 return Err(err); 2874 } 2875 continue; 2876 } 2877 2878 let callee_base = base + self.frames[fi].func.register_count as usize; 2879 let callee_regs = callee_func.register_count as usize; 2880 self.ensure_registers(callee_base + callee_regs); 2881 2882 // Copy arguments into callee's registers. 2883 for i in 0..callee_func.param_count.min(arg_count) { 2884 self.registers[callee_base + i as usize] = args[i as usize].clone(); 2885 } 2886 // Fill remaining params with undefined. 2887 for i in arg_count..callee_func.param_count { 2888 self.registers[callee_base + i as usize] = Value::Undefined; 2889 } 2890 2891 self.frames.push(CallFrame { 2892 func: callee_func, 2893 ip: 0, 2894 base: callee_base, 2895 return_reg: base + dst as usize, 2896 exception_handlers: Vec::new(), 2897 upvalues: callee_upvalues, 2898 }); 2899 } 2900 } 2901 } 2902 Op::Return => { 2903 let reg = Self::read_u8(&mut self.frames[fi]); 2904 let base = self.frames[fi].base; 2905 let val = self.registers[base + reg as usize].clone(); 2906 2907 if self.frames.len() == 1 { 2908 self.frames.pop(); 2909 return Ok(val); 2910 } 2911 2912 let old = self.frames.pop().unwrap(); 2913 self.registers[old.return_reg] = val; 2914 } 2915 Op::Throw => { 2916 let reg = Self::read_u8(&mut self.frames[fi]); 2917 let base = self.frames[fi].base; 2918 let val = self.registers[base + reg as usize].clone(); 2919 2920 if !self.handle_exception(val) { 2921 let msg = self.registers[base + reg as usize].to_js_string(&self.gc); 2922 return Err(RuntimeError { 2923 kind: ErrorKind::Error, 2924 message: msg, 2925 }); 2926 } 2927 } 2928 Op::CreateClosure => { 2929 let dst = Self::read_u8(&mut self.frames[fi]); 2930 let func_idx = Self::read_u16(&mut self.frames[fi]) as usize; 2931 let base = self.frames[fi].base; 2932 let inner_func = self.frames[fi].func.functions[func_idx].clone(); 2933 let name = inner_func.name.clone(); 2934 2935 // Resolve upvalues from the parent scope. 2936 let mut upvalues = Vec::with_capacity(inner_func.upvalue_defs.len()); 2937 for def in &inner_func.upvalue_defs { 2938 let cell_ref = if def.is_local { 2939 // Parent has a cell in register `def.index`. 2940 match &self.registers[base + def.index as usize] { 2941 Value::Object(r) => *r, 2942 _ => { 2943 return Err(RuntimeError { 2944 kind: ErrorKind::Error, 2945 message: 2946 "CreateClosure: upvalue register does not hold a cell" 2947 .into(), 2948 }); 2949 } 2950 } 2951 } else { 2952 // Transitive: parent's own upvalue at `def.index`. 2953 self.frames[fi].upvalues[def.index as usize] 2954 }; 2955 upvalues.push(cell_ref); 2956 } 2957 2958 // Create a .prototype object for the function (for instanceof). 2959 let proto_obj = self.gc.alloc(HeapObject::Object(ObjectData::new())); 2960 let gc_ref = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 2961 name, 2962 kind: FunctionKind::Bytecode(BytecodeFunc { func: inner_func }), 2963 prototype_obj: Some(proto_obj), 2964 properties: HashMap::new(), 2965 upvalues, 2966 }))); 2967 // Set .prototype.constructor = this function. 2968 if let Some(HeapObject::Object(data)) = self.gc.get_mut(proto_obj) { 2969 data.properties.insert( 2970 "constructor".to_string(), 2971 Property { 2972 value: Value::Function(gc_ref), 2973 writable: true, 2974 enumerable: false, 2975 configurable: true, 2976 }, 2977 ); 2978 } 2979 self.registers[base + dst as usize] = Value::Function(gc_ref); 2980 2981 // Trigger GC if needed. 2982 if self.gc.should_collect() { 2983 let roots = self.collect_roots(); 2984 self.gc.collect(&roots); 2985 } 2986 } 2987 2988 // ── Object / property ────────────────────────── 2989 Op::GetProperty => { 2990 let dst = Self::read_u8(&mut self.frames[fi]); 2991 let obj_r = Self::read_u8(&mut self.frames[fi]); 2992 let key_r = Self::read_u8(&mut self.frames[fi]); 2993 let base = self.frames[fi].base; 2994 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 2995 // Save gc_ref for DOM interception. 2996 let obj_gc_ref = match self.registers[base + obj_r as usize] { 2997 Value::Object(r) | Value::Function(r) => Some(r), 2998 _ => None, 2999 }; 3000 let val = match self.registers[base + obj_r as usize] { 3001 Value::Object(gc_ref) | Value::Function(gc_ref) => { 3002 gc_get_property(&self.gc, gc_ref, &key) 3003 } 3004 Value::String(ref s) => { 3005 let v = string_get_property(s, &key); 3006 if matches!(v, Value::Undefined) { 3007 self.string_prototype 3008 .map(|p| gc_get_property(&self.gc, p, &key)) 3009 .unwrap_or(Value::Undefined) 3010 } else { 3011 v 3012 } 3013 } 3014 Value::Number(_) => self 3015 .number_prototype 3016 .map(|p| gc_get_property(&self.gc, p, &key)) 3017 .unwrap_or(Value::Undefined), 3018 Value::Boolean(_) => self 3019 .boolean_prototype 3020 .map(|p| gc_get_property(&self.gc, p, &key)) 3021 .unwrap_or(Value::Undefined), 3022 _ => Value::Undefined, 3023 }; 3024 // DOM dynamic property interception. 3025 let val = match val { 3026 Value::Undefined if obj_gc_ref.is_some() => self 3027 .resolve_dom_property(obj_gc_ref.unwrap(), &key) 3028 .unwrap_or(Value::Undefined), 3029 other => other, 3030 }; 3031 self.registers[base + dst as usize] = val; 3032 } 3033 Op::SetProperty => { 3034 let obj_r = Self::read_u8(&mut self.frames[fi]); 3035 let key_r = Self::read_u8(&mut self.frames[fi]); 3036 let val_r = Self::read_u8(&mut self.frames[fi]); 3037 let base = self.frames[fi].base; 3038 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 3039 let val = self.registers[base + val_r as usize].clone(); 3040 let obj_gc = match self.registers[base + obj_r as usize] { 3041 Value::Object(r) => Some(r), 3042 Value::Function(r) => Some(r), 3043 _ => None, 3044 }; 3045 // DOM property set interception. 3046 let dom_handled = obj_gc 3047 .map(|r| self.handle_dom_property_set(r, &key, &val)) 3048 .unwrap_or(false); 3049 if !dom_handled { 3050 if let Some(gc_ref) = obj_gc { 3051 match self.gc.get_mut(gc_ref) { 3052 Some(HeapObject::Object(data)) => { 3053 if let Some(prop) = data.properties.get_mut(&key) { 3054 if prop.writable { 3055 prop.value = val; 3056 } 3057 } else { 3058 data.properties.insert(key, Property::data(val)); 3059 } 3060 } 3061 Some(HeapObject::Function(fdata)) => { 3062 if let Some(prop) = fdata.properties.get_mut(&key) { 3063 if prop.writable { 3064 prop.value = val; 3065 } 3066 } else { 3067 fdata.properties.insert(key, Property::data(val)); 3068 } 3069 } 3070 _ => {} 3071 } 3072 } 3073 } 3074 } 3075 Op::CreateObject => { 3076 let dst = Self::read_u8(&mut self.frames[fi]); 3077 let base = self.frames[fi].base; 3078 let mut obj = ObjectData::new(); 3079 obj.prototype = self.object_prototype; 3080 let gc_ref = self.gc.alloc(HeapObject::Object(obj)); 3081 self.registers[base + dst as usize] = Value::Object(gc_ref); 3082 3083 if self.gc.should_collect() { 3084 let roots = self.collect_roots(); 3085 self.gc.collect(&roots); 3086 } 3087 } 3088 Op::CreateArray => { 3089 let dst = Self::read_u8(&mut self.frames[fi]); 3090 let base = self.frames[fi].base; 3091 let mut obj = ObjectData::new(); 3092 obj.prototype = self.array_prototype; 3093 obj.properties.insert( 3094 "length".to_string(), 3095 Property { 3096 value: Value::Number(0.0), 3097 writable: true, 3098 enumerable: false, 3099 configurable: false, 3100 }, 3101 ); 3102 let gc_ref = self.gc.alloc(HeapObject::Object(obj)); 3103 self.registers[base + dst as usize] = Value::Object(gc_ref); 3104 3105 if self.gc.should_collect() { 3106 let roots = self.collect_roots(); 3107 self.gc.collect(&roots); 3108 } 3109 } 3110 Op::GetPropertyByName => { 3111 let dst = Self::read_u8(&mut self.frames[fi]); 3112 let obj_r = Self::read_u8(&mut self.frames[fi]); 3113 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 3114 let base = self.frames[fi].base; 3115 let key = self.frames[fi].func.names[name_idx].clone(); 3116 let obj_gc_ref = match self.registers[base + obj_r as usize] { 3117 Value::Object(r) | Value::Function(r) => Some(r), 3118 _ => None, 3119 }; 3120 let val = match self.registers[base + obj_r as usize] { 3121 Value::Object(gc_ref) | Value::Function(gc_ref) => { 3122 gc_get_property(&self.gc, gc_ref, &key) 3123 } 3124 Value::String(ref s) => { 3125 let v = string_get_property(s, &key); 3126 if matches!(v, Value::Undefined) { 3127 self.string_prototype 3128 .map(|p| gc_get_property(&self.gc, p, &key)) 3129 .unwrap_or(Value::Undefined) 3130 } else { 3131 v 3132 } 3133 } 3134 Value::Number(_) => self 3135 .number_prototype 3136 .map(|p| gc_get_property(&self.gc, p, &key)) 3137 .unwrap_or(Value::Undefined), 3138 Value::Boolean(_) => self 3139 .boolean_prototype 3140 .map(|p| gc_get_property(&self.gc, p, &key)) 3141 .unwrap_or(Value::Undefined), 3142 _ => Value::Undefined, 3143 }; 3144 // DOM dynamic property interception. 3145 let val = match val { 3146 Value::Undefined if obj_gc_ref.is_some() => self 3147 .resolve_dom_property(obj_gc_ref.unwrap(), &key) 3148 .unwrap_or(Value::Undefined), 3149 other => other, 3150 }; 3151 self.registers[base + dst as usize] = val; 3152 } 3153 Op::SetPropertyByName => { 3154 let obj_r = Self::read_u8(&mut self.frames[fi]); 3155 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 3156 let val_r = Self::read_u8(&mut self.frames[fi]); 3157 let base = self.frames[fi].base; 3158 let key = self.frames[fi].func.names[name_idx].clone(); 3159 let val = self.registers[base + val_r as usize].clone(); 3160 let obj_gc = match self.registers[base + obj_r as usize] { 3161 Value::Object(r) => Some(r), 3162 Value::Function(r) => Some(r), 3163 _ => None, 3164 }; 3165 let dom_handled = obj_gc 3166 .map(|r| self.handle_dom_property_set(r, &key, &val)) 3167 .unwrap_or(false); 3168 if !dom_handled { 3169 if let Some(gc_ref) = obj_gc { 3170 match self.gc.get_mut(gc_ref) { 3171 Some(HeapObject::Object(data)) => { 3172 if let Some(prop) = data.properties.get_mut(&key) { 3173 if prop.writable { 3174 prop.value = val; 3175 } 3176 } else { 3177 data.properties.insert(key, Property::data(val)); 3178 } 3179 } 3180 Some(HeapObject::Function(fdata)) => { 3181 if let Some(prop) = fdata.properties.get_mut(&key) { 3182 if prop.writable { 3183 prop.value = val; 3184 } 3185 } else { 3186 fdata.properties.insert(key, Property::data(val)); 3187 } 3188 } 3189 _ => {} 3190 } 3191 } 3192 } 3193 } 3194 3195 // ── Misc ─────────────────────────────────────── 3196 Op::Delete => { 3197 let dst = Self::read_u8(&mut self.frames[fi]); 3198 let obj_r = Self::read_u8(&mut self.frames[fi]); 3199 let key_r = Self::read_u8(&mut self.frames[fi]); 3200 let base = self.frames[fi].base; 3201 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 3202 let result = 3203 if let Value::Object(gc_ref) = self.registers[base + obj_r as usize] { 3204 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 3205 match data.properties.get(&key) { 3206 Some(prop) if !prop.configurable => false, 3207 Some(_) => { 3208 data.properties.remove(&key); 3209 true 3210 } 3211 None => true, 3212 } 3213 } else { 3214 true 3215 } 3216 } else { 3217 true 3218 }; 3219 self.registers[base + dst as usize] = Value::Boolean(result); 3220 } 3221 Op::ForInInit => { 3222 let dst = Self::read_u8(&mut self.frames[fi]); 3223 let obj_r = Self::read_u8(&mut self.frames[fi]); 3224 let base = self.frames[fi].base; 3225 let keys = match self.registers[base + obj_r as usize] { 3226 Value::Object(gc_ref) => gc_enumerate_keys(&self.gc, gc_ref), 3227 _ => Vec::new(), 3228 }; 3229 // Store keys as an array object. 3230 let mut arr = ObjectData::new(); 3231 for (i, key) in keys.iter().enumerate() { 3232 arr.properties 3233 .insert(i.to_string(), Property::data(Value::String(key.clone()))); 3234 } 3235 arr.properties.insert( 3236 "length".to_string(), 3237 Property { 3238 value: Value::Number(keys.len() as f64), 3239 writable: true, 3240 enumerable: false, 3241 configurable: false, 3242 }, 3243 ); 3244 let gc_ref = self.gc.alloc(HeapObject::Object(arr)); 3245 self.registers[base + dst as usize] = Value::Object(gc_ref); 3246 } 3247 Op::ForInNext => { 3248 let dst_val = Self::read_u8(&mut self.frames[fi]); 3249 let dst_done = Self::read_u8(&mut self.frames[fi]); 3250 let keys_r = Self::read_u8(&mut self.frames[fi]); 3251 let idx_r = Self::read_u8(&mut self.frames[fi]); 3252 let base = self.frames[fi].base; 3253 let idx = self.registers[base + idx_r as usize].to_number() as usize; 3254 let len = match self.registers[base + keys_r as usize] { 3255 Value::Object(gc_ref) => { 3256 gc_get_property(&self.gc, gc_ref, "length").to_number() as usize 3257 } 3258 _ => 0, 3259 }; 3260 if idx >= len { 3261 self.registers[base + dst_done as usize] = Value::Boolean(true); 3262 self.registers[base + dst_val as usize] = Value::Undefined; 3263 } else { 3264 let key_str = idx.to_string(); 3265 let key = match self.registers[base + keys_r as usize] { 3266 Value::Object(gc_ref) => gc_get_property(&self.gc, gc_ref, &key_str), 3267 _ => Value::Undefined, 3268 }; 3269 self.registers[base + dst_val as usize] = key; 3270 self.registers[base + dst_done as usize] = Value::Boolean(false); 3271 } 3272 } 3273 Op::SetPrototype => { 3274 let obj_r = Self::read_u8(&mut self.frames[fi]); 3275 let proto_r = Self::read_u8(&mut self.frames[fi]); 3276 let base = self.frames[fi].base; 3277 let proto = match &self.registers[base + proto_r as usize] { 3278 Value::Object(r) => Some(*r), 3279 Value::Null => None, 3280 _ => None, 3281 }; 3282 if let Value::Object(gc_ref) = self.registers[base + obj_r as usize] { 3283 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 3284 data.prototype = proto; 3285 } 3286 } 3287 } 3288 Op::GetPrototype => { 3289 let dst = Self::read_u8(&mut self.frames[fi]); 3290 let obj_r = Self::read_u8(&mut self.frames[fi]); 3291 let base = self.frames[fi].base; 3292 let proto = match self.registers[base + obj_r as usize] { 3293 Value::Object(gc_ref) => match self.gc.get(gc_ref) { 3294 Some(HeapObject::Object(data)) => { 3295 data.prototype.map(Value::Object).unwrap_or(Value::Null) 3296 } 3297 _ => Value::Null, 3298 }, 3299 _ => Value::Null, 3300 }; 3301 self.registers[base + dst as usize] = proto; 3302 } 3303 3304 // ── Exception handling ───────────────────────────── 3305 Op::PushExceptionHandler => { 3306 let catch_reg = Self::read_u8(&mut self.frames[fi]); 3307 let offset = Self::read_i32(&mut self.frames[fi]); 3308 let catch_ip = (self.frames[fi].ip as i32 + offset) as usize; 3309 self.frames[fi].exception_handlers.push(ExceptionHandler { 3310 catch_ip, 3311 catch_reg, 3312 }); 3313 } 3314 Op::PopExceptionHandler => { 3315 self.frames[fi].exception_handlers.pop(); 3316 } 3317 3318 // ── Closure / upvalue ops ───────────────────────── 3319 Op::NewCell => { 3320 let dst = Self::read_u8(&mut self.frames[fi]); 3321 let base = self.frames[fi].base; 3322 let cell = self.gc.alloc(HeapObject::Cell(Value::Undefined)); 3323 self.registers[base + dst as usize] = Value::Object(cell); 3324 3325 if self.gc.should_collect() { 3326 let roots = self.collect_roots(); 3327 self.gc.collect(&roots); 3328 } 3329 } 3330 Op::CellLoad => { 3331 let dst = Self::read_u8(&mut self.frames[fi]); 3332 let cell_reg = Self::read_u8(&mut self.frames[fi]); 3333 let base = self.frames[fi].base; 3334 let cell_ref = match &self.registers[base + cell_reg as usize] { 3335 Value::Object(r) => *r, 3336 _ => { 3337 return Err(RuntimeError { 3338 kind: ErrorKind::Error, 3339 message: "CellLoad: register does not hold a cell".into(), 3340 }); 3341 } 3342 }; 3343 let val = match self.gc.get(cell_ref) { 3344 Some(HeapObject::Cell(v)) => v.clone(), 3345 _ => Value::Undefined, 3346 }; 3347 self.registers[base + dst as usize] = val; 3348 } 3349 Op::CellStore => { 3350 let cell_reg = Self::read_u8(&mut self.frames[fi]); 3351 let src = Self::read_u8(&mut self.frames[fi]); 3352 let base = self.frames[fi].base; 3353 let cell_ref = match &self.registers[base + cell_reg as usize] { 3354 Value::Object(r) => *r, 3355 _ => { 3356 return Err(RuntimeError { 3357 kind: ErrorKind::Error, 3358 message: "CellStore: register does not hold a cell".into(), 3359 }); 3360 } 3361 }; 3362 let val = self.registers[base + src as usize].clone(); 3363 if let Some(HeapObject::Cell(cell_val)) = self.gc.get_mut(cell_ref) { 3364 *cell_val = val; 3365 } 3366 } 3367 Op::LoadUpvalue => { 3368 let dst = Self::read_u8(&mut self.frames[fi]); 3369 let idx = Self::read_u8(&mut self.frames[fi]) as usize; 3370 let base = self.frames[fi].base; 3371 let cell_ref = self.frames[fi].upvalues[idx]; 3372 let val = match self.gc.get(cell_ref) { 3373 Some(HeapObject::Cell(v)) => v.clone(), 3374 _ => Value::Undefined, 3375 }; 3376 self.registers[base + dst as usize] = val; 3377 } 3378 Op::StoreUpvalue => { 3379 let idx = Self::read_u8(&mut self.frames[fi]) as usize; 3380 let src = Self::read_u8(&mut self.frames[fi]); 3381 let base = self.frames[fi].base; 3382 let val = self.registers[base + src as usize].clone(); 3383 let cell_ref = self.frames[fi].upvalues[idx]; 3384 if let Some(HeapObject::Cell(cell_val)) = self.gc.get_mut(cell_ref) { 3385 *cell_val = val; 3386 } 3387 } 3388 3389 // ── Iterator / generator ───────────────────────── 3390 Op::Yield => { 3391 let _dst = Self::read_u8(&mut self.frames[fi]); 3392 let src = Self::read_u8(&mut self.frames[fi]); 3393 let base = self.frames[fi].base; 3394 let yield_val = self.registers[base + src as usize].clone(); 3395 3396 // Save the generator's state. 3397 let frame = &self.frames[fi]; 3398 let gen_ref = match self.registers.get(frame.return_reg) { 3399 Some(Value::Object(r)) => *r, 3400 _ => { 3401 return Err(RuntimeError { 3402 kind: ErrorKind::Error, 3403 message: "Yield outside generator".into(), 3404 }); 3405 } 3406 }; 3407 3408 // Save registers and IP into the generator object. 3409 let saved_ip = self.frames[fi].ip; 3410 let saved_base = self.frames[fi].base; 3411 let reg_count = self.frames[fi].func.register_count as usize; 3412 let saved_regs: Vec<Value> = 3413 self.registers[saved_base..saved_base + reg_count].to_vec(); 3414 3415 // Save exception handlers as (catch_ip, catch_reg) pairs. 3416 let saved_handlers: Vec<(usize, Reg)> = self.frames[fi] 3417 .exception_handlers 3418 .iter() 3419 .map(|h| (h.catch_ip, h.catch_reg)) 3420 .collect(); 3421 3422 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 3423 gen.ip = saved_ip; 3424 gen.registers = saved_regs; 3425 gen.state = GeneratorState::Suspended; 3426 gen.exception_handlers = saved_handlers; 3427 } 3428 3429 // Pop the generator frame. 3430 self.frames.pop(); 3431 3432 // Create {value, done: false} result. 3433 let result = self.make_iterator_result(yield_val, false); 3434 3435 // Generators always run via run_generator which uses an isolated 3436 // frame stack. After Yield pops the generator frame, the stack is 3437 // empty and we return the {value, done} result to run_generator. 3438 return Ok(result); 3439 } 3440 3441 // Await works identically to Yield at the opcode level: save state and 3442 // suspend. The async driver (not the generator protocol) handles resume. 3443 Op::Await => { 3444 let _dst = Self::read_u8(&mut self.frames[fi]); 3445 let src = Self::read_u8(&mut self.frames[fi]); 3446 let base = self.frames[fi].base; 3447 let await_val = self.registers[base + src as usize].clone(); 3448 3449 // Save the generator's state (same as Yield). 3450 let frame = &self.frames[fi]; 3451 let gen_ref = match self.registers.get(frame.return_reg) { 3452 Some(Value::Object(r)) => *r, 3453 _ => { 3454 return Err(RuntimeError { 3455 kind: ErrorKind::Error, 3456 message: "Await outside async function".into(), 3457 }); 3458 } 3459 }; 3460 3461 let saved_ip = self.frames[fi].ip; 3462 let saved_base = self.frames[fi].base; 3463 let reg_count = self.frames[fi].func.register_count as usize; 3464 let saved_regs: Vec<Value> = 3465 self.registers[saved_base..saved_base + reg_count].to_vec(); 3466 3467 let saved_handlers: Vec<(usize, Reg)> = self.frames[fi] 3468 .exception_handlers 3469 .iter() 3470 .map(|h| (h.catch_ip, h.catch_reg)) 3471 .collect(); 3472 3473 if let Some(HeapObject::Generator(gen)) = self.gc.get_mut(gen_ref) { 3474 gen.ip = saved_ip; 3475 gen.registers = saved_regs; 3476 gen.state = GeneratorState::Suspended; 3477 gen.exception_handlers = saved_handlers; 3478 } 3479 3480 self.frames.pop(); 3481 3482 let result = self.make_iterator_result(await_val, false); 3483 return Ok(result); 3484 } 3485 3486 Op::Spread => { 3487 let dst = Self::read_u8(&mut self.frames[fi]); 3488 let src = Self::read_u8(&mut self.frames[fi]); 3489 let base = self.frames[fi].base; 3490 let iterable = self.registers[base + src as usize].clone(); 3491 3492 // Get the iterator from the iterable. 3493 let iterator = self.get_iterator(&iterable)?; 3494 3495 // Iterate and push each element into the dst array. 3496 loop { 3497 let (value, done) = self.iterator_next(&iterator)?; 3498 if done { 3499 break; 3500 } 3501 // Push value into dst array. 3502 let dst_ref = match self.registers[base + dst as usize] { 3503 Value::Object(r) => r, 3504 _ => break, 3505 }; 3506 if let Some(HeapObject::Object(data)) = self.gc.get_mut(dst_ref) { 3507 let len = match data.properties.get("length") { 3508 Some(prop) => prop.value.to_number() as usize, 3509 None => 0, 3510 }; 3511 data.properties 3512 .insert(len.to_string(), Property::data(value)); 3513 data.properties.insert( 3514 "length".to_string(), 3515 Property { 3516 value: Value::Number((len + 1) as f64), 3517 writable: true, 3518 enumerable: false, 3519 configurable: false, 3520 }, 3521 ); 3522 } 3523 } 3524 } 3525 } 3526 } 3527 } 3528 3529 /// Read 3 register operands and return (dst, lhs, rhs, base) as usize indices. 3530 fn read_3reg(&mut self, fi: usize) -> (usize, usize, usize, usize) { 3531 let dst = Self::read_u8(&mut self.frames[fi]) as usize; 3532 let lhs = Self::read_u8(&mut self.frames[fi]) as usize; 3533 let rhs = Self::read_u8(&mut self.frames[fi]) as usize; 3534 let base = self.frames[fi].base; 3535 (dst, lhs, rhs, base) 3536 } 3537 3538 /// Try to find an exception handler on the call stack. 3539 /// Returns true if a handler was found (execution resumes there). 3540 fn handle_exception(&mut self, value: Value) -> bool { 3541 while let Some(frame) = self.frames.last_mut() { 3542 if let Some(handler) = frame.exception_handlers.pop() { 3543 let base = frame.base; 3544 frame.ip = handler.catch_ip; 3545 self.registers[base + handler.catch_reg as usize] = value; 3546 return true; 3547 } 3548 if self.frames.len() == 1 { 3549 break; 3550 } 3551 self.frames.pop(); 3552 } 3553 false 3554 } 3555 3556 /// Register a native function as a global. 3557 pub fn define_native( 3558 &mut self, 3559 name: &str, 3560 callback: fn(&[Value], &mut NativeContext) -> Result<Value, RuntimeError>, 3561 ) { 3562 let gc_ref = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 3563 name: name.to_string(), 3564 kind: FunctionKind::Native(NativeFunc { callback }), 3565 prototype_obj: None, 3566 properties: HashMap::new(), 3567 upvalues: Vec::new(), 3568 }))); 3569 self.globals 3570 .insert(name.to_string(), Value::Function(gc_ref)); 3571 } 3572 3573 /// Get a global variable value. 3574 pub fn get_global(&self, name: &str) -> Option<&Value> { 3575 self.globals.get(name) 3576 } 3577 3578 /// Set a global variable. 3579 pub fn set_global(&mut self, name: &str, val: Value) { 3580 self.globals.insert(name.to_string(), val); 3581 } 3582 3583 /// Remove a global variable. 3584 pub fn remove_global(&mut self, name: &str) { 3585 self.globals.remove(name); 3586 } 3587} 3588 3589impl Default for Vm { 3590 fn default() -> Self { 3591 Self::new() 3592 } 3593} 3594 3595/// Internal enum to avoid holding a GC borrow across the call setup. 3596enum CallInfo { 3597 Native(fn(&[Value], &mut NativeContext) -> Result<Value, RuntimeError>), 3598 Bytecode(Function, Vec<GcRef>), 3599} 3600 3601// ── Generator native callbacks ────────────────────────────── 3602 3603/// Native callback for generator.next(value). 3604/// `this` is the generator wrapper object containing __gen__. 3605fn generator_next(args: &[Value], ctx: &mut NativeContext) -> Result<Value, RuntimeError> { 3606 // The generator wrapper stores the actual generator as __gen__. 3607 let gen_ref = match &ctx.this { 3608 Value::Object(r) => match gc_get_property(ctx.gc, *r, "__gen__") { 3609 Value::Object(gen_r) => gen_r, 3610 _ => return Err(RuntimeError::type_error("not a generator")), 3611 }, 3612 _ => return Err(RuntimeError::type_error("not a generator")), 3613 }; 3614 3615 let send_value = args.first().cloned().unwrap_or(Value::Undefined); 3616 3617 // We can't call run_generator from a NativeContext since we only have &mut Gc. 3618 // Instead, store the request and let the caller handle it. 3619 // This is a limitation — we need to restructure. 3620 // For now, use a different approach: store the gen_ref and value in a special 3621 // return value that the VM intercepts. 3622 // Actually, generator.next() needs to be handled specially by the VM. 3623 // Let's return a sentinel that the VM's call handling can detect. 3624 3625 // Store gen_ref and send_value for the VM to process. 3626 // We use a special object with __generator_resume__ marker. 3627 let mut obj = ObjectData::new(); 3628 obj.properties.insert( 3629 "__generator_resume__".to_string(), 3630 Property::builtin(Value::Boolean(true)), 3631 ); 3632 obj.properties.insert( 3633 "__gen_ref__".to_string(), 3634 Property::builtin(Value::Object(gen_ref)), 3635 ); 3636 obj.properties 3637 .insert("__send_value__".to_string(), Property::builtin(send_value)); 3638 obj.properties.insert( 3639 "__resume_kind__".to_string(), 3640 Property::builtin(Value::String("next".to_string())), 3641 ); 3642 let r = ctx.gc.alloc(HeapObject::Object(obj)); 3643 Ok(Value::Object(r)) 3644} 3645 3646/// Native callback for generator.return(value). 3647fn generator_return(args: &[Value], ctx: &mut NativeContext) -> Result<Value, RuntimeError> { 3648 let gen_ref = match &ctx.this { 3649 Value::Object(r) => match gc_get_property(ctx.gc, *r, "__gen__") { 3650 Value::Object(gen_r) => gen_r, 3651 _ => return Err(RuntimeError::type_error("not a generator")), 3652 }, 3653 _ => return Err(RuntimeError::type_error("not a generator")), 3654 }; 3655 3656 let return_value = args.first().cloned().unwrap_or(Value::Undefined); 3657 3658 let mut obj = ObjectData::new(); 3659 obj.properties.insert( 3660 "__generator_resume__".to_string(), 3661 Property::builtin(Value::Boolean(true)), 3662 ); 3663 obj.properties.insert( 3664 "__gen_ref__".to_string(), 3665 Property::builtin(Value::Object(gen_ref)), 3666 ); 3667 obj.properties.insert( 3668 "__send_value__".to_string(), 3669 Property::builtin(return_value), 3670 ); 3671 obj.properties.insert( 3672 "__resume_kind__".to_string(), 3673 Property::builtin(Value::String("return".to_string())), 3674 ); 3675 let r = ctx.gc.alloc(HeapObject::Object(obj)); 3676 Ok(Value::Object(r)) 3677} 3678 3679/// Native callback for generator.throw(error). 3680fn generator_throw(args: &[Value], ctx: &mut NativeContext) -> Result<Value, RuntimeError> { 3681 let gen_ref = match &ctx.this { 3682 Value::Object(r) => match gc_get_property(ctx.gc, *r, "__gen__") { 3683 Value::Object(gen_r) => gen_r, 3684 _ => return Err(RuntimeError::type_error("not a generator")), 3685 }, 3686 _ => return Err(RuntimeError::type_error("not a generator")), 3687 }; 3688 3689 let error_value = args.first().cloned().unwrap_or(Value::Undefined); 3690 3691 let mut obj = ObjectData::new(); 3692 obj.properties.insert( 3693 "__generator_resume__".to_string(), 3694 Property::builtin(Value::Boolean(true)), 3695 ); 3696 obj.properties.insert( 3697 "__gen_ref__".to_string(), 3698 Property::builtin(Value::Object(gen_ref)), 3699 ); 3700 obj.properties 3701 .insert("__send_value__".to_string(), Property::builtin(error_value)); 3702 obj.properties.insert( 3703 "__resume_kind__".to_string(), 3704 Property::builtin(Value::String("throw".to_string())), 3705 ); 3706 let r = ctx.gc.alloc(HeapObject::Object(obj)); 3707 Ok(Value::Object(r)) 3708} 3709 3710/// Native callback for generator[Symbol.iterator]() — returns `this`. 3711fn generator_symbol_iterator( 3712 _args: &[Value], 3713 ctx: &mut NativeContext, 3714) -> Result<Value, RuntimeError> { 3715 Ok(ctx.this.clone()) 3716} 3717 3718// ── Async function native callbacks ────────────────────────── 3719 3720/// Native callback for async function resume. Called from microtask drain. 3721/// Returns a marker object with `__async_resume__` so the VM can detect it 3722/// and call `drive_async_step`. 3723/// 3724/// The resume data (gen_ref, result_promise, is_throw) is stored on the 3725/// function's `__async_data__` property. The VM extracts it into the 3726/// `ASYNC_RESUME_DATA` thread-local before invoking this callback. 3727fn async_resume_callback(args: &[Value], ctx: &mut NativeContext) -> Result<Value, RuntimeError> { 3728 let value = args.first().cloned().unwrap_or(Value::Undefined); 3729 let data_ref = ASYNC_RESUME_DATA.with(|cell| cell.take()); 3730 if let Some(data) = data_ref { 3731 let gen_ref = match gc_get_property(ctx.gc, data, "__gen_ref__") { 3732 Value::Object(r) => r, 3733 _ => return Ok(value), 3734 }; 3735 let result_promise = match gc_get_property(ctx.gc, data, "__result_promise__") { 3736 Value::Object(r) => r, 3737 _ => return Ok(value), 3738 }; 3739 let is_throw = matches!( 3740 gc_get_property(ctx.gc, data, "__is_throw__"), 3741 Value::Boolean(true) 3742 ); 3743 3744 // Return a marker for the VM to detect. 3745 let mut obj = ObjectData::new(); 3746 obj.properties.insert( 3747 "__async_resume__".to_string(), 3748 Property::builtin(Value::Boolean(true)), 3749 ); 3750 obj.properties.insert( 3751 "__gen_ref__".to_string(), 3752 Property::builtin(Value::Object(gen_ref)), 3753 ); 3754 obj.properties.insert( 3755 "__result_promise__".to_string(), 3756 Property::builtin(Value::Object(result_promise)), 3757 ); 3758 obj.properties.insert( 3759 "__is_throw__".to_string(), 3760 Property::builtin(Value::Boolean(is_throw)), 3761 ); 3762 obj.properties 3763 .insert("__value__".to_string(), Property::builtin(value)); 3764 let r = ctx.gc.alloc(HeapObject::Object(obj)); 3765 Ok(Value::Object(r)) 3766 } else { 3767 Ok(value) 3768 } 3769} 3770 3771thread_local! { 3772 static ASYNC_RESUME_DATA: std::cell::Cell<Option<GcRef>> = const { std::cell::Cell::new(None) }; 3773} 3774 3775/// Native callback for async generator `.next(value)`. 3776fn async_generator_next(args: &[Value], ctx: &mut NativeContext) -> Result<Value, RuntimeError> { 3777 let gen_ref = match &ctx.this { 3778 Value::Object(r) => match gc_get_property(ctx.gc, *r, "__gen__") { 3779 Value::Object(gen_r) => gen_r, 3780 _ => return Err(RuntimeError::type_error("not an async generator")), 3781 }, 3782 _ => return Err(RuntimeError::type_error("not an async generator")), 3783 }; 3784 3785 let send_value = args.first().cloned().unwrap_or(Value::Undefined); 3786 3787 // Return a marker for the VM to handle. 3788 let mut obj = ObjectData::new(); 3789 obj.properties.insert( 3790 "__async_generator_resume__".to_string(), 3791 Property::builtin(Value::Boolean(true)), 3792 ); 3793 obj.properties.insert( 3794 "__gen_ref__".to_string(), 3795 Property::builtin(Value::Object(gen_ref)), 3796 ); 3797 obj.properties 3798 .insert("__send_value__".to_string(), Property::builtin(send_value)); 3799 obj.properties.insert( 3800 "__resume_kind__".to_string(), 3801 Property::builtin(Value::String("next".to_string())), 3802 ); 3803 let r = ctx.gc.alloc(HeapObject::Object(obj)); 3804 Ok(Value::Object(r)) 3805} 3806 3807/// Native callback for async generator `.return(value)`. 3808fn async_generator_return(args: &[Value], ctx: &mut NativeContext) -> Result<Value, RuntimeError> { 3809 let gen_ref = match &ctx.this { 3810 Value::Object(r) => match gc_get_property(ctx.gc, *r, "__gen__") { 3811 Value::Object(gen_r) => gen_r, 3812 _ => return Err(RuntimeError::type_error("not an async generator")), 3813 }, 3814 _ => return Err(RuntimeError::type_error("not an async generator")), 3815 }; 3816 3817 let return_value = args.first().cloned().unwrap_or(Value::Undefined); 3818 3819 let mut obj = ObjectData::new(); 3820 obj.properties.insert( 3821 "__async_generator_resume__".to_string(), 3822 Property::builtin(Value::Boolean(true)), 3823 ); 3824 obj.properties.insert( 3825 "__gen_ref__".to_string(), 3826 Property::builtin(Value::Object(gen_ref)), 3827 ); 3828 obj.properties.insert( 3829 "__send_value__".to_string(), 3830 Property::builtin(return_value), 3831 ); 3832 obj.properties.insert( 3833 "__resume_kind__".to_string(), 3834 Property::builtin(Value::String("return".to_string())), 3835 ); 3836 let r = ctx.gc.alloc(HeapObject::Object(obj)); 3837 Ok(Value::Object(r)) 3838} 3839 3840// ── Tests ──────────────────────────────────────────────────── 3841 3842#[cfg(test)] 3843mod tests { 3844 use super::*; 3845 use crate::bytecode::{BytecodeBuilder, Constant, Op}; 3846 use crate::compiler; 3847 use crate::parser::Parser; 3848 3849 /// Helper: compile and execute JS source, return the completion value. 3850 fn eval(source: &str) -> Result<Value, RuntimeError> { 3851 let program = Parser::parse(source).expect("parse failed"); 3852 let func = compiler::compile(&program).expect("compile failed"); 3853 let mut vm = Vm::new(); 3854 vm.execute(&func) 3855 } 3856 3857 // ── Value tests ───────────────────────────────────────── 3858 3859 #[test] 3860 fn test_to_boolean() { 3861 let mut gc: Gc<HeapObject> = Gc::new(); 3862 assert!(!Value::Undefined.to_boolean()); 3863 assert!(!Value::Null.to_boolean()); 3864 assert!(!Value::Boolean(false).to_boolean()); 3865 assert!(Value::Boolean(true).to_boolean()); 3866 assert!(!Value::Number(0.0).to_boolean()); 3867 assert!(!Value::Number(f64::NAN).to_boolean()); 3868 assert!(Value::Number(1.0).to_boolean()); 3869 assert!(!Value::String(String::new()).to_boolean()); 3870 assert!(Value::String("hello".to_string()).to_boolean()); 3871 let obj_ref = gc.alloc(HeapObject::Object(ObjectData::new())); 3872 assert!(Value::Object(obj_ref).to_boolean()); 3873 } 3874 3875 #[test] 3876 fn test_to_number() { 3877 assert!(Value::Undefined.to_number().is_nan()); 3878 assert_eq!(Value::Null.to_number(), 0.0); 3879 assert_eq!(Value::Boolean(true).to_number(), 1.0); 3880 assert_eq!(Value::Boolean(false).to_number(), 0.0); 3881 assert_eq!(Value::Number(42.0).to_number(), 42.0); 3882 assert_eq!(Value::String("42".to_string()).to_number(), 42.0); 3883 assert_eq!(Value::String("".to_string()).to_number(), 0.0); 3884 assert!(Value::String("abc".to_string()).to_number().is_nan()); 3885 } 3886 3887 #[test] 3888 fn test_type_of() { 3889 let mut gc: Gc<HeapObject> = Gc::new(); 3890 assert_eq!(Value::Undefined.type_of(), "undefined"); 3891 assert_eq!(Value::Null.type_of(), "object"); 3892 assert_eq!(Value::Boolean(true).type_of(), "boolean"); 3893 assert_eq!(Value::Number(1.0).type_of(), "number"); 3894 assert_eq!(Value::String("hi".to_string()).type_of(), "string"); 3895 let obj_ref = gc.alloc(HeapObject::Object(ObjectData::new())); 3896 assert_eq!(Value::Object(obj_ref).type_of(), "object"); 3897 } 3898 3899 // ── VM bytecode-level tests ───────────────────────────── 3900 3901 #[test] 3902 fn test_load_const_number() { 3903 let mut b = BytecodeBuilder::new("<test>".into(), 0); 3904 b.func.register_count = 1; 3905 let ci = b.add_constant(Constant::Number(42.0)); 3906 b.emit_reg_u16(Op::LoadConst, 0, ci); 3907 b.emit_reg(Op::Return, 0); 3908 let func = b.finish(); 3909 3910 let mut vm = Vm::new(); 3911 let result = vm.execute(&func).unwrap(); 3912 match result { 3913 Value::Number(n) => assert_eq!(n, 42.0), 3914 _ => panic!("expected Number, got {result:?}"), 3915 } 3916 } 3917 3918 #[test] 3919 fn test_arithmetic_ops() { 3920 let mut b = BytecodeBuilder::new("<test>".into(), 0); 3921 b.func.register_count = 3; 3922 let c10 = b.add_constant(Constant::Number(10.0)); 3923 let c3 = b.add_constant(Constant::Number(3.0)); 3924 b.emit_reg_u16(Op::LoadConst, 0, c10); 3925 b.emit_reg_u16(Op::LoadConst, 1, c3); 3926 b.emit_reg3(Op::Add, 2, 0, 1); 3927 b.emit_reg(Op::Return, 2); 3928 let func = b.finish(); 3929 3930 let mut vm = Vm::new(); 3931 match vm.execute(&func).unwrap() { 3932 Value::Number(n) => assert_eq!(n, 13.0), 3933 v => panic!("expected 13, got {v:?}"), 3934 } 3935 } 3936 3937 #[test] 3938 fn test_string_concat() { 3939 let mut b = BytecodeBuilder::new("<test>".into(), 0); 3940 b.func.register_count = 3; 3941 let c1 = b.add_constant(Constant::String("hello".into())); 3942 let c2 = b.add_constant(Constant::String(" world".into())); 3943 b.emit_reg_u16(Op::LoadConst, 0, c1); 3944 b.emit_reg_u16(Op::LoadConst, 1, c2); 3945 b.emit_reg3(Op::Add, 2, 0, 1); 3946 b.emit_reg(Op::Return, 2); 3947 let func = b.finish(); 3948 3949 let mut vm = Vm::new(); 3950 match vm.execute(&func).unwrap() { 3951 Value::String(s) => assert_eq!(s, "hello world"), 3952 v => panic!("expected string, got {v:?}"), 3953 } 3954 } 3955 3956 #[test] 3957 fn test_jump_if_false() { 3958 let mut b = BytecodeBuilder::new("<test>".into(), 0); 3959 b.func.register_count = 2; 3960 b.emit_reg(Op::LoadFalse, 0); 3961 let patch = b.emit_cond_jump(Op::JumpIfFalse, 0); 3962 b.emit_load_int8(1, 1); 3963 let skip = b.emit_jump(Op::Jump); 3964 b.patch_jump(patch); 3965 b.emit_load_int8(1, 2); 3966 b.patch_jump(skip); 3967 b.emit_reg(Op::Return, 1); 3968 let func = b.finish(); 3969 3970 let mut vm = Vm::new(); 3971 match vm.execute(&func).unwrap() { 3972 Value::Number(n) => assert_eq!(n, 2.0), 3973 v => panic!("expected 2, got {v:?}"), 3974 } 3975 } 3976 3977 #[test] 3978 fn test_globals() { 3979 let mut b = BytecodeBuilder::new("<test>".into(), 0); 3980 b.func.register_count = 2; 3981 let name = b.add_name("x"); 3982 b.emit_load_int8(0, 42); 3983 b.emit_store_global(name, 0); 3984 b.emit_load_global(1, name); 3985 b.emit_reg(Op::Return, 1); 3986 let func = b.finish(); 3987 3988 let mut vm = Vm::new(); 3989 match vm.execute(&func).unwrap() { 3990 Value::Number(n) => assert_eq!(n, 42.0), 3991 v => panic!("expected 42, got {v:?}"), 3992 } 3993 } 3994 3995 #[test] 3996 fn test_function_call() { 3997 let mut inner_b = BytecodeBuilder::new("add1".into(), 1); 3998 inner_b.func.register_count = 2; 3999 inner_b.emit_load_int8(1, 1); 4000 inner_b.emit_reg3(Op::Add, 0, 0, 1); 4001 inner_b.emit_reg(Op::Return, 0); 4002 let inner = inner_b.finish(); 4003 4004 let mut b = BytecodeBuilder::new("<test>".into(), 0); 4005 b.func.register_count = 4; 4006 let fi = b.add_function(inner); 4007 b.emit_reg_u16(Op::CreateClosure, 0, fi); 4008 b.emit_load_int8(1, 10); 4009 b.emit_call(2, 0, 1, 1); 4010 b.emit_reg(Op::Return, 2); 4011 let func = b.finish(); 4012 4013 let mut vm = Vm::new(); 4014 match vm.execute(&func).unwrap() { 4015 Value::Number(n) => assert_eq!(n, 11.0), 4016 v => panic!("expected 11, got {v:?}"), 4017 } 4018 } 4019 4020 #[test] 4021 fn test_native_function() { 4022 let mut b = BytecodeBuilder::new("<test>".into(), 0); 4023 b.func.register_count = 3; 4024 let name = b.add_name("double"); 4025 b.emit_load_global(0, name); 4026 b.emit_load_int8(1, 21); 4027 b.emit_call(2, 0, 1, 1); 4028 b.emit_reg(Op::Return, 2); 4029 let func = b.finish(); 4030 4031 let mut vm = Vm::new(); 4032 vm.define_native("double", |args, _ctx| { 4033 let n = args.first().unwrap_or(&Value::Undefined).to_number(); 4034 Ok(Value::Number(n * 2.0)) 4035 }); 4036 match vm.execute(&func).unwrap() { 4037 Value::Number(n) => assert_eq!(n, 42.0), 4038 v => panic!("expected 42, got {v:?}"), 4039 } 4040 } 4041 4042 #[test] 4043 fn test_object_property() { 4044 let mut b = BytecodeBuilder::new("<test>".into(), 0); 4045 b.func.register_count = 3; 4046 let name = b.add_name("x"); 4047 b.emit_reg(Op::CreateObject, 0); 4048 b.emit_load_int8(1, 42); 4049 b.emit_set_prop_name(0, name, 1); 4050 b.emit_get_prop_name(2, 0, name); 4051 b.emit_reg(Op::Return, 2); 4052 let func = b.finish(); 4053 4054 let mut vm = Vm::new(); 4055 match vm.execute(&func).unwrap() { 4056 Value::Number(n) => assert_eq!(n, 42.0), 4057 v => panic!("expected 42, got {v:?}"), 4058 } 4059 } 4060 4061 #[test] 4062 fn test_typeof_operator() { 4063 let mut b = BytecodeBuilder::new("<test>".into(), 0); 4064 b.func.register_count = 2; 4065 b.emit_load_int8(0, 5); 4066 b.emit_reg_reg(Op::TypeOf, 1, 0); 4067 b.emit_reg(Op::Return, 1); 4068 let func = b.finish(); 4069 4070 let mut vm = Vm::new(); 4071 match vm.execute(&func).unwrap() { 4072 Value::String(s) => assert_eq!(s, "number"), 4073 v => panic!("expected 'number', got {v:?}"), 4074 } 4075 } 4076 4077 #[test] 4078 fn test_comparison() { 4079 let mut b = BytecodeBuilder::new("<test>".into(), 0); 4080 b.func.register_count = 3; 4081 b.emit_load_int8(0, 5); 4082 b.emit_load_int8(1, 10); 4083 b.emit_reg3(Op::LessThan, 2, 0, 1); 4084 b.emit_reg(Op::Return, 2); 4085 let func = b.finish(); 4086 4087 let mut vm = Vm::new(); 4088 match vm.execute(&func).unwrap() { 4089 Value::Boolean(b) => assert!(b), 4090 v => panic!("expected true, got {v:?}"), 4091 } 4092 } 4093 4094 #[test] 4095 fn test_abstract_equality() { 4096 assert!(abstract_eq(&Value::Null, &Value::Undefined)); 4097 assert!(abstract_eq(&Value::Undefined, &Value::Null)); 4098 assert!(abstract_eq( 4099 &Value::Number(1.0), 4100 &Value::String("1".to_string()) 4101 )); 4102 assert!(abstract_eq(&Value::Boolean(true), &Value::Number(1.0))); 4103 assert!(!abstract_eq(&Value::Number(0.0), &Value::Null)); 4104 } 4105 4106 #[test] 4107 fn test_strict_equality() { 4108 assert!(strict_eq(&Value::Number(1.0), &Value::Number(1.0))); 4109 assert!(!strict_eq( 4110 &Value::Number(1.0), 4111 &Value::String("1".to_string()) 4112 )); 4113 assert!(strict_eq(&Value::Null, &Value::Null)); 4114 assert!(!strict_eq(&Value::Null, &Value::Undefined)); 4115 } 4116 4117 #[test] 4118 fn test_bitwise_ops() { 4119 let mut b = BytecodeBuilder::new("<test>".into(), 0); 4120 b.func.register_count = 3; 4121 b.emit_load_int8(0, 0x0F); 4122 b.emit_load_int8(1, 0x03); 4123 b.emit_reg3(Op::BitAnd, 2, 0, 1); 4124 b.emit_reg(Op::Return, 2); 4125 let func = b.finish(); 4126 4127 let mut vm = Vm::new(); 4128 match vm.execute(&func).unwrap() { 4129 Value::Number(n) => assert_eq!(n, 3.0), 4130 v => panic!("expected 3, got {v:?}"), 4131 } 4132 } 4133 4134 #[test] 4135 fn test_throw_uncaught() { 4136 let mut b = BytecodeBuilder::new("<test>".into(), 0); 4137 b.func.register_count = 1; 4138 let ci = b.add_constant(Constant::String("oops".into())); 4139 b.emit_reg_u16(Op::LoadConst, 0, ci); 4140 b.emit_reg(Op::Throw, 0); 4141 let func = b.finish(); 4142 4143 let mut vm = Vm::new(); 4144 let err = vm.execute(&func).unwrap_err(); 4145 assert_eq!(err.message, "oops"); 4146 } 4147 4148 #[test] 4149 fn test_loop_counting() { 4150 let mut b = BytecodeBuilder::new("<test>".into(), 0); 4151 b.func.register_count = 3; 4152 b.emit_load_int8(0, 0); 4153 b.emit_load_int8(1, 5); 4154 let loop_start = b.offset(); 4155 b.emit_reg3(Op::LessThan, 2, 0, 1); 4156 let exit_patch = b.emit_cond_jump(Op::JumpIfFalse, 2); 4157 b.emit_load_int8(2, 1); 4158 b.emit_reg3(Op::Add, 0, 0, 2); 4159 b.emit_jump_to(loop_start); 4160 b.patch_jump(exit_patch); 4161 b.emit_reg(Op::Return, 0); 4162 let func = b.finish(); 4163 4164 let mut vm = Vm::new(); 4165 match vm.execute(&func).unwrap() { 4166 Value::Number(n) => assert_eq!(n, 5.0), 4167 v => panic!("expected 5, got {v:?}"), 4168 } 4169 } 4170 4171 // ── End-to-end (compile + execute) tests ──────────────── 4172 4173 #[test] 4174 fn test_e2e_arithmetic() { 4175 match eval("2 + 3 * 4").unwrap() { 4176 Value::Number(n) => assert_eq!(n, 14.0), 4177 v => panic!("expected 14, got {v:?}"), 4178 } 4179 } 4180 4181 #[test] 4182 fn test_e2e_variables() { 4183 match eval("var x = 10; var y = 20; x + y").unwrap() { 4184 Value::Number(n) => assert_eq!(n, 30.0), 4185 v => panic!("expected 30, got {v:?}"), 4186 } 4187 } 4188 4189 #[test] 4190 fn test_e2e_if_else() { 4191 match eval("var x = 5; if (x > 3) { x = 100; } x").unwrap() { 4192 Value::Number(n) => assert_eq!(n, 100.0), 4193 v => panic!("expected 100, got {v:?}"), 4194 } 4195 } 4196 4197 #[test] 4198 fn test_e2e_while_loop() { 4199 match eval("var i = 0; var sum = 0; while (i < 10) { sum = sum + i; i = i + 1; } sum") 4200 .unwrap() 4201 { 4202 Value::Number(n) => assert_eq!(n, 45.0), 4203 v => panic!("expected 45, got {v:?}"), 4204 } 4205 } 4206 4207 #[test] 4208 fn test_e2e_function_call() { 4209 match eval("function add(a, b) { return a + b; } add(3, 4)").unwrap() { 4210 Value::Number(n) => assert_eq!(n, 7.0), 4211 v => panic!("expected 7, got {v:?}"), 4212 } 4213 } 4214 4215 #[test] 4216 fn test_e2e_recursive_factorial() { 4217 let src = r#" 4218 function fact(n) { 4219 if (n <= 1) return 1; 4220 return n * fact(n - 1); 4221 } 4222 fact(6) 4223 "#; 4224 match eval(src).unwrap() { 4225 Value::Number(n) => assert_eq!(n, 720.0), 4226 v => panic!("expected 720, got {v:?}"), 4227 } 4228 } 4229 4230 #[test] 4231 fn test_e2e_string_concat() { 4232 match eval("'hello' + ' ' + 'world'").unwrap() { 4233 Value::String(s) => assert_eq!(s, "hello world"), 4234 v => panic!("expected 'hello world', got {v:?}"), 4235 } 4236 } 4237 4238 #[test] 4239 fn test_e2e_comparison_coercion() { 4240 match eval("1 == '1'").unwrap() { 4241 Value::Boolean(b) => assert!(b), 4242 v => panic!("expected true, got {v:?}"), 4243 } 4244 match eval("1 === '1'").unwrap() { 4245 Value::Boolean(b) => assert!(!b), 4246 v => panic!("expected false, got {v:?}"), 4247 } 4248 } 4249 4250 #[test] 4251 fn test_e2e_typeof() { 4252 match eval("typeof 42").unwrap() { 4253 Value::String(s) => assert_eq!(s, "number"), 4254 v => panic!("expected 'number', got {v:?}"), 4255 } 4256 match eval("typeof 'hello'").unwrap() { 4257 Value::String(s) => assert_eq!(s, "string"), 4258 v => panic!("expected 'string', got {v:?}"), 4259 } 4260 } 4261 4262 #[test] 4263 fn test_e2e_object_literal() { 4264 match eval("var o = { x: 10, y: 20 }; o.x + o.y").unwrap() { 4265 Value::Number(n) => assert_eq!(n, 30.0), 4266 v => panic!("expected 30, got {v:?}"), 4267 } 4268 } 4269 4270 #[test] 4271 fn test_e2e_for_loop() { 4272 match eval("var s = 0; for (var i = 0; i < 5; i = i + 1) { s = s + i; } s").unwrap() { 4273 Value::Number(n) => assert_eq!(n, 10.0), 4274 v => panic!("expected 10, got {v:?}"), 4275 } 4276 } 4277 4278 #[test] 4279 fn test_e2e_nested_functions() { 4280 // Note: closures (capturing parent scope vars) not yet supported. 4281 // This test verifies nested function declarations and calls work. 4282 let src = r#" 4283 function outer(x) { 4284 function inner(y) { 4285 return y + 1; 4286 } 4287 return inner(x); 4288 } 4289 outer(5) 4290 "#; 4291 match eval(src).unwrap() { 4292 Value::Number(n) => assert_eq!(n, 6.0), 4293 v => panic!("expected 6, got {v:?}"), 4294 } 4295 } 4296 4297 #[test] 4298 fn test_e2e_logical_operators() { 4299 match eval("true && false").unwrap() { 4300 Value::Boolean(b) => assert!(!b), 4301 v => panic!("expected false, got {v:?}"), 4302 } 4303 match eval("false || true").unwrap() { 4304 Value::Boolean(b) => assert!(b), 4305 v => panic!("expected true, got {v:?}"), 4306 } 4307 } 4308 4309 #[test] 4310 fn test_e2e_unary_neg() { 4311 match eval("-(5 + 3)").unwrap() { 4312 Value::Number(n) => assert_eq!(n, -8.0), 4313 v => panic!("expected -8, got {v:?}"), 4314 } 4315 } 4316 4317 #[test] 4318 fn test_e2e_ternary() { 4319 match eval("true ? 1 : 2").unwrap() { 4320 Value::Number(n) => assert_eq!(n, 1.0), 4321 v => panic!("expected 1, got {v:?}"), 4322 } 4323 match eval("false ? 1 : 2").unwrap() { 4324 Value::Number(n) => assert_eq!(n, 2.0), 4325 v => panic!("expected 2, got {v:?}"), 4326 } 4327 } 4328 4329 #[test] 4330 fn test_e2e_fibonacci() { 4331 let src = r#" 4332 function fib(n) { 4333 if (n <= 1) return n; 4334 return fib(n - 1) + fib(n - 2); 4335 } 4336 fib(10) 4337 "#; 4338 match eval(src).unwrap() { 4339 Value::Number(n) => assert_eq!(n, 55.0), 4340 v => panic!("expected 55, got {v:?}"), 4341 } 4342 } 4343 4344 // ── GC integration tests ──────────────────────────────── 4345 4346 #[test] 4347 fn test_gc_object_survives_collection() { 4348 let src = r#" 4349 var o = { x: 42 }; 4350 o.x 4351 "#; 4352 match eval(src).unwrap() { 4353 Value::Number(n) => assert_eq!(n, 42.0), 4354 v => panic!("expected 42, got {v:?}"), 4355 } 4356 } 4357 4358 #[test] 4359 fn test_gc_many_objects() { 4360 // Allocate many objects to trigger GC threshold. 4361 let src = r#" 4362 var sum = 0; 4363 var i = 0; 4364 while (i < 100) { 4365 var o = { val: i }; 4366 sum = sum + o.val; 4367 i = i + 1; 4368 } 4369 sum 4370 "#; 4371 match eval(src).unwrap() { 4372 Value::Number(n) => assert_eq!(n, 4950.0), 4373 v => panic!("expected 4950, got {v:?}"), 4374 } 4375 } 4376 4377 #[test] 4378 fn test_gc_reference_identity() { 4379 // With GC, object assignment is by reference. 4380 let mut gc: Gc<HeapObject> = Gc::new(); 4381 let r = gc.alloc(HeapObject::Object(ObjectData::new())); 4382 let a = Value::Object(r); 4383 let b = a.clone(); 4384 assert!(strict_eq(&a, &b)); // Same GcRef → strict equal. 4385 } 4386 4387 // ── Object model tests ────────────────────────────────── 4388 4389 #[test] 4390 fn test_prototype_chain_lookup() { 4391 // Property lookup walks the prototype chain. 4392 let src = r#" 4393 function Animal() {} 4394 var a = {}; 4395 a.sound = "woof"; 4396 a.sound 4397 "#; 4398 match eval(src).unwrap() { 4399 Value::String(s) => assert_eq!(s, "woof"), 4400 v => panic!("expected 'woof', got {v:?}"), 4401 } 4402 } 4403 4404 #[test] 4405 fn test_typeof_all_types() { 4406 // typeof returns correct strings for all types. 4407 let cases = [ 4408 ("typeof undefined", "undefined"), 4409 ("typeof null", "object"), 4410 ("typeof true", "boolean"), 4411 ("typeof 42", "number"), 4412 ("typeof 'hello'", "string"), 4413 ("typeof {}", "object"), 4414 ("typeof function(){}", "function"), 4415 ]; 4416 for (src, expected) in cases { 4417 match eval(src).unwrap() { 4418 Value::String(s) => assert_eq!(s, expected, "typeof failed for: {src}"), 4419 v => panic!("expected string for {src}, got {v:?}"), 4420 } 4421 } 4422 } 4423 4424 #[test] 4425 fn test_instanceof_basic() { 4426 let src = r#" 4427 function Foo() {} 4428 var f = {}; 4429 f instanceof Foo 4430 "#; 4431 // Plain object without prototype link → false 4432 match eval(src).unwrap() { 4433 Value::Boolean(b) => assert!(!b), 4434 v => panic!("expected false, got {v:?}"), 4435 } 4436 } 4437 4438 #[test] 4439 fn test_in_operator() { 4440 let src = r#" 4441 var o = { x: 1, y: 2 }; 4442 var r1 = "x" in o; 4443 var r2 = "z" in o; 4444 r1 === true && r2 === false 4445 "#; 4446 match eval(src).unwrap() { 4447 Value::Boolean(b) => assert!(b), 4448 v => panic!("expected true, got {v:?}"), 4449 } 4450 } 4451 4452 #[test] 4453 fn test_delete_property() { 4454 let src = r#" 4455 var o = { x: 1, y: 2 }; 4456 delete o.x; 4457 typeof o.x === "undefined" && o.y === 2 4458 "#; 4459 match eval(src).unwrap() { 4460 Value::Boolean(b) => assert!(b), 4461 v => panic!("expected true, got {v:?}"), 4462 } 4463 } 4464 4465 #[test] 4466 fn test_delete_computed_property() { 4467 let src = r#" 4468 var o = { a: 10, b: 20 }; 4469 var key = "a"; 4470 delete o[key]; 4471 typeof o.a === "undefined" && o.b === 20 4472 "#; 4473 match eval(src).unwrap() { 4474 Value::Boolean(b) => assert!(b), 4475 v => panic!("expected true, got {v:?}"), 4476 } 4477 } 4478 4479 #[test] 4480 fn test_delete_non_configurable() { 4481 // Array length is non-configurable, delete should return false. 4482 let mut gc: Gc<HeapObject> = Gc::new(); 4483 let mut obj = ObjectData::new(); 4484 obj.properties.insert( 4485 "x".to_string(), 4486 Property { 4487 value: Value::Number(1.0), 4488 writable: true, 4489 enumerable: true, 4490 configurable: false, 4491 }, 4492 ); 4493 let obj_ref = gc.alloc(HeapObject::Object(obj)); 4494 4495 // Try to delete the non-configurable property. 4496 match gc.get_mut(obj_ref) { 4497 Some(HeapObject::Object(data)) => { 4498 let prop = data.properties.get("x").unwrap(); 4499 assert!(!prop.configurable); 4500 // The property should still be there. 4501 assert!(data.properties.contains_key("x")); 4502 } 4503 _ => panic!("expected object"), 4504 } 4505 } 4506 4507 #[test] 4508 fn test_property_writable_flag() { 4509 // Setting a non-writable property should silently fail. 4510 let mut gc: Gc<HeapObject> = Gc::new(); 4511 let mut obj = ObjectData::new(); 4512 obj.properties.insert( 4513 "frozen".to_string(), 4514 Property { 4515 value: Value::Number(42.0), 4516 writable: false, 4517 enumerable: true, 4518 configurable: false, 4519 }, 4520 ); 4521 let obj_ref = gc.alloc(HeapObject::Object(obj)); 4522 4523 // Verify the property value. 4524 match gc.get(obj_ref) { 4525 Some(HeapObject::Object(data)) => { 4526 assert_eq!( 4527 data.properties.get("frozen").unwrap().value.to_number(), 4528 42.0 4529 ); 4530 } 4531 _ => panic!("expected object"), 4532 } 4533 } 4534 4535 #[test] 4536 fn test_for_in_basic() { 4537 let src = r#" 4538 var o = { a: 1, b: 2, c: 3 }; 4539 var sum = 0; 4540 for (var key in o) { 4541 sum = sum + o[key]; 4542 } 4543 sum 4544 "#; 4545 match eval(src).unwrap() { 4546 Value::Number(n) => assert_eq!(n, 6.0), 4547 v => panic!("expected 6, got {v:?}"), 4548 } 4549 } 4550 4551 #[test] 4552 fn test_for_in_collects_keys() { 4553 let src = r#" 4554 var o = { x: 10, y: 20 }; 4555 var keys = ""; 4556 for (var k in o) { 4557 keys = keys + k + ","; 4558 } 4559 keys 4560 "#; 4561 match eval(src).unwrap() { 4562 Value::String(s) => { 4563 // Both keys should appear (order may vary with HashMap). 4564 assert!(s.contains("x,")); 4565 assert!(s.contains("y,")); 4566 } 4567 v => panic!("expected string, got {v:?}"), 4568 } 4569 } 4570 4571 #[test] 4572 fn test_for_in_empty_object() { 4573 let src = r#" 4574 var o = {}; 4575 var count = 0; 4576 for (var k in o) { 4577 count = count + 1; 4578 } 4579 count 4580 "#; 4581 match eval(src).unwrap() { 4582 Value::Number(n) => assert_eq!(n, 0.0), 4583 v => panic!("expected 0, got {v:?}"), 4584 } 4585 } 4586 4587 #[test] 4588 fn test_property_enumerable_flag() { 4589 // Array "length" is non-enumerable, should not appear in for-in. 4590 let src = r#" 4591 var arr = [10, 20, 30]; 4592 var keys = ""; 4593 for (var k in arr) { 4594 keys = keys + k + ","; 4595 } 4596 keys 4597 "#; 4598 match eval(src).unwrap() { 4599 Value::String(s) => { 4600 // "length" should NOT be in the keys (it's non-enumerable). 4601 assert!(!s.contains("length")); 4602 // Array indices should be present. 4603 assert!(s.contains("0,")); 4604 assert!(s.contains("1,")); 4605 assert!(s.contains("2,")); 4606 } 4607 v => panic!("expected string, got {v:?}"), 4608 } 4609 } 4610 4611 #[test] 4612 fn test_object_reference_semantics() { 4613 // Objects have reference semantics (shared via GcRef). 4614 let src = r#" 4615 var a = { x: 1 }; 4616 var b = a; 4617 b.x = 42; 4618 a.x 4619 "#; 4620 match eval(src).unwrap() { 4621 Value::Number(n) => assert_eq!(n, 42.0), 4622 v => panic!("expected 42, got {v:?}"), 4623 } 4624 } 4625 4626 #[test] 4627 fn test_gc_enumerate_keys_order() { 4628 // Integer keys should come first, sorted numerically. 4629 let mut gc: Gc<HeapObject> = Gc::new(); 4630 let mut obj = ObjectData::new(); 4631 obj.properties 4632 .insert("b".to_string(), Property::data(Value::Number(2.0))); 4633 obj.properties 4634 .insert("2".to_string(), Property::data(Value::Number(3.0))); 4635 obj.properties 4636 .insert("a".to_string(), Property::data(Value::Number(1.0))); 4637 obj.properties 4638 .insert("0".to_string(), Property::data(Value::Number(0.0))); 4639 let obj_ref = gc.alloc(HeapObject::Object(obj)); 4640 4641 let keys = gc_enumerate_keys(&gc, obj_ref); 4642 // Integer keys first (sorted), then string keys. 4643 let int_part: Vec<&str> = keys.iter().take(2).map(|s| s.as_str()).collect(); 4644 assert_eq!(int_part, vec!["0", "2"]); 4645 // Remaining keys are string keys (order depends on HashMap iteration). 4646 let str_part: Vec<&str> = keys.iter().skip(2).map(|s| s.as_str()).collect(); 4647 assert!(str_part.contains(&"a")); 4648 assert!(str_part.contains(&"b")); 4649 } 4650 4651 #[test] 4652 fn test_instanceof_with_gc() { 4653 // Direct test of gc_instanceof. 4654 let mut gc: Gc<HeapObject> = Gc::new(); 4655 4656 // Create a constructor function with a .prototype object. 4657 let proto = gc.alloc(HeapObject::Object(ObjectData::new())); 4658 let ctor = gc.alloc(HeapObject::Function(Box::new(FunctionData { 4659 name: "Foo".to_string(), 4660 kind: FunctionKind::Native(NativeFunc { 4661 callback: |_, _ctx| Ok(Value::Undefined), 4662 }), 4663 prototype_obj: Some(proto), 4664 properties: HashMap::new(), 4665 upvalues: Vec::new(), 4666 }))); 4667 4668 // Create an object whose [[Prototype]] is the constructor's .prototype. 4669 let mut obj_data = ObjectData::new(); 4670 obj_data.prototype = Some(proto); 4671 let obj = gc.alloc(HeapObject::Object(obj_data)); 4672 4673 assert!(gc_instanceof(&gc, obj, ctor)); 4674 4675 // An unrelated object should not match. 4676 let other = gc.alloc(HeapObject::Object(ObjectData::new())); 4677 assert!(!gc_instanceof(&gc, other, ctor)); 4678 } 4679 4680 #[test] 4681 fn test_try_catch_basic() { 4682 // Simple try/catch should catch a thrown value. 4683 let src = r#" 4684 var caught = false; 4685 try { throw "err"; } catch (e) { caught = true; } 4686 caught 4687 "#; 4688 match eval(src).unwrap() { 4689 Value::Boolean(true) => {} 4690 v => panic!("expected true, got {v:?}"), 4691 } 4692 } 4693 4694 #[test] 4695 fn test_try_catch_nested_call() { 4696 // try/catch should catch errors thrown from called functions. 4697 let src = r#" 4698 function thrower() { throw "err"; } 4699 var caught = false; 4700 try { thrower(); } catch (e) { caught = true; } 4701 caught 4702 "#; 4703 match eval(src).unwrap() { 4704 Value::Boolean(true) => {} 4705 v => panic!("expected true, got {v:?}"), 4706 } 4707 } 4708 4709 // ── Closure tests ──────────────────────────────────────── 4710 4711 #[test] 4712 fn test_closure_basic() { 4713 // Basic closure: inner function reads outer variable. 4714 let src = r#" 4715 function outer() { 4716 var x = 10; 4717 function inner() { 4718 return x; 4719 } 4720 return inner(); 4721 } 4722 outer() 4723 "#; 4724 match eval(src).unwrap() { 4725 Value::Number(n) => assert_eq!(n, 10.0), 4726 v => panic!("expected 10, got {v:?}"), 4727 } 4728 } 4729 4730 #[test] 4731 fn test_closure_return_function() { 4732 // Closure survives the outer function's return. 4733 let src = r#" 4734 function makeAdder(x) { 4735 return function(y) { return x + y; }; 4736 } 4737 var add5 = makeAdder(5); 4738 add5(3) 4739 "#; 4740 match eval(src).unwrap() { 4741 Value::Number(n) => assert_eq!(n, 8.0), 4742 v => panic!("expected 8, got {v:?}"), 4743 } 4744 } 4745 4746 #[test] 4747 fn test_closure_mutation() { 4748 // Closures share live references — mutation is visible. 4749 let src = r#" 4750 function counter() { 4751 var n = 0; 4752 return function() { n = n + 1; return n; }; 4753 } 4754 var c = counter(); 4755 c(); 4756 c(); 4757 c() 4758 "#; 4759 match eval(src).unwrap() { 4760 Value::Number(n) => assert_eq!(n, 3.0), 4761 v => panic!("expected 3, got {v:?}"), 4762 } 4763 } 4764 4765 #[test] 4766 fn test_closure_shared_variable() { 4767 // Two closures from the same scope share the same variable. 4768 let src = r#" 4769 function make() { 4770 var x = 0; 4771 function inc() { x = x + 1; } 4772 function get() { return x; } 4773 inc(); 4774 inc(); 4775 return get(); 4776 } 4777 make() 4778 "#; 4779 match eval(src).unwrap() { 4780 Value::Number(n) => assert_eq!(n, 2.0), 4781 v => panic!("expected 2, got {v:?}"), 4782 } 4783 } 4784 4785 #[test] 4786 fn test_closure_arrow() { 4787 // Arrow function captures outer variable. 4788 let src = r#" 4789 function outer() { 4790 var x = 42; 4791 var f = () => x; 4792 return f(); 4793 } 4794 outer() 4795 "#; 4796 match eval(src).unwrap() { 4797 Value::Number(n) => assert_eq!(n, 42.0), 4798 v => panic!("expected 42, got {v:?}"), 4799 } 4800 } 4801 4802 #[test] 4803 fn test_closure_nested() { 4804 // Transitive capture: grandchild function reads grandparent variable. 4805 let src = r#" 4806 function outer() { 4807 var x = 100; 4808 function middle() { 4809 function inner() { 4810 return x; 4811 } 4812 return inner(); 4813 } 4814 return middle(); 4815 } 4816 outer() 4817 "#; 4818 match eval(src).unwrap() { 4819 Value::Number(n) => assert_eq!(n, 100.0), 4820 v => panic!("expected 100, got {v:?}"), 4821 } 4822 } 4823 4824 #[test] 4825 fn test_closure_param_capture() { 4826 // Closure captures a function parameter. 4827 let src = r#" 4828 function multiply(factor) { 4829 return function(x) { return x * factor; }; 4830 } 4831 var double = multiply(2); 4832 double(7) 4833 "#; 4834 match eval(src).unwrap() { 4835 Value::Number(n) => assert_eq!(n, 14.0), 4836 v => panic!("expected 14, got {v:?}"), 4837 } 4838 } 4839 4840 // ── const tests ────────────────────────────────────────── 4841 4842 #[test] 4843 fn test_const_basic() { 4844 let src = "const x = 42; x"; 4845 match eval(src).unwrap() { 4846 Value::Number(n) => assert_eq!(n, 42.0), 4847 v => panic!("expected 42, got {v:?}"), 4848 } 4849 } 4850 4851 #[test] 4852 fn test_const_reassignment_error() { 4853 let src = "const x = 1; x = 2;"; 4854 let program = crate::parser::Parser::parse(src).expect("parse ok"); 4855 let result = crate::compiler::compile(&program); 4856 assert!( 4857 result.is_err(), 4858 "const reassignment should be a compile error" 4859 ); 4860 } 4861 4862 #[test] 4863 fn test_const_missing_init_error() { 4864 let src = "const x;"; 4865 let program = crate::parser::Parser::parse(src).expect("parse ok"); 4866 let result = crate::compiler::compile(&program); 4867 assert!( 4868 result.is_err(), 4869 "const without initializer should be a compile error" 4870 ); 4871 } 4872 4873 // ── this binding tests ─────────────────────────────────── 4874 4875 #[test] 4876 fn test_method_call_this() { 4877 let src = r#" 4878 var obj = {}; 4879 obj.x = 10; 4880 obj.getX = function() { return this.x; }; 4881 obj.getX() 4882 "#; 4883 match eval(src).unwrap() { 4884 Value::Number(n) => assert_eq!(n, 10.0), 4885 v => panic!("expected 10, got {v:?}"), 4886 } 4887 } 4888 4889 // ── Object built-in tests ──────────────────────────────── 4890 4891 #[test] 4892 fn test_object_keys() { 4893 let src = r#" 4894 var obj = {}; 4895 obj.a = 1; 4896 obj.b = 2; 4897 obj.c = 3; 4898 var k = Object.keys(obj); 4899 k.length 4900 "#; 4901 match eval(src).unwrap() { 4902 Value::Number(n) => assert_eq!(n, 3.0), 4903 v => panic!("expected 3, got {v:?}"), 4904 } 4905 } 4906 4907 #[test] 4908 fn test_object_values() { 4909 let src = r#" 4910 var obj = {}; 4911 obj.x = 10; 4912 var v = Object.values(obj); 4913 v[0] 4914 "#; 4915 match eval(src).unwrap() { 4916 Value::Number(n) => assert_eq!(n, 10.0), 4917 v => panic!("expected 10, got {v:?}"), 4918 } 4919 } 4920 4921 #[test] 4922 fn test_object_entries() { 4923 let src = r#" 4924 var obj = {}; 4925 obj.x = 42; 4926 var e = Object.entries(obj); 4927 e[0][1] 4928 "#; 4929 match eval(src).unwrap() { 4930 Value::Number(n) => assert_eq!(n, 42.0), 4931 v => panic!("expected 42, got {v:?}"), 4932 } 4933 } 4934 4935 #[test] 4936 fn test_object_assign() { 4937 let src = r#" 4938 var a = {}; 4939 a.x = 1; 4940 var b = {}; 4941 b.y = 2; 4942 var c = Object.assign(a, b); 4943 c.y 4944 "#; 4945 match eval(src).unwrap() { 4946 Value::Number(n) => assert_eq!(n, 2.0), 4947 v => panic!("expected 2, got {v:?}"), 4948 } 4949 } 4950 4951 #[test] 4952 fn test_object_create() { 4953 let src = r#" 4954 var proto = {}; 4955 proto.greet = "hello"; 4956 var child = Object.create(proto); 4957 child.greet 4958 "#; 4959 match eval(src).unwrap() { 4960 Value::String(s) => assert_eq!(s, "hello"), 4961 v => panic!("expected 'hello', got {v:?}"), 4962 } 4963 } 4964 4965 #[test] 4966 fn test_object_is() { 4967 let src = "Object.is(NaN, NaN)"; 4968 match eval(src).unwrap() { 4969 Value::Boolean(b) => assert!(b), 4970 v => panic!("expected true, got {v:?}"), 4971 } 4972 } 4973 4974 #[test] 4975 fn test_object_freeze() { 4976 let src = r#" 4977 var obj = {}; 4978 obj.x = 1; 4979 Object.freeze(obj); 4980 Object.isFrozen(obj) 4981 "#; 4982 match eval(src).unwrap() { 4983 Value::Boolean(b) => assert!(b), 4984 v => panic!("expected true, got {v:?}"), 4985 } 4986 } 4987 4988 #[test] 4989 fn test_object_has_own_property() { 4990 let src = r#" 4991 var obj = {}; 4992 obj.x = 1; 4993 obj.hasOwnProperty("x") 4994 "#; 4995 match eval(src).unwrap() { 4996 Value::Boolean(b) => assert!(b), 4997 v => panic!("expected true, got {v:?}"), 4998 } 4999 } 5000 5001 // ── Array built-in tests ───────────────────────────────── 5002 5003 #[test] 5004 fn test_array_push_pop() { 5005 let src = r#" 5006 var arr = [1, 2, 3]; 5007 arr.push(4); 5008 arr.pop() 5009 "#; 5010 match eval(src).unwrap() { 5011 Value::Number(n) => assert_eq!(n, 4.0), 5012 v => panic!("expected 4, got {v:?}"), 5013 } 5014 } 5015 5016 #[test] 5017 fn test_array_push_length() { 5018 let src = r#" 5019 var arr = []; 5020 arr.push(10); 5021 arr.push(20); 5022 arr.length 5023 "#; 5024 match eval(src).unwrap() { 5025 Value::Number(n) => assert_eq!(n, 2.0), 5026 v => panic!("expected 2, got {v:?}"), 5027 } 5028 } 5029 5030 #[test] 5031 fn test_array_shift_unshift() { 5032 let src = r#" 5033 var arr = [1, 2, 3]; 5034 arr.unshift(0); 5035 arr.shift() 5036 "#; 5037 match eval(src).unwrap() { 5038 Value::Number(n) => assert_eq!(n, 0.0), 5039 v => panic!("expected 0, got {v:?}"), 5040 } 5041 } 5042 5043 #[test] 5044 fn test_array_index_of() { 5045 let src = r#" 5046 var arr = [10, 20, 30]; 5047 arr.indexOf(20) 5048 "#; 5049 match eval(src).unwrap() { 5050 Value::Number(n) => assert_eq!(n, 1.0), 5051 v => panic!("expected 1, got {v:?}"), 5052 } 5053 } 5054 5055 #[test] 5056 fn test_array_includes() { 5057 let src = r#" 5058 var arr = [1, 2, 3]; 5059 arr.includes(2) 5060 "#; 5061 match eval(src).unwrap() { 5062 Value::Boolean(b) => assert!(b), 5063 v => panic!("expected true, got {v:?}"), 5064 } 5065 } 5066 5067 #[test] 5068 fn test_array_join() { 5069 let src = r#" 5070 var arr = [1, 2, 3]; 5071 arr.join("-") 5072 "#; 5073 match eval(src).unwrap() { 5074 Value::String(s) => assert_eq!(s, "1-2-3"), 5075 v => panic!("expected '1-2-3', got {v:?}"), 5076 } 5077 } 5078 5079 #[test] 5080 fn test_array_slice() { 5081 let src = r#" 5082 var arr = [1, 2, 3, 4, 5]; 5083 var s = arr.slice(1, 3); 5084 s.length 5085 "#; 5086 match eval(src).unwrap() { 5087 Value::Number(n) => assert_eq!(n, 2.0), 5088 v => panic!("expected 2, got {v:?}"), 5089 } 5090 } 5091 5092 #[test] 5093 fn test_array_concat() { 5094 let src = r#" 5095 var a = [1, 2]; 5096 var b = [3, 4]; 5097 var c = a.concat(b); 5098 c.length 5099 "#; 5100 match eval(src).unwrap() { 5101 Value::Number(n) => assert_eq!(n, 4.0), 5102 v => panic!("expected 4, got {v:?}"), 5103 } 5104 } 5105 5106 #[test] 5107 fn test_array_reverse() { 5108 let src = r#" 5109 var arr = [1, 2, 3]; 5110 arr.reverse(); 5111 arr[0] 5112 "#; 5113 match eval(src).unwrap() { 5114 Value::Number(n) => assert_eq!(n, 3.0), 5115 v => panic!("expected 3, got {v:?}"), 5116 } 5117 } 5118 5119 #[test] 5120 fn test_array_splice() { 5121 let src = r#" 5122 var arr = [1, 2, 3, 4, 5]; 5123 var removed = arr.splice(1, 2); 5124 removed.length 5125 "#; 5126 match eval(src).unwrap() { 5127 Value::Number(n) => assert_eq!(n, 2.0), 5128 v => panic!("expected 2, got {v:?}"), 5129 } 5130 } 5131 5132 #[test] 5133 fn test_array_is_array() { 5134 let src = "Array.isArray([1, 2, 3])"; 5135 match eval(src).unwrap() { 5136 Value::Boolean(b) => assert!(b), 5137 v => panic!("expected true, got {v:?}"), 5138 } 5139 } 5140 5141 #[test] 5142 fn test_array_map() { 5143 let src = r#" 5144 var arr = [1, 2, 3]; 5145 var doubled = arr.map(function(x) { return x * 2; }); 5146 doubled[1] 5147 "#; 5148 match eval(src).unwrap() { 5149 Value::Number(n) => assert_eq!(n, 4.0), 5150 v => panic!("expected 4, got {v:?}"), 5151 } 5152 } 5153 5154 #[test] 5155 fn test_array_filter() { 5156 let src = r#" 5157 var arr = [1, 2, 3, 4, 5]; 5158 var evens = arr.filter(function(x) { return x % 2 === 0; }); 5159 evens.length 5160 "#; 5161 match eval(src).unwrap() { 5162 Value::Number(n) => assert_eq!(n, 2.0), 5163 v => panic!("expected 2, got {v:?}"), 5164 } 5165 } 5166 5167 #[test] 5168 fn test_array_reduce() { 5169 let src = r#" 5170 var arr = [1, 2, 3, 4]; 5171 arr.reduce(function(acc, x) { return acc + x; }, 0) 5172 "#; 5173 match eval(src).unwrap() { 5174 Value::Number(n) => assert_eq!(n, 10.0), 5175 v => panic!("expected 10, got {v:?}"), 5176 } 5177 } 5178 5179 #[test] 5180 fn test_array_foreach() { 5181 let src = r#" 5182 var arr = [1, 2, 3]; 5183 var sum = 0; 5184 arr.forEach(function(x) { sum = sum + x; }); 5185 sum 5186 "#; 5187 match eval(src).unwrap() { 5188 Value::Number(n) => assert_eq!(n, 6.0), 5189 v => panic!("expected 6, got {v:?}"), 5190 } 5191 } 5192 5193 #[test] 5194 fn test_array_find() { 5195 let src = r#" 5196 var arr = [1, 2, 3, 4]; 5197 arr.find(function(x) { return x > 2; }) 5198 "#; 5199 match eval(src).unwrap() { 5200 Value::Number(n) => assert_eq!(n, 3.0), 5201 v => panic!("expected 3, got {v:?}"), 5202 } 5203 } 5204 5205 #[test] 5206 fn test_array_find_index() { 5207 let src = r#" 5208 var arr = [1, 2, 3, 4]; 5209 arr.findIndex(function(x) { return x > 2; }) 5210 "#; 5211 match eval(src).unwrap() { 5212 Value::Number(n) => assert_eq!(n, 2.0), 5213 v => panic!("expected 2, got {v:?}"), 5214 } 5215 } 5216 5217 #[test] 5218 fn test_array_some_every() { 5219 let src = r#" 5220 var arr = [2, 4, 6]; 5221 var all_even = arr.every(function(x) { return x % 2 === 0; }); 5222 var has_big = arr.some(function(x) { return x > 10; }); 5223 all_even && !has_big 5224 "#; 5225 match eval(src).unwrap() { 5226 Value::Boolean(b) => assert!(b), 5227 v => panic!("expected true, got {v:?}"), 5228 } 5229 } 5230 5231 #[test] 5232 fn test_array_sort() { 5233 let src = r#" 5234 var arr = [3, 1, 2]; 5235 arr.sort(); 5236 arr[0] 5237 "#; 5238 match eval(src).unwrap() { 5239 Value::Number(n) => assert_eq!(n, 1.0), 5240 v => panic!("expected 1, got {v:?}"), 5241 } 5242 } 5243 5244 #[test] 5245 fn test_array_sort_custom() { 5246 let src = r#" 5247 var arr = [3, 1, 2]; 5248 arr.sort(function(a, b) { return a - b; }); 5249 arr[2] 5250 "#; 5251 match eval(src).unwrap() { 5252 Value::Number(n) => assert_eq!(n, 3.0), 5253 v => panic!("expected 3, got {v:?}"), 5254 } 5255 } 5256 5257 #[test] 5258 fn test_array_from() { 5259 let src = r#" 5260 var arr = Array.from("abc"); 5261 arr.length 5262 "#; 5263 match eval(src).unwrap() { 5264 Value::Number(n) => assert_eq!(n, 3.0), 5265 v => panic!("expected 3, got {v:?}"), 5266 } 5267 } 5268 5269 #[test] 5270 fn test_array_from_array() { 5271 let src = r#" 5272 var orig = [10, 20, 30]; 5273 var copy = Array.from(orig); 5274 copy[2] 5275 "#; 5276 match eval(src).unwrap() { 5277 Value::Number(n) => assert_eq!(n, 30.0), 5278 v => panic!("expected 30, got {v:?}"), 5279 } 5280 } 5281 5282 #[test] 5283 fn test_array_flat() { 5284 let src = r#" 5285 var arr = [[1, 2], [3, 4]]; 5286 var flat = arr.flat(); 5287 flat.length 5288 "#; 5289 match eval(src).unwrap() { 5290 Value::Number(n) => assert_eq!(n, 4.0), 5291 v => panic!("expected 4, got {v:?}"), 5292 } 5293 } 5294 5295 // ── Error built-in tests ───────────────────────────────── 5296 5297 #[test] 5298 fn test_error_constructor() { 5299 let src = r#" 5300 var e = new Error("oops"); 5301 e.message 5302 "#; 5303 match eval(src).unwrap() { 5304 Value::String(s) => assert_eq!(s, "oops"), 5305 v => panic!("expected 'oops', got {v:?}"), 5306 } 5307 } 5308 5309 #[test] 5310 fn test_type_error_constructor() { 5311 let src = r#" 5312 var e = new TypeError("bad type"); 5313 e.message 5314 "#; 5315 match eval(src).unwrap() { 5316 Value::String(s) => assert_eq!(s, "bad type"), 5317 v => panic!("expected 'bad type', got {v:?}"), 5318 } 5319 } 5320 5321 // ── Global function tests ──────────────────────────────── 5322 5323 #[test] 5324 fn test_parse_int() { 5325 let src = "parseInt('42')"; 5326 match eval(src).unwrap() { 5327 Value::Number(n) => assert_eq!(n, 42.0), 5328 v => panic!("expected 42, got {v:?}"), 5329 } 5330 } 5331 5332 #[test] 5333 fn test_parse_int_hex() { 5334 let src = "parseInt('0xFF', 16)"; 5335 match eval(src).unwrap() { 5336 Value::Number(n) => assert_eq!(n, 255.0), 5337 v => panic!("expected 255, got {v:?}"), 5338 } 5339 } 5340 5341 #[test] 5342 fn test_is_nan() { 5343 let src = "isNaN(NaN)"; 5344 match eval(src).unwrap() { 5345 Value::Boolean(b) => assert!(b), 5346 v => panic!("expected true, got {v:?}"), 5347 } 5348 } 5349 5350 #[test] 5351 fn test_is_finite() { 5352 let src = "isFinite(42)"; 5353 match eval(src).unwrap() { 5354 Value::Boolean(b) => assert!(b), 5355 v => panic!("expected true, got {v:?}"), 5356 } 5357 } 5358 5359 // ── String built-in tests ───────────────────────────────── 5360 5361 #[test] 5362 fn test_string_constructor() { 5363 match eval("String(42)").unwrap() { 5364 Value::String(s) => assert_eq!(s, "42"), 5365 v => panic!("expected '42', got {v:?}"), 5366 } 5367 match eval("String(true)").unwrap() { 5368 Value::String(s) => assert_eq!(s, "true"), 5369 v => panic!("expected 'true', got {v:?}"), 5370 } 5371 match eval("String()").unwrap() { 5372 Value::String(s) => assert_eq!(s, ""), 5373 v => panic!("expected '', got {v:?}"), 5374 } 5375 } 5376 5377 #[test] 5378 fn test_string_length() { 5379 match eval("'hello'.length").unwrap() { 5380 Value::Number(n) => assert_eq!(n, 5.0), 5381 v => panic!("expected 5, got {v:?}"), 5382 } 5383 } 5384 5385 #[test] 5386 fn test_string_char_at() { 5387 match eval("'hello'.charAt(1)").unwrap() { 5388 Value::String(s) => assert_eq!(s, "e"), 5389 v => panic!("expected 'e', got {v:?}"), 5390 } 5391 match eval("'hello'.charAt(10)").unwrap() { 5392 Value::String(s) => assert_eq!(s, ""), 5393 v => panic!("expected '', got {v:?}"), 5394 } 5395 } 5396 5397 #[test] 5398 fn test_string_char_code_at() { 5399 match eval("'A'.charCodeAt(0)").unwrap() { 5400 Value::Number(n) => assert_eq!(n, 65.0), 5401 v => panic!("expected 65, got {v:?}"), 5402 } 5403 } 5404 5405 #[test] 5406 fn test_string_proto_concat() { 5407 match eval("'hello'.concat(' ', 'world')").unwrap() { 5408 Value::String(s) => assert_eq!(s, "hello world"), 5409 v => panic!("expected 'hello world', got {v:?}"), 5410 } 5411 } 5412 5413 #[test] 5414 fn test_string_slice() { 5415 match eval("'hello world'.slice(6)").unwrap() { 5416 Value::String(s) => assert_eq!(s, "world"), 5417 v => panic!("expected 'world', got {v:?}"), 5418 } 5419 match eval("'hello'.slice(1, 3)").unwrap() { 5420 Value::String(s) => assert_eq!(s, "el"), 5421 v => panic!("expected 'el', got {v:?}"), 5422 } 5423 match eval("'hello'.slice(-3)").unwrap() { 5424 Value::String(s) => assert_eq!(s, "llo"), 5425 v => panic!("expected 'llo', got {v:?}"), 5426 } 5427 } 5428 5429 #[test] 5430 fn test_string_substring() { 5431 match eval("'hello'.substring(1, 3)").unwrap() { 5432 Value::String(s) => assert_eq!(s, "el"), 5433 v => panic!("expected 'el', got {v:?}"), 5434 } 5435 // substring swaps args if start > end 5436 match eval("'hello'.substring(3, 1)").unwrap() { 5437 Value::String(s) => assert_eq!(s, "el"), 5438 v => panic!("expected 'el', got {v:?}"), 5439 } 5440 } 5441 5442 #[test] 5443 fn test_string_index_of() { 5444 match eval("'hello world'.indexOf('world')").unwrap() { 5445 Value::Number(n) => assert_eq!(n, 6.0), 5446 v => panic!("expected 6, got {v:?}"), 5447 } 5448 match eval("'hello'.indexOf('xyz')").unwrap() { 5449 Value::Number(n) => assert_eq!(n, -1.0), 5450 v => panic!("expected -1, got {v:?}"), 5451 } 5452 } 5453 5454 #[test] 5455 fn test_string_last_index_of() { 5456 match eval("'abcabc'.lastIndexOf('abc')").unwrap() { 5457 Value::Number(n) => assert_eq!(n, 3.0), 5458 v => panic!("expected 3, got {v:?}"), 5459 } 5460 } 5461 5462 #[test] 5463 fn test_string_includes() { 5464 match eval("'hello world'.includes('world')").unwrap() { 5465 Value::Boolean(b) => assert!(b), 5466 v => panic!("expected true, got {v:?}"), 5467 } 5468 match eval("'hello'.includes('xyz')").unwrap() { 5469 Value::Boolean(b) => assert!(!b), 5470 v => panic!("expected false, got {v:?}"), 5471 } 5472 } 5473 5474 #[test] 5475 fn test_string_starts_ends_with() { 5476 match eval("'hello'.startsWith('hel')").unwrap() { 5477 Value::Boolean(b) => assert!(b), 5478 v => panic!("expected true, got {v:?}"), 5479 } 5480 match eval("'hello'.endsWith('llo')").unwrap() { 5481 Value::Boolean(b) => assert!(b), 5482 v => panic!("expected true, got {v:?}"), 5483 } 5484 } 5485 5486 #[test] 5487 fn test_string_trim() { 5488 match eval("' hello '.trim()").unwrap() { 5489 Value::String(s) => assert_eq!(s, "hello"), 5490 v => panic!("expected 'hello', got {v:?}"), 5491 } 5492 match eval("' hello '.trimStart()").unwrap() { 5493 Value::String(s) => assert_eq!(s, "hello "), 5494 v => panic!("expected 'hello ', got {v:?}"), 5495 } 5496 match eval("' hello '.trimEnd()").unwrap() { 5497 Value::String(s) => assert_eq!(s, " hello"), 5498 v => panic!("expected ' hello', got {v:?}"), 5499 } 5500 } 5501 5502 #[test] 5503 fn test_string_pad() { 5504 match eval("'5'.padStart(3, '0')").unwrap() { 5505 Value::String(s) => assert_eq!(s, "005"), 5506 v => panic!("expected '005', got {v:?}"), 5507 } 5508 match eval("'5'.padEnd(3, '0')").unwrap() { 5509 Value::String(s) => assert_eq!(s, "500"), 5510 v => panic!("expected '500', got {v:?}"), 5511 } 5512 } 5513 5514 #[test] 5515 fn test_string_repeat() { 5516 match eval("'ab'.repeat(3)").unwrap() { 5517 Value::String(s) => assert_eq!(s, "ababab"), 5518 v => panic!("expected 'ababab', got {v:?}"), 5519 } 5520 } 5521 5522 #[test] 5523 fn test_string_split() { 5524 // split returns an array; verify length and elements. 5525 match eval("'a,b,c'.split(',').length").unwrap() { 5526 Value::Number(n) => assert_eq!(n, 3.0), 5527 v => panic!("expected 3, got {v:?}"), 5528 } 5529 match eval("'a,b,c'.split(',')[0]").unwrap() { 5530 Value::String(s) => assert_eq!(s, "a"), 5531 v => panic!("expected 'a', got {v:?}"), 5532 } 5533 match eval("'a,b,c'.split(',')[2]").unwrap() { 5534 Value::String(s) => assert_eq!(s, "c"), 5535 v => panic!("expected 'c', got {v:?}"), 5536 } 5537 } 5538 5539 #[test] 5540 fn test_string_replace() { 5541 match eval("'hello world'.replace('world', 'there')").unwrap() { 5542 Value::String(s) => assert_eq!(s, "hello there"), 5543 v => panic!("expected 'hello there', got {v:?}"), 5544 } 5545 } 5546 5547 #[test] 5548 fn test_string_replace_all() { 5549 match eval("'aabbcc'.replaceAll('b', 'x')").unwrap() { 5550 Value::String(s) => assert_eq!(s, "aaxxcc"), 5551 v => panic!("expected 'aaxxcc', got {v:?}"), 5552 } 5553 } 5554 5555 #[test] 5556 fn test_string_case() { 5557 match eval("'Hello'.toLowerCase()").unwrap() { 5558 Value::String(s) => assert_eq!(s, "hello"), 5559 v => panic!("expected 'hello', got {v:?}"), 5560 } 5561 match eval("'Hello'.toUpperCase()").unwrap() { 5562 Value::String(s) => assert_eq!(s, "HELLO"), 5563 v => panic!("expected 'HELLO', got {v:?}"), 5564 } 5565 } 5566 5567 #[test] 5568 fn test_string_at() { 5569 match eval("'hello'.at(0)").unwrap() { 5570 Value::String(s) => assert_eq!(s, "h"), 5571 v => panic!("expected 'h', got {v:?}"), 5572 } 5573 match eval("'hello'.at(-1)").unwrap() { 5574 Value::String(s) => assert_eq!(s, "o"), 5575 v => panic!("expected 'o', got {v:?}"), 5576 } 5577 } 5578 5579 #[test] 5580 fn test_string_from_char_code() { 5581 match eval("String.fromCharCode(72, 101, 108)").unwrap() { 5582 Value::String(s) => assert_eq!(s, "Hel"), 5583 v => panic!("expected 'Hel', got {v:?}"), 5584 } 5585 } 5586 5587 #[test] 5588 fn test_string_from_code_point() { 5589 match eval("String.fromCodePoint(65, 66, 67)").unwrap() { 5590 Value::String(s) => assert_eq!(s, "ABC"), 5591 v => panic!("expected 'ABC', got {v:?}"), 5592 } 5593 } 5594 5595 // ── Number built-in tests ───────────────────────────────── 5596 5597 #[test] 5598 fn test_number_constructor() { 5599 match eval("Number('42')").unwrap() { 5600 Value::Number(n) => assert_eq!(n, 42.0), 5601 v => panic!("expected 42, got {v:?}"), 5602 } 5603 match eval("Number(true)").unwrap() { 5604 Value::Number(n) => assert_eq!(n, 1.0), 5605 v => panic!("expected 1, got {v:?}"), 5606 } 5607 match eval("Number()").unwrap() { 5608 Value::Number(n) => assert_eq!(n, 0.0), 5609 v => panic!("expected 0, got {v:?}"), 5610 } 5611 } 5612 5613 #[test] 5614 fn test_number_is_nan() { 5615 match eval("Number.isNaN(NaN)").unwrap() { 5616 Value::Boolean(b) => assert!(b), 5617 v => panic!("expected true, got {v:?}"), 5618 } 5619 match eval("Number.isNaN(42)").unwrap() { 5620 Value::Boolean(b) => assert!(!b), 5621 v => panic!("expected false, got {v:?}"), 5622 } 5623 // Number.isNaN doesn't coerce — string "NaN" is not NaN. 5624 match eval("Number.isNaN('NaN')").unwrap() { 5625 Value::Boolean(b) => assert!(!b), 5626 v => panic!("expected false, got {v:?}"), 5627 } 5628 } 5629 5630 #[test] 5631 fn test_number_is_finite() { 5632 match eval("Number.isFinite(42)").unwrap() { 5633 Value::Boolean(b) => assert!(b), 5634 v => panic!("expected true, got {v:?}"), 5635 } 5636 match eval("Number.isFinite(Infinity)").unwrap() { 5637 Value::Boolean(b) => assert!(!b), 5638 v => panic!("expected false, got {v:?}"), 5639 } 5640 } 5641 5642 #[test] 5643 fn test_number_is_integer() { 5644 match eval("Number.isInteger(42)").unwrap() { 5645 Value::Boolean(b) => assert!(b), 5646 v => panic!("expected true, got {v:?}"), 5647 } 5648 match eval("Number.isInteger(42.5)").unwrap() { 5649 Value::Boolean(b) => assert!(!b), 5650 v => panic!("expected false, got {v:?}"), 5651 } 5652 } 5653 5654 #[test] 5655 fn test_number_is_safe_integer() { 5656 match eval("Number.isSafeInteger(42)").unwrap() { 5657 Value::Boolean(b) => assert!(b), 5658 v => panic!("expected true, got {v:?}"), 5659 } 5660 match eval("Number.isSafeInteger(9007199254740992)").unwrap() { 5661 Value::Boolean(b) => assert!(!b), 5662 v => panic!("expected false, got {v:?}"), 5663 } 5664 } 5665 5666 #[test] 5667 fn test_number_constants() { 5668 match eval("Number.MAX_SAFE_INTEGER").unwrap() { 5669 Value::Number(n) => assert_eq!(n, 9007199254740991.0), 5670 v => panic!("expected MAX_SAFE_INTEGER, got {v:?}"), 5671 } 5672 match eval("Number.EPSILON").unwrap() { 5673 Value::Number(n) => assert_eq!(n, f64::EPSILON), 5674 v => panic!("expected EPSILON, got {v:?}"), 5675 } 5676 } 5677 5678 #[test] 5679 fn test_number_to_fixed() { 5680 match eval("var n = 3.14159; n.toFixed(2)").unwrap() { 5681 Value::String(s) => assert_eq!(s, "3.14"), 5682 v => panic!("expected '3.14', got {v:?}"), 5683 } 5684 } 5685 5686 #[test] 5687 fn test_number_to_string_radix() { 5688 match eval("var n = 255; n.toString(16)").unwrap() { 5689 Value::String(s) => assert_eq!(s, "ff"), 5690 v => panic!("expected 'ff', got {v:?}"), 5691 } 5692 match eval("var n = 10; n.toString(2)").unwrap() { 5693 Value::String(s) => assert_eq!(s, "1010"), 5694 v => panic!("expected '1010', got {v:?}"), 5695 } 5696 } 5697 5698 #[test] 5699 fn test_number_parse_int() { 5700 match eval("Number.parseInt('42')").unwrap() { 5701 Value::Number(n) => assert_eq!(n, 42.0), 5702 v => panic!("expected 42, got {v:?}"), 5703 } 5704 } 5705 5706 // ── Boolean built-in tests ──────────────────────────────── 5707 5708 #[test] 5709 fn test_boolean_constructor() { 5710 match eval("Boolean(1)").unwrap() { 5711 Value::Boolean(b) => assert!(b), 5712 v => panic!("expected true, got {v:?}"), 5713 } 5714 match eval("Boolean(0)").unwrap() { 5715 Value::Boolean(b) => assert!(!b), 5716 v => panic!("expected false, got {v:?}"), 5717 } 5718 match eval("Boolean('')").unwrap() { 5719 Value::Boolean(b) => assert!(!b), 5720 v => panic!("expected false, got {v:?}"), 5721 } 5722 match eval("Boolean('hello')").unwrap() { 5723 Value::Boolean(b) => assert!(b), 5724 v => panic!("expected true, got {v:?}"), 5725 } 5726 } 5727 5728 #[test] 5729 fn test_boolean_to_string() { 5730 match eval("true.toString()").unwrap() { 5731 Value::String(s) => assert_eq!(s, "true"), 5732 v => panic!("expected 'true', got {v:?}"), 5733 } 5734 match eval("false.toString()").unwrap() { 5735 Value::String(s) => assert_eq!(s, "false"), 5736 v => panic!("expected 'false', got {v:?}"), 5737 } 5738 } 5739 5740 // ── Symbol built-in tests ───────────────────────────────── 5741 5742 #[test] 5743 fn test_symbol_uniqueness() { 5744 // Each Symbol() call should produce a unique value. 5745 match eval("var a = Symbol('x'); var b = Symbol('x'); a === b").unwrap() { 5746 Value::Boolean(b) => assert!(!b), 5747 v => panic!("expected false, got {v:?}"), 5748 } 5749 } 5750 5751 #[test] 5752 fn test_symbol_well_known() { 5753 match eval("typeof Symbol.iterator").unwrap() { 5754 Value::String(s) => assert_eq!(s, "string"), 5755 v => panic!("expected 'string', got {v:?}"), 5756 } 5757 match eval("Symbol.iterator").unwrap() { 5758 Value::String(s) => assert_eq!(s, "@@iterator"), 5759 v => panic!("expected '@@iterator', got {v:?}"), 5760 } 5761 } 5762 5763 #[test] 5764 fn test_symbol_for_and_key_for() { 5765 // "for" is a keyword, so use bracket notation: Symbol["for"](...). 5766 match eval("Symbol['for']('test') === Symbol['for']('test')").unwrap() { 5767 Value::Boolean(b) => assert!(b), 5768 v => panic!("expected true, got {v:?}"), 5769 } 5770 match eval("Symbol.keyFor(Symbol['for']('mykey'))").unwrap() { 5771 Value::String(s) => assert_eq!(s, "mykey"), 5772 v => panic!("expected 'mykey', got {v:?}"), 5773 } 5774 } 5775 5776 // ── Primitive auto-boxing tests ─────────────────────────── 5777 5778 #[test] 5779 fn test_string_method_chaining() { 5780 match eval("' Hello World '.trim().toLowerCase()").unwrap() { 5781 Value::String(s) => assert_eq!(s, "hello world"), 5782 v => panic!("expected 'hello world', got {v:?}"), 5783 } 5784 } 5785 5786 #[test] 5787 fn test_string_substr() { 5788 match eval("'hello world'.substr(6, 5)").unwrap() { 5789 Value::String(s) => assert_eq!(s, "world"), 5790 v => panic!("expected 'world', got {v:?}"), 5791 } 5792 } 5793 5794 // ── Math built-in ───────────────────────────────────────── 5795 5796 #[test] 5797 fn test_math_constants() { 5798 let r = eval("Math.PI").unwrap(); 5799 match r { 5800 Value::Number(n) => assert!((n - std::f64::consts::PI).abs() < 1e-10), 5801 _ => panic!("Expected number"), 5802 } 5803 let r = eval("Math.E").unwrap(); 5804 match r { 5805 Value::Number(n) => assert!((n - std::f64::consts::E).abs() < 1e-10), 5806 _ => panic!("Expected number"), 5807 } 5808 let r = eval("Math.SQRT2").unwrap(); 5809 match r { 5810 Value::Number(n) => assert!((n - std::f64::consts::SQRT_2).abs() < 1e-10), 5811 _ => panic!("Expected number"), 5812 } 5813 } 5814 5815 #[test] 5816 fn test_math_abs() { 5817 assert_eq!(eval("Math.abs(-5)").unwrap().to_number(), 5.0); 5818 assert_eq!(eval("Math.abs(3)").unwrap().to_number(), 3.0); 5819 assert_eq!(eval("Math.abs(0)").unwrap().to_number(), 0.0); 5820 } 5821 5822 #[test] 5823 fn test_math_floor_ceil_round_trunc() { 5824 assert_eq!(eval("Math.floor(4.7)").unwrap().to_number(), 4.0); 5825 assert_eq!(eval("Math.ceil(4.2)").unwrap().to_number(), 5.0); 5826 assert_eq!(eval("Math.round(4.5)").unwrap().to_number(), 5.0); 5827 assert_eq!(eval("Math.round(4.4)").unwrap().to_number(), 4.0); 5828 assert_eq!(eval("Math.trunc(4.7)").unwrap().to_number(), 4.0); 5829 assert_eq!(eval("Math.trunc(-4.7)").unwrap().to_number(), -4.0); 5830 } 5831 5832 #[test] 5833 fn test_math_max_min() { 5834 assert_eq!(eval("Math.max(1, 3, 2)").unwrap().to_number(), 3.0); 5835 assert_eq!(eval("Math.min(1, 3, 2)").unwrap().to_number(), 1.0); 5836 assert_eq!(eval("Math.max()").unwrap().to_number(), f64::NEG_INFINITY); 5837 assert_eq!(eval("Math.min()").unwrap().to_number(), f64::INFINITY); 5838 } 5839 5840 #[test] 5841 fn test_math_pow_sqrt() { 5842 assert_eq!(eval("Math.pow(2, 10)").unwrap().to_number(), 1024.0); 5843 assert_eq!(eval("Math.sqrt(9)").unwrap().to_number(), 3.0); 5844 let cbrt = eval("Math.cbrt(27)").unwrap().to_number(); 5845 assert!((cbrt - 3.0).abs() < 1e-10); 5846 } 5847 5848 #[test] 5849 fn test_math_trig() { 5850 let sin = eval("Math.sin(0)").unwrap().to_number(); 5851 assert!(sin.abs() < 1e-10); 5852 let cos = eval("Math.cos(0)").unwrap().to_number(); 5853 assert!((cos - 1.0).abs() < 1e-10); 5854 let atan2 = eval("Math.atan2(1, 1)").unwrap().to_number(); 5855 assert!((atan2 - std::f64::consts::FRAC_PI_4).abs() < 1e-10); 5856 } 5857 5858 #[test] 5859 fn test_math_log_exp() { 5860 let exp = eval("Math.exp(1)").unwrap().to_number(); 5861 assert!((exp - std::f64::consts::E).abs() < 1e-10); 5862 let log = eval("Math.log(Math.E)").unwrap().to_number(); 5863 assert!((log - 1.0).abs() < 1e-10); 5864 let log2 = eval("Math.log2(8)").unwrap().to_number(); 5865 assert!((log2 - 3.0).abs() < 1e-10); 5866 let log10 = eval("Math.log10(1000)").unwrap().to_number(); 5867 assert!((log10 - 3.0).abs() < 1e-10); 5868 } 5869 5870 #[test] 5871 fn test_math_sign() { 5872 assert_eq!(eval("Math.sign(5)").unwrap().to_number(), 1.0); 5873 assert_eq!(eval("Math.sign(-5)").unwrap().to_number(), -1.0); 5874 assert_eq!(eval("Math.sign(0)").unwrap().to_number(), 0.0); 5875 } 5876 5877 #[test] 5878 fn test_math_clz32() { 5879 assert_eq!(eval("Math.clz32(1)").unwrap().to_number(), 31.0); 5880 assert_eq!(eval("Math.clz32(0)").unwrap().to_number(), 32.0); 5881 } 5882 5883 #[test] 5884 fn test_math_imul() { 5885 assert_eq!(eval("Math.imul(3, 4)").unwrap().to_number(), 12.0); 5886 assert_eq!(eval("Math.imul(0xffffffff, 5)").unwrap().to_number(), -5.0); 5887 } 5888 5889 #[test] 5890 fn test_math_hypot() { 5891 assert_eq!(eval("Math.hypot(3, 4)").unwrap().to_number(), 5.0); 5892 assert_eq!(eval("Math.hypot()").unwrap().to_number(), 0.0); 5893 } 5894 5895 #[test] 5896 fn test_math_random() { 5897 match eval("var r = Math.random(); r >= 0 && r < 1").unwrap() { 5898 Value::Boolean(b) => assert!(b), 5899 v => panic!("expected true, got {v:?}"), 5900 } 5901 } 5902 5903 #[test] 5904 fn test_math_fround() { 5905 let r = eval("Math.fround(5.5)").unwrap().to_number(); 5906 assert_eq!(r, 5.5f32 as f64); 5907 } 5908 5909 // ── Date built-in ───────────────────────────────────────── 5910 5911 #[test] 5912 fn test_date_now() { 5913 let r = eval("Date.now()").unwrap().to_number(); 5914 assert!(r > 1_577_836_800_000.0); 5915 } 5916 5917 #[test] 5918 fn test_date_utc() { 5919 let r = eval("Date.UTC(2020, 0, 1)").unwrap().to_number(); 5920 assert_eq!(r, 1_577_836_800_000.0); 5921 } 5922 5923 #[test] 5924 fn test_date_parse() { 5925 let r = eval("Date.parse('2020-01-01T00:00:00.000Z')") 5926 .unwrap() 5927 .to_number(); 5928 assert_eq!(r, 1_577_836_800_000.0); 5929 } 5930 5931 #[test] 5932 fn test_date_constructor_ms() { 5933 let r = eval("var d = new Date(1577836800000); d.getFullYear()") 5934 .unwrap() 5935 .to_number(); 5936 assert_eq!(r, 2020.0); 5937 } 5938 5939 #[test] 5940 fn test_date_constructor_components() { 5941 let r = eval("var d = new Date(2020, 0, 1, 0, 0, 0, 0); d.getTime()") 5942 .unwrap() 5943 .to_number(); 5944 assert_eq!(r, 1_577_836_800_000.0); 5945 } 5946 5947 #[test] 5948 fn test_date_getters() { 5949 let src = r#" 5950 var d = new Date(1577836800000); 5951 var results = [ 5952 d.getFullYear(), 5953 d.getMonth(), 5954 d.getDate(), 5955 d.getHours(), 5956 d.getMinutes(), 5957 d.getSeconds(), 5958 d.getMilliseconds(), 5959 d.getDay() 5960 ]; 5961 results[0] === 2020 && results[1] === 0 && results[2] === 1 && 5962 results[3] === 0 && results[4] === 0 && results[5] === 0 && 5963 results[6] === 0 && results[7] === 3 5964 "#; 5965 match eval(src).unwrap() { 5966 Value::Boolean(b) => assert!(b), 5967 v => panic!("expected true, got {v:?}"), 5968 } 5969 } 5970 5971 #[test] 5972 fn test_date_setters() { 5973 let src = r#" 5974 var d = new Date(1577836800000); 5975 d.setFullYear(2025); 5976 d.getFullYear() 5977 "#; 5978 assert_eq!(eval(src).unwrap().to_number(), 2025.0); 5979 } 5980 5981 #[test] 5982 fn test_date_to_iso_string() { 5983 match eval("var d = new Date(1577836800000); d.toISOString()").unwrap() { 5984 Value::String(s) => assert_eq!(s, "2020-01-01T00:00:00.000Z"), 5985 v => panic!("expected ISO string, got {v:?}"), 5986 } 5987 } 5988 5989 #[test] 5990 fn test_date_value_of() { 5991 let r = eval("var d = new Date(1577836800000); d.valueOf()") 5992 .unwrap() 5993 .to_number(); 5994 assert_eq!(r, 1_577_836_800_000.0); 5995 } 5996 5997 #[test] 5998 fn test_date_to_string() { 5999 let r = eval("var d = new Date(1577836800000); d.toString()").unwrap(); 6000 match r { 6001 Value::String(s) => assert!(s.contains("2020") && s.contains("GMT")), 6002 _ => panic!("Expected string"), 6003 } 6004 } 6005 6006 #[test] 6007 fn test_date_to_json() { 6008 match eval("var d = new Date(1577836800000); d.toJSON()").unwrap() { 6009 Value::String(s) => assert_eq!(s, "2020-01-01T00:00:00.000Z"), 6010 v => panic!("expected ISO string, got {v:?}"), 6011 } 6012 } 6013 6014 #[test] 6015 fn test_date_constructor_string() { 6016 let r = eval("var d = new Date('2020-06-15T12:30:00Z'); d.getMonth()") 6017 .unwrap() 6018 .to_number(); 6019 assert_eq!(r, 5.0); 6020 } 6021 6022 // ── JSON built-in ───────────────────────────────────────── 6023 6024 #[test] 6025 fn test_json_parse_primitives() { 6026 assert!(matches!(eval("JSON.parse('null')").unwrap(), Value::Null)); 6027 match eval("JSON.parse('true')").unwrap() { 6028 Value::Boolean(b) => assert!(b), 6029 v => panic!("expected true, got {v:?}"), 6030 } 6031 match eval("JSON.parse('false')").unwrap() { 6032 Value::Boolean(b) => assert!(!b), 6033 v => panic!("expected false, got {v:?}"), 6034 } 6035 assert_eq!(eval("JSON.parse('42')").unwrap().to_number(), 42.0); 6036 match eval(r#"JSON.parse('"hello"')"#).unwrap() { 6037 Value::String(s) => assert_eq!(s, "hello"), 6038 v => panic!("expected 'hello', got {v:?}"), 6039 } 6040 } 6041 6042 #[test] 6043 fn test_json_parse_array() { 6044 let src = r#" 6045 var a = JSON.parse('[1, 2, 3]'); 6046 a.length === 3 && a[0] === 1 && a[1] === 2 && a[2] === 3 6047 "#; 6048 match eval(src).unwrap() { 6049 Value::Boolean(b) => assert!(b), 6050 v => panic!("expected true, got {v:?}"), 6051 } 6052 } 6053 6054 #[test] 6055 fn test_json_parse_object() { 6056 let src = r#" 6057 var o = JSON.parse('{"name":"test","value":42}'); 6058 o.name === "test" && o.value === 42 6059 "#; 6060 match eval(src).unwrap() { 6061 Value::Boolean(b) => assert!(b), 6062 v => panic!("expected true, got {v:?}"), 6063 } 6064 } 6065 6066 #[test] 6067 fn test_json_parse_nested() { 6068 let src = r#" 6069 var o = JSON.parse('{"a":[1,{"b":2}]}'); 6070 o.a[1].b === 2 6071 "#; 6072 match eval(src).unwrap() { 6073 Value::Boolean(b) => assert!(b), 6074 v => panic!("expected true, got {v:?}"), 6075 } 6076 } 6077 6078 #[test] 6079 fn test_json_parse_invalid() { 6080 assert!(eval("JSON.parse('{invalid}')").is_err()); 6081 assert!(eval("JSON.parse('')").is_err()); 6082 } 6083 6084 #[test] 6085 fn test_json_stringify_primitives() { 6086 match eval("JSON.stringify(null)").unwrap() { 6087 Value::String(s) => assert_eq!(s, "null"), 6088 v => panic!("expected 'null', got {v:?}"), 6089 } 6090 match eval("JSON.stringify(true)").unwrap() { 6091 Value::String(s) => assert_eq!(s, "true"), 6092 v => panic!("expected 'true', got {v:?}"), 6093 } 6094 match eval("JSON.stringify(42)").unwrap() { 6095 Value::String(s) => assert_eq!(s, "42"), 6096 v => panic!("expected '42', got {v:?}"), 6097 } 6098 match eval(r#"JSON.stringify("hello")"#).unwrap() { 6099 Value::String(s) => assert_eq!(s, "\"hello\""), 6100 v => panic!("expected quoted hello, got {v:?}"), 6101 } 6102 } 6103 6104 #[test] 6105 fn test_json_stringify_array() { 6106 match eval("JSON.stringify([1, 2, 3])").unwrap() { 6107 Value::String(s) => assert_eq!(s, "[1,2,3]"), 6108 v => panic!("expected '[1,2,3]', got {v:?}"), 6109 } 6110 } 6111 6112 #[test] 6113 fn test_json_stringify_object() { 6114 let src = r#" 6115 var o = {a: 1, b: "hello"}; 6116 JSON.stringify(o) 6117 "#; 6118 let r = eval(src).unwrap(); 6119 match r { 6120 Value::String(s) => { 6121 assert!(s.contains("\"a\"") && s.contains("\"b\"")); 6122 assert!(s.contains('1') && s.contains("\"hello\"")); 6123 } 6124 _ => panic!("Expected string"), 6125 } 6126 } 6127 6128 #[test] 6129 fn test_json_stringify_nested() { 6130 let src = r#" 6131 JSON.stringify({a: [1, 2], b: {c: 3}}) 6132 "#; 6133 let r = eval(src).unwrap(); 6134 match r { 6135 Value::String(s) => { 6136 assert!(s.contains("[1,2]")); 6137 assert!(s.contains("\"c\":3") || s.contains("\"c\": 3")); 6138 } 6139 _ => panic!("Expected string"), 6140 } 6141 } 6142 6143 #[test] 6144 fn test_json_stringify_special_values() { 6145 match eval("JSON.stringify(NaN)").unwrap() { 6146 Value::String(s) => assert_eq!(s, "null"), 6147 v => panic!("expected 'null', got {v:?}"), 6148 } 6149 match eval("JSON.stringify(Infinity)").unwrap() { 6150 Value::String(s) => assert_eq!(s, "null"), 6151 v => panic!("expected 'null', got {v:?}"), 6152 } 6153 assert!(matches!( 6154 eval("JSON.stringify(undefined)").unwrap(), 6155 Value::Undefined 6156 )); 6157 } 6158 6159 #[test] 6160 fn test_json_stringify_with_indent() { 6161 let src = r#"JSON.stringify([1, 2], null, 2)"#; 6162 let r = eval(src).unwrap(); 6163 match r { 6164 Value::String(s) => { 6165 assert!(s.contains('\n')); 6166 assert!(s.contains(" 1")); 6167 } 6168 _ => panic!("Expected string"), 6169 } 6170 } 6171 6172 #[test] 6173 fn test_json_parse_escape_sequences() { 6174 let src = r#"JSON.parse('"hello\\nworld"')"#; 6175 match eval(src).unwrap() { 6176 Value::String(s) => assert_eq!(s, "hello\nworld"), 6177 v => panic!("expected escaped string, got {v:?}"), 6178 } 6179 } 6180 6181 #[test] 6182 fn test_json_roundtrip() { 6183 let src = r#" 6184 var original = {name: "test", values: [1, 2, 3], nested: {ok: true}}; 6185 var json = JSON.stringify(original); 6186 var parsed = JSON.parse(json); 6187 parsed.name === "test" && parsed.values[1] === 2 && parsed.nested.ok === true 6188 "#; 6189 match eval(src).unwrap() { 6190 Value::Boolean(b) => assert!(b), 6191 v => panic!("expected true, got {v:?}"), 6192 } 6193 } 6194 6195 #[test] 6196 fn test_json_stringify_circular_detection() { 6197 let src = r#" 6198 var obj = {}; 6199 obj.self = obj; 6200 try { 6201 JSON.stringify(obj); 6202 false; 6203 } catch (e) { 6204 e.message.indexOf("circular") !== -1; 6205 } 6206 "#; 6207 match eval(src).unwrap() { 6208 Value::Boolean(b) => assert!(b), 6209 v => panic!("expected true, got {v:?}"), 6210 } 6211 } 6212 6213 #[test] 6214 fn test_json_parse_unicode_escape() { 6215 let src = r#"JSON.parse('"\\u0041"')"#; 6216 match eval(src).unwrap() { 6217 Value::String(s) => assert_eq!(s, "A"), 6218 v => panic!("expected 'A', got {v:?}"), 6219 } 6220 } 6221 6222 #[test] 6223 fn test_json_stringify_empty() { 6224 match eval("JSON.stringify([])").unwrap() { 6225 Value::String(s) => assert_eq!(s, "[]"), 6226 v => panic!("expected '[]', got {v:?}"), 6227 } 6228 match eval("JSON.stringify({})").unwrap() { 6229 Value::String(s) => assert_eq!(s, "{}"), 6230 v => panic!("expected '{{}}', got {v:?}"), 6231 } 6232 } 6233 6234 // ── RegExp tests ──────────────────────────────────────── 6235 6236 #[test] 6237 fn test_regexp_constructor() { 6238 match eval("var r = new RegExp('abc', 'g'); r.source").unwrap() { 6239 Value::String(s) => assert_eq!(s, "abc"), 6240 v => panic!("expected 'abc', got {v:?}"), 6241 } 6242 match eval("var r = new RegExp('abc', 'gi'); r.flags").unwrap() { 6243 Value::String(s) => assert_eq!(s, "gi"), 6244 v => panic!("expected 'gi', got {v:?}"), 6245 } 6246 match eval("var r = new RegExp('abc'); r.global").unwrap() { 6247 Value::Boolean(b) => assert!(!b), 6248 v => panic!("expected false, got {v:?}"), 6249 } 6250 match eval("var r = new RegExp('abc', 'g'); r.global").unwrap() { 6251 Value::Boolean(b) => assert!(b), 6252 v => panic!("expected true, got {v:?}"), 6253 } 6254 } 6255 6256 #[test] 6257 fn test_regexp_test() { 6258 match eval("var r = new RegExp('abc'); r.test('xabcx')").unwrap() { 6259 Value::Boolean(b) => assert!(b), 6260 v => panic!("expected true, got {v:?}"), 6261 } 6262 match eval("var r = new RegExp('abc'); r.test('xyz')").unwrap() { 6263 Value::Boolean(b) => assert!(!b), 6264 v => panic!("expected false, got {v:?}"), 6265 } 6266 match eval("var r = new RegExp('\\\\d+'); r.test('abc123')").unwrap() { 6267 Value::Boolean(b) => assert!(b), 6268 v => panic!("expected true, got {v:?}"), 6269 } 6270 } 6271 6272 #[test] 6273 fn test_regexp_exec() { 6274 match eval("var r = new RegExp('(a)(b)(c)'); var m = r.exec('abc'); m[0]").unwrap() { 6275 Value::String(s) => assert_eq!(s, "abc"), 6276 v => panic!("expected 'abc', got {v:?}"), 6277 } 6278 match eval("var r = new RegExp('(a)(b)(c)'); var m = r.exec('abc'); m[1]").unwrap() { 6279 Value::String(s) => assert_eq!(s, "a"), 6280 v => panic!("expected 'a', got {v:?}"), 6281 } 6282 match eval("var r = new RegExp('b+'); var m = r.exec('aabbc'); m[0]").unwrap() { 6283 Value::String(s) => assert_eq!(s, "bb"), 6284 v => panic!("expected 'bb', got {v:?}"), 6285 } 6286 match eval("var r = new RegExp('xyz'); r.exec('abc')").unwrap() { 6287 Value::Null => {} 6288 v => panic!("expected null, got {v:?}"), 6289 } 6290 } 6291 6292 #[test] 6293 fn test_regexp_exec_global() { 6294 let src = "var r = new RegExp('a', 'g'); r.exec('aba')[0]"; 6295 match eval(src).unwrap() { 6296 Value::String(s) => assert_eq!(s, "a"), 6297 v => panic!("expected 'a', got {v:?}"), 6298 } 6299 let src = r#" 6300 var r = new RegExp('a', 'g'); 6301 r.exec('aba'); 6302 var m = r.exec('aba'); 6303 m[0] + ',' + m.index 6304 "#; 6305 match eval(src).unwrap() { 6306 Value::String(s) => assert_eq!(s, "a,2"), 6307 v => panic!("expected 'a,2', got {v:?}"), 6308 } 6309 } 6310 6311 #[test] 6312 fn test_regexp_to_string() { 6313 match eval("var r = new RegExp('abc', 'gi'); r.toString()").unwrap() { 6314 Value::String(s) => assert_eq!(s, "/abc/gi"), 6315 v => panic!("expected '/abc/gi', got {v:?}"), 6316 } 6317 match eval("/hello\\d+/.toString()").unwrap() { 6318 Value::String(s) => assert_eq!(s, "/hello\\d+/"), 6319 v => panic!("expected '/hello\\d+/', got {v:?}"), 6320 } 6321 } 6322 6323 #[test] 6324 fn test_regexp_literal() { 6325 match eval("/abc/.test('abc')").unwrap() { 6326 Value::Boolean(b) => assert!(b), 6327 v => panic!("expected true, got {v:?}"), 6328 } 6329 match eval("/abc/.test('xyz')").unwrap() { 6330 Value::Boolean(b) => assert!(!b), 6331 v => panic!("expected false, got {v:?}"), 6332 } 6333 match eval("/\\d+/.test('123')").unwrap() { 6334 Value::Boolean(b) => assert!(b), 6335 v => panic!("expected true, got {v:?}"), 6336 } 6337 match eval("/abc/i.test('ABC')").unwrap() { 6338 Value::Boolean(b) => assert!(b), 6339 v => panic!("expected true, got {v:?}"), 6340 } 6341 } 6342 6343 #[test] 6344 fn test_regexp_literal_exec() { 6345 match eval("var m = /([a-z]+)(\\d+)/.exec('abc123'); m[0]").unwrap() { 6346 Value::String(s) => assert_eq!(s, "abc123"), 6347 v => panic!("expected 'abc123', got {v:?}"), 6348 } 6349 match eval("var m = /([a-z]+)(\\d+)/.exec('abc123'); m[1]").unwrap() { 6350 Value::String(s) => assert_eq!(s, "abc"), 6351 v => panic!("expected 'abc', got {v:?}"), 6352 } 6353 match eval("var m = /([a-z]+)(\\d+)/.exec('abc123'); m[2]").unwrap() { 6354 Value::String(s) => assert_eq!(s, "123"), 6355 v => panic!("expected '123', got {v:?}"), 6356 } 6357 } 6358 6359 #[test] 6360 fn test_string_match_regexp() { 6361 match eval("'hello world'.match(/world/)[0]").unwrap() { 6362 Value::String(s) => assert_eq!(s, "world"), 6363 v => panic!("expected 'world', got {v:?}"), 6364 } 6365 match eval("'aaa'.match(/a/g).length").unwrap() { 6366 Value::Number(n) => assert_eq!(n, 3.0), 6367 v => panic!("expected 3, got {v:?}"), 6368 } 6369 match eval("'abc'.match(/xyz/)").unwrap() { 6370 Value::Null => {} 6371 v => panic!("expected null, got {v:?}"), 6372 } 6373 } 6374 6375 #[test] 6376 fn test_string_search_regexp() { 6377 match eval("'hello world'.search(/world/)").unwrap() { 6378 Value::Number(n) => assert_eq!(n, 6.0), 6379 v => panic!("expected 6, got {v:?}"), 6380 } 6381 match eval("'abc'.search(/xyz/)").unwrap() { 6382 Value::Number(n) => assert_eq!(n, -1.0), 6383 v => panic!("expected -1, got {v:?}"), 6384 } 6385 match eval("'abc123'.search(/\\d/)").unwrap() { 6386 Value::Number(n) => assert_eq!(n, 3.0), 6387 v => panic!("expected 3, got {v:?}"), 6388 } 6389 } 6390 6391 #[test] 6392 fn test_string_replace_regexp() { 6393 match eval("'hello world'.replace(/world/, 'rust')").unwrap() { 6394 Value::String(s) => assert_eq!(s, "hello rust"), 6395 v => panic!("expected 'hello rust', got {v:?}"), 6396 } 6397 match eval("'aaa'.replace(/a/, 'b')").unwrap() { 6398 Value::String(s) => assert_eq!(s, "baa"), 6399 v => panic!("expected 'baa', got {v:?}"), 6400 } 6401 match eval("'aaa'.replace(/a/g, 'b')").unwrap() { 6402 Value::String(s) => assert_eq!(s, "bbb"), 6403 v => panic!("expected 'bbb', got {v:?}"), 6404 } 6405 } 6406 6407 #[test] 6408 fn test_string_replace_capture_groups() { 6409 let src = r#"'John Smith'.replace(/(\w+) (\w+)/, '$2, $1')"#; 6410 match eval(src).unwrap() { 6411 Value::String(s) => assert_eq!(s, "Smith, John"), 6412 v => panic!("expected 'Smith, John', got {v:?}"), 6413 } 6414 match eval("'abc'.replace(/(b)/, '[$1]')").unwrap() { 6415 Value::String(s) => assert_eq!(s, "a[b]c"), 6416 v => panic!("expected 'a[b]c', got {v:?}"), 6417 } 6418 } 6419 6420 #[test] 6421 fn test_string_split_regexp() { 6422 match eval("'a1b2c3'.split(/\\d/).length").unwrap() { 6423 Value::Number(n) => assert_eq!(n, 4.0), 6424 v => panic!("expected 4, got {v:?}"), 6425 } 6426 match eval("'a1b2c3'.split(/\\d/)[0]").unwrap() { 6427 Value::String(s) => assert_eq!(s, "a"), 6428 v => panic!("expected 'a', got {v:?}"), 6429 } 6430 } 6431 6432 #[test] 6433 fn test_regexp_ignore_case() { 6434 match eval("/abc/i.exec('XAbCx')[0]").unwrap() { 6435 Value::String(s) => assert_eq!(s, "AbC"), 6436 v => panic!("expected 'AbC', got {v:?}"), 6437 } 6438 } 6439 6440 #[test] 6441 fn test_regexp_multiline() { 6442 match eval("/^b/m.test('a\\nb')").unwrap() { 6443 Value::Boolean(b) => assert!(b), 6444 v => panic!("expected true, got {v:?}"), 6445 } 6446 match eval("/^b/.test('a\\nb')").unwrap() { 6447 Value::Boolean(b) => assert!(!b), 6448 v => panic!("expected false, got {v:?}"), 6449 } 6450 } 6451 6452 #[test] 6453 fn test_regexp_dot_all() { 6454 match eval("/a.b/s.test('a\\nb')").unwrap() { 6455 Value::Boolean(b) => assert!(b), 6456 v => panic!("expected true, got {v:?}"), 6457 } 6458 match eval("/a.b/.test('a\\nb')").unwrap() { 6459 Value::Boolean(b) => assert!(!b), 6460 v => panic!("expected false, got {v:?}"), 6461 } 6462 } 6463 6464 #[test] 6465 fn test_regexp_word_boundary() { 6466 match eval("/\\bfoo\\b/.test('a foo b')").unwrap() { 6467 Value::Boolean(b) => assert!(b), 6468 v => panic!("expected true, got {v:?}"), 6469 } 6470 match eval("/\\bfoo\\b/.test('foobar')").unwrap() { 6471 Value::Boolean(b) => assert!(!b), 6472 v => panic!("expected false, got {v:?}"), 6473 } 6474 } 6475 6476 #[test] 6477 fn test_regexp_quantifiers_vm() { 6478 match eval("/a{3}/.test('aaa')").unwrap() { 6479 Value::Boolean(b) => assert!(b), 6480 v => panic!("expected true, got {v:?}"), 6481 } 6482 match eval("/a{3}/.test('aa')").unwrap() { 6483 Value::Boolean(b) => assert!(!b), 6484 v => panic!("expected false, got {v:?}"), 6485 } 6486 match eval("/a+?/.exec('aaa')[0]").unwrap() { 6487 Value::String(s) => assert_eq!(s, "a"), 6488 v => panic!("expected 'a', got {v:?}"), 6489 } 6490 } 6491 6492 #[test] 6493 fn test_regexp_alternation_vm() { 6494 match eval("/cat|dog/.exec('I have a dog')[0]").unwrap() { 6495 Value::String(s) => assert_eq!(s, "dog"), 6496 v => panic!("expected 'dog', got {v:?}"), 6497 } 6498 } 6499 6500 #[test] 6501 fn test_regexp_lookahead_vm() { 6502 match eval("/a(?=b)/.test('ab')").unwrap() { 6503 Value::Boolean(b) => assert!(b), 6504 v => panic!("expected true, got {v:?}"), 6505 } 6506 match eval("/a(?=b)/.test('ac')").unwrap() { 6507 Value::Boolean(b) => assert!(!b), 6508 v => panic!("expected false, got {v:?}"), 6509 } 6510 match eval("/a(?!b)/.test('ac')").unwrap() { 6511 Value::Boolean(b) => assert!(b), 6512 v => panic!("expected true, got {v:?}"), 6513 } 6514 } 6515 6516 #[test] 6517 fn test_regexp_char_class_vm() { 6518 match eval("/[abc]/.test('b')").unwrap() { 6519 Value::Boolean(b) => assert!(b), 6520 v => panic!("expected true, got {v:?}"), 6521 } 6522 match eval("/[a-z]+/.exec('Hello')[0]").unwrap() { 6523 Value::String(s) => assert_eq!(s, "ello"), 6524 v => panic!("expected 'ello', got {v:?}"), 6525 } 6526 } 6527 6528 #[test] 6529 fn test_regexp_backreference_vm() { 6530 match eval("/(a)\\1/.test('aa')").unwrap() { 6531 Value::Boolean(b) => assert!(b), 6532 v => panic!("expected true, got {v:?}"), 6533 } 6534 match eval("/(a)\\1/.test('ab')").unwrap() { 6535 Value::Boolean(b) => assert!(!b), 6536 v => panic!("expected false, got {v:?}"), 6537 } 6538 } 6539 6540 #[test] 6541 fn test_regexp_properties() { 6542 match eval("var r = /abc/gim; r.global").unwrap() { 6543 Value::Boolean(b) => assert!(b), 6544 v => panic!("expected true, got {v:?}"), 6545 } 6546 match eval("/abc/.lastIndex").unwrap() { 6547 Value::Number(n) => assert_eq!(n, 0.0), 6548 v => panic!("expected 0, got {v:?}"), 6549 } 6550 } 6551 6552 #[test] 6553 fn test_string_replace_all_regexp() { 6554 match eval("'aba'.replaceAll(/a/g, 'x')").unwrap() { 6555 Value::String(s) => assert_eq!(s, "xbx"), 6556 v => panic!("expected 'xbx', got {v:?}"), 6557 } 6558 } 6559 6560 // ── Map tests ───────────────────────────────────────────── 6561 6562 #[test] 6563 fn test_map_basic() { 6564 match eval("var m = new Map(); m.set('a', 1); m.get('a')").unwrap() { 6565 Value::Number(n) => assert_eq!(n, 1.0), 6566 v => panic!("expected 1, got {v:?}"), 6567 } 6568 } 6569 6570 #[test] 6571 fn test_map_size() { 6572 match eval("var m = new Map(); m.set('a', 1); m.set('b', 2); m.size").unwrap() { 6573 Value::Number(n) => assert_eq!(n, 2.0), 6574 v => panic!("expected 2, got {v:?}"), 6575 } 6576 } 6577 6578 #[test] 6579 fn test_map_has() { 6580 match eval("var m = new Map(); m.set('x', 10); m.has('x')").unwrap() { 6581 Value::Boolean(b) => assert!(b), 6582 v => panic!("expected true, got {v:?}"), 6583 } 6584 match eval("var m = new Map(); m.has('x')").unwrap() { 6585 Value::Boolean(b) => assert!(!b), 6586 v => panic!("expected false, got {v:?}"), 6587 } 6588 } 6589 6590 #[test] 6591 fn test_map_delete() { 6592 match eval("var m = new Map(); m.set('a', 1); m['delete']('a'); m.has('a')").unwrap() { 6593 Value::Boolean(b) => assert!(!b), 6594 v => panic!("expected false, got {v:?}"), 6595 } 6596 match eval("var m = new Map(); m.set('a', 1); m['delete']('a'); m.size").unwrap() { 6597 Value::Number(n) => assert_eq!(n, 0.0), 6598 v => panic!("expected 0, got {v:?}"), 6599 } 6600 } 6601 6602 #[test] 6603 fn test_map_clear() { 6604 match eval("var m = new Map(); m.set('a', 1); m.set('b', 2); m.clear(); m.size").unwrap() { 6605 Value::Number(n) => assert_eq!(n, 0.0), 6606 v => panic!("expected 0, got {v:?}"), 6607 } 6608 } 6609 6610 #[test] 6611 fn test_map_overwrite() { 6612 match eval("var m = new Map(); m.set('a', 1); m.set('a', 2); m.get('a')").unwrap() { 6613 Value::Number(n) => assert_eq!(n, 2.0), 6614 v => panic!("expected 2, got {v:?}"), 6615 } 6616 // Size should still be 1 after overwriting. 6617 match eval("var m = new Map(); m.set('a', 1); m.set('a', 2); m.size").unwrap() { 6618 Value::Number(n) => assert_eq!(n, 1.0), 6619 v => panic!("expected 1, got {v:?}"), 6620 } 6621 } 6622 6623 #[test] 6624 fn test_map_get_missing() { 6625 match eval("var m = new Map(); m.get('missing')").unwrap() { 6626 Value::Undefined => {} 6627 v => panic!("expected undefined, got {v:?}"), 6628 } 6629 } 6630 6631 #[test] 6632 fn test_map_chaining() { 6633 // set() returns the Map for chaining. 6634 match eval("var m = new Map(); m.set('a', 1).set('b', 2); m.size").unwrap() { 6635 Value::Number(n) => assert_eq!(n, 2.0), 6636 v => panic!("expected 2, got {v:?}"), 6637 } 6638 } 6639 6640 #[test] 6641 fn test_map_nan_key() { 6642 // NaN === NaN for Map keys (SameValueZero). 6643 match eval("var m = new Map(); m.set(NaN, 'nan'); m.get(NaN)").unwrap() { 6644 Value::String(s) => assert_eq!(s, "nan"), 6645 v => panic!("expected 'nan', got {v:?}"), 6646 } 6647 } 6648 6649 #[test] 6650 fn test_map_object_key() { 6651 match eval("var m = new Map(); var o = {}; m.set(o, 'val'); m.get(o)").unwrap() { 6652 Value::String(s) => assert_eq!(s, "val"), 6653 v => panic!("expected 'val', got {v:?}"), 6654 } 6655 } 6656 6657 #[test] 6658 fn test_map_constructor_with_pairs() { 6659 match eval("var m = new Map([['a', 1], ['b', 2]]); m.get('b')").unwrap() { 6660 Value::Number(n) => assert_eq!(n, 2.0), 6661 v => panic!("expected 2, got {v:?}"), 6662 } 6663 } 6664 6665 #[test] 6666 fn test_map_keys_values_entries() { 6667 match eval("var m = new Map(); m.set('a', 1); m.set('b', 2); m.keys().length").unwrap() { 6668 Value::Number(n) => assert_eq!(n, 2.0), 6669 v => panic!("expected 2, got {v:?}"), 6670 } 6671 match eval("var m = new Map(); m.set('a', 1); m.set('b', 2); m.values()[1]").unwrap() { 6672 Value::Number(n) => assert_eq!(n, 2.0), 6673 v => panic!("expected 2, got {v:?}"), 6674 } 6675 match eval("var m = new Map(); m.set('a', 1); m.entries()[0][0]").unwrap() { 6676 Value::String(s) => assert_eq!(s, "a"), 6677 v => panic!("expected 'a', got {v:?}"), 6678 } 6679 } 6680 6681 #[test] 6682 fn test_map_insertion_order() { 6683 match eval("var m = new Map(); m.set('c', 3); m.set('a', 1); m.set('b', 2); m.keys()[0]") 6684 .unwrap() 6685 { 6686 Value::String(s) => assert_eq!(s, "c"), 6687 v => panic!("expected 'c', got {v:?}"), 6688 } 6689 } 6690 6691 // ── Set tests ───────────────────────────────────────────── 6692 6693 #[test] 6694 fn test_set_basic() { 6695 match eval("var s = new Set(); s.add(1); s.add(2); s.size").unwrap() { 6696 Value::Number(n) => assert_eq!(n, 2.0), 6697 v => panic!("expected 2, got {v:?}"), 6698 } 6699 } 6700 6701 #[test] 6702 fn test_set_has() { 6703 match eval("var s = new Set(); s.add(42); s.has(42)").unwrap() { 6704 Value::Boolean(b) => assert!(b), 6705 v => panic!("expected true, got {v:?}"), 6706 } 6707 match eval("var s = new Set(); s.has(42)").unwrap() { 6708 Value::Boolean(b) => assert!(!b), 6709 v => panic!("expected false, got {v:?}"), 6710 } 6711 } 6712 6713 #[test] 6714 fn test_set_delete() { 6715 match eval("var s = new Set(); s.add(1); s['delete'](1); s.has(1)").unwrap() { 6716 Value::Boolean(b) => assert!(!b), 6717 v => panic!("expected false, got {v:?}"), 6718 } 6719 match eval("var s = new Set(); s.add(1); s['delete'](1); s.size").unwrap() { 6720 Value::Number(n) => assert_eq!(n, 0.0), 6721 v => panic!("expected 0, got {v:?}"), 6722 } 6723 } 6724 6725 #[test] 6726 fn test_set_clear() { 6727 match eval("var s = new Set(); s.add(1); s.add(2); s.clear(); s.size").unwrap() { 6728 Value::Number(n) => assert_eq!(n, 0.0), 6729 v => panic!("expected 0, got {v:?}"), 6730 } 6731 } 6732 6733 #[test] 6734 fn test_set_uniqueness() { 6735 match eval("var s = new Set(); s.add(1); s.add(1); s.add(1); s.size").unwrap() { 6736 Value::Number(n) => assert_eq!(n, 1.0), 6737 v => panic!("expected 1, got {v:?}"), 6738 } 6739 } 6740 6741 #[test] 6742 fn test_set_chaining() { 6743 match eval("var s = new Set(); s.add(1).add(2).add(3); s.size").unwrap() { 6744 Value::Number(n) => assert_eq!(n, 3.0), 6745 v => panic!("expected 3, got {v:?}"), 6746 } 6747 } 6748 6749 #[test] 6750 fn test_set_nan() { 6751 match eval("var s = new Set(); s.add(NaN); s.add(NaN); s.size").unwrap() { 6752 Value::Number(n) => assert_eq!(n, 1.0), 6753 v => panic!("expected 1, got {v:?}"), 6754 } 6755 match eval("var s = new Set(); s.add(NaN); s.has(NaN)").unwrap() { 6756 Value::Boolean(b) => assert!(b), 6757 v => panic!("expected true, got {v:?}"), 6758 } 6759 } 6760 6761 #[test] 6762 fn test_set_constructor_from_array() { 6763 match eval("var s = new Set([1, 2, 3, 2, 1]); s.size").unwrap() { 6764 Value::Number(n) => assert_eq!(n, 3.0), 6765 v => panic!("expected 3, got {v:?}"), 6766 } 6767 } 6768 6769 #[test] 6770 fn test_set_values() { 6771 match eval("var s = new Set(); s.add('a'); s.add('b'); s.values().length").unwrap() { 6772 Value::Number(n) => assert_eq!(n, 2.0), 6773 v => panic!("expected 2, got {v:?}"), 6774 } 6775 } 6776 6777 #[test] 6778 fn test_set_entries() { 6779 // Set.entries() returns [value, value] pairs. 6780 match eval("var s = new Set(); s.add('x'); s.entries()[0][0]").unwrap() { 6781 Value::String(s) => assert_eq!(s, "x"), 6782 v => panic!("expected 'x', got {v:?}"), 6783 } 6784 match eval("var s = new Set(); s.add('x'); s.entries()[0][1]").unwrap() { 6785 Value::String(s) => assert_eq!(s, "x"), 6786 v => panic!("expected 'x', got {v:?}"), 6787 } 6788 } 6789 6790 #[test] 6791 fn test_set_insertion_order() { 6792 match eval("var s = new Set(); s.add('c'); s.add('a'); s.add('b'); s.values()[0]").unwrap() 6793 { 6794 Value::String(s) => assert_eq!(s, "c"), 6795 v => panic!("expected 'c', got {v:?}"), 6796 } 6797 } 6798 6799 // ── WeakMap tests ───────────────────────────────────────── 6800 6801 #[test] 6802 fn test_weakmap_basic() { 6803 match eval("var wm = new WeakMap(); var o = {}; wm.set(o, 'val'); wm.get(o)").unwrap() { 6804 Value::String(s) => assert_eq!(s, "val"), 6805 v => panic!("expected 'val', got {v:?}"), 6806 } 6807 } 6808 6809 #[test] 6810 fn test_weakmap_has_delete() { 6811 match eval("var wm = new WeakMap(); var o = {}; wm.set(o, 1); wm.has(o)").unwrap() { 6812 Value::Boolean(b) => assert!(b), 6813 v => panic!("expected true, got {v:?}"), 6814 } 6815 match eval("var wm = new WeakMap(); var o = {}; wm.set(o, 1); wm['delete'](o); wm.has(o)") 6816 .unwrap() 6817 { 6818 Value::Boolean(b) => assert!(!b), 6819 v => panic!("expected false, got {v:?}"), 6820 } 6821 } 6822 6823 #[test] 6824 fn test_weakmap_rejects_primitive_key() { 6825 assert!(eval("var wm = new WeakMap(); wm.set('str', 1)").is_err()); 6826 assert!(eval("var wm = new WeakMap(); wm.set(42, 1)").is_err()); 6827 } 6828 6829 // ── WeakSet tests ───────────────────────────────────────── 6830 6831 #[test] 6832 fn test_weakset_basic() { 6833 match eval("var ws = new WeakSet(); var o = {}; ws.add(o); ws.has(o)").unwrap() { 6834 Value::Boolean(b) => assert!(b), 6835 v => panic!("expected true, got {v:?}"), 6836 } 6837 } 6838 6839 #[test] 6840 fn test_weakset_delete() { 6841 match eval("var ws = new WeakSet(); var o = {}; ws.add(o); ws['delete'](o); ws.has(o)") 6842 .unwrap() 6843 { 6844 Value::Boolean(b) => assert!(!b), 6845 v => panic!("expected false, got {v:?}"), 6846 } 6847 } 6848 6849 #[test] 6850 fn test_weakset_rejects_primitive() { 6851 assert!(eval("var ws = new WeakSet(); ws.add('str')").is_err()); 6852 assert!(eval("var ws = new WeakSet(); ws.add(42)").is_err()); 6853 } 6854 6855 // ── Promise tests ──────────────────────────────────────────── 6856 6857 #[test] 6858 fn test_promise_typeof() { 6859 match eval("typeof Promise").unwrap() { 6860 Value::String(s) => assert_eq!(s, "function"), 6861 v => panic!("expected 'function', got {v:?}"), 6862 } 6863 } 6864 6865 #[test] 6866 fn test_promise_static_resolve_exists() { 6867 match eval("typeof Promise.resolve").unwrap() { 6868 Value::String(s) => assert_eq!(s, "function"), 6869 v => panic!("expected 'function', got {v:?}"), 6870 } 6871 } 6872 6873 #[test] 6874 fn test_promise_resolve_returns_object() { 6875 match eval("typeof Promise.resolve(42)").unwrap() { 6876 Value::String(s) => assert_eq!(s, "object"), 6877 v => panic!("expected 'object', got {v:?}"), 6878 } 6879 } 6880 6881 #[test] 6882 fn test_promise_resolve_then_exists() { 6883 match eval("typeof Promise.resolve(42).then").unwrap() { 6884 Value::String(s) => assert_eq!(s, "function"), 6885 v => panic!("expected 'function', got {v:?}"), 6886 } 6887 } 6888 6889 /// Helper: eval JS, then read an undeclared global set by callbacks. 6890 /// NOTE: Variables set inside closures MUST NOT be declared with `var` 6891 /// in the same scope that creates the closure, because the compiler 6892 /// would put them in Cells instead of globals. 6893 fn eval_global(source: &str, name: &str) -> Result<Value, RuntimeError> { 6894 let program = Parser::parse(source).expect("parse failed"); 6895 let func = compiler::compile(&program).expect("compile failed"); 6896 let mut vm = Vm::new(); 6897 vm.execute(&func)?; 6898 Ok(vm.globals.get(name).cloned().unwrap_or(Value::Undefined)) 6899 } 6900 6901 #[test] 6902 fn test_promise_resolve_then() { 6903 // Don't use `var result` — it would be captured. Use implicit global. 6904 match eval_global( 6905 "Promise.resolve(42).then(function(v) { result = v; });", 6906 "result", 6907 ) 6908 .unwrap() 6909 { 6910 Value::Number(n) => assert_eq!(n, 42.0), 6911 v => panic!("expected 42, got {v:?}"), 6912 } 6913 } 6914 6915 #[test] 6916 fn test_promise_reject_catch() { 6917 // Use bracket notation for catch (it's a keyword in the parser). 6918 match eval_global( 6919 "Promise.reject('err')['catch'](function(e) { result = e; });", 6920 "result", 6921 ) 6922 .unwrap() 6923 { 6924 Value::String(s) => assert_eq!(s, "err"), 6925 v => panic!("expected 'err', got {v:?}"), 6926 } 6927 } 6928 6929 #[test] 6930 fn test_promise_constructor_resolve() { 6931 match eval_global( 6932 "var p = Promise(function(resolve) { resolve(10); }); p.then(function(v) { result = v; });", 6933 "result", 6934 ) 6935 .unwrap() 6936 { 6937 Value::Number(n) => assert_eq!(n, 10.0), 6938 v => panic!("expected 10, got {v:?}"), 6939 } 6940 } 6941 6942 #[test] 6943 fn test_promise_constructor_reject() { 6944 match eval_global( 6945 "var p = Promise(function(resolve, reject) { reject('fail'); }); p['catch'](function(e) { result = e; });", 6946 "result", 6947 ) 6948 .unwrap() 6949 { 6950 Value::String(s) => assert_eq!(s, "fail"), 6951 v => panic!("expected 'fail', got {v:?}"), 6952 } 6953 } 6954 6955 #[test] 6956 fn test_promise_executor_runs_synchronously() { 6957 // Executor runs synchronously. Use eval to check completion value. 6958 match eval("var x = 'before'; Promise(function(resolve) { x = 'during'; resolve(); }); x") 6959 .unwrap() 6960 { 6961 Value::String(s) => assert_eq!(s, "during"), 6962 v => panic!("expected 'during', got {v:?}"), 6963 } 6964 } 6965 6966 #[test] 6967 fn test_promise_then_chaining() { 6968 match eval_global( 6969 "Promise.resolve(1).then(function(v) { return v + 1; }).then(function(v) { result = v; });", 6970 "result", 6971 ) 6972 .unwrap() 6973 { 6974 Value::Number(n) => assert_eq!(n, 2.0), 6975 v => panic!("expected 2, got {v:?}"), 6976 } 6977 } 6978 6979 #[test] 6980 fn test_promise_catch_returns_to_then() { 6981 match eval_global( 6982 "Promise.reject('err')['catch'](function(e) { return 'recovered'; }).then(function(v) { result = v; });", 6983 "result", 6984 ) 6985 .unwrap() 6986 { 6987 Value::String(s) => assert_eq!(s, "recovered"), 6988 v => panic!("expected 'recovered', got {v:?}"), 6989 } 6990 } 6991 6992 #[test] 6993 fn test_promise_then_error_goes_to_catch() { 6994 // When a then handler throws, the rejection reason is the thrown value 6995 // wrapped in an Error object by the VM. Check it's an object. 6996 match eval_global( 6997 "Promise.resolve(1).then(function(v) { throw 'oops'; })['catch'](function(e) { result = typeof e; });", 6998 "result", 6999 ) 7000 .unwrap() 7001 { 7002 // The thrown string gets wrapped in an error object by RuntimeError::to_value. 7003 Value::String(s) => assert!( 7004 s == "object" || s == "string", 7005 "expected 'object' or 'string', got '{s}'" 7006 ), 7007 v => panic!("expected string type, got {v:?}"), 7008 } 7009 } 7010 7011 #[test] 7012 fn test_promise_resolve_with_promise() { 7013 match eval_global( 7014 "var p = Promise.resolve(99); p.then(function(v) { result = v; });", 7015 "result", 7016 ) 7017 .unwrap() 7018 { 7019 Value::Number(n) => assert_eq!(n, 99.0), 7020 v => panic!("expected 99, got {v:?}"), 7021 } 7022 } 7023 7024 #[test] 7025 fn test_promise_multiple_then() { 7026 match eval_global( 7027 "var p = Promise.resolve(5); p.then(function(v) { a = v; }); p.then(function(v) { b = v * 2; });", 7028 "a", 7029 ) 7030 .unwrap() 7031 { 7032 Value::Number(n) => assert_eq!(n, 5.0), 7033 v => panic!("expected 5, got {v:?}"), 7034 } 7035 } 7036 7037 #[test] 7038 fn test_promise_double_resolve_ignored() { 7039 match eval_global( 7040 "var p = Promise(function(resolve) { resolve(1); resolve(2); }); p.then(function(v) { result = v; });", 7041 "result", 7042 ) 7043 .unwrap() 7044 { 7045 Value::Number(n) => assert_eq!(n, 1.0), 7046 v => panic!("expected 1, got {v:?}"), 7047 } 7048 } 7049 7050 #[test] 7051 fn test_promise_executor_throw_rejects() { 7052 match eval_global( 7053 "var p = Promise(function() { throw 'boom'; }); p['catch'](function(e) { result = e; });", 7054 "result", 7055 ) 7056 .unwrap() 7057 { 7058 Value::String(s) => assert_eq!(s, "boom"), 7059 v => panic!("expected 'boom', got {v:?}"), 7060 } 7061 } 7062 7063 #[test] 7064 fn test_promise_finally_fulfilled() { 7065 match eval_global( 7066 "Promise.resolve(42)['finally'](function() { result = 'done'; });", 7067 "result", 7068 ) 7069 .unwrap() 7070 { 7071 Value::String(s) => assert_eq!(s, "done"), 7072 v => panic!("expected 'done', got {v:?}"), 7073 } 7074 } 7075 7076 #[test] 7077 fn test_promise_finally_rejected() { 7078 match eval_global( 7079 "Promise.reject('err')['finally'](function() { result = 'done'; });", 7080 "result", 7081 ) 7082 .unwrap() 7083 { 7084 Value::String(s) => assert_eq!(s, "done"), 7085 v => panic!("expected 'done', got {v:?}"), 7086 } 7087 } 7088 7089 #[test] 7090 fn test_promise_all_empty() { 7091 match eval_global( 7092 "Promise.all([]).then(function(v) { result = v.length; });", 7093 "result", 7094 ) 7095 .unwrap() 7096 { 7097 Value::Number(n) => assert_eq!(n, 0.0), 7098 v => panic!("expected 0, got {v:?}"), 7099 } 7100 } 7101 7102 #[test] 7103 fn test_promise_all_resolved() { 7104 match eval_global( 7105 "Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]).then(function(v) { result = v[0] + v[1] + v[2]; });", 7106 "result", 7107 ) 7108 .unwrap() 7109 { 7110 Value::Number(n) => assert_eq!(n, 6.0), 7111 v => panic!("expected 6, got {v:?}"), 7112 } 7113 } 7114 7115 #[test] 7116 fn test_promise_all_rejects_on_first() { 7117 match eval_global( 7118 "Promise.all([Promise.resolve(1), Promise.reject('fail')])['catch'](function(e) { result = e; });", 7119 "result", 7120 ) 7121 .unwrap() 7122 { 7123 Value::String(s) => assert_eq!(s, "fail"), 7124 v => panic!("expected 'fail', got {v:?}"), 7125 } 7126 } 7127 7128 #[test] 7129 fn test_promise_race_first_wins() { 7130 match eval_global( 7131 "Promise.race([Promise.resolve('first'), Promise.resolve('second')]).then(function(v) { result = v; });", 7132 "result", 7133 ) 7134 .unwrap() 7135 { 7136 Value::String(s) => assert_eq!(s, "first"), 7137 v => panic!("expected 'first', got {v:?}"), 7138 } 7139 } 7140 7141 #[test] 7142 fn test_promise_race_reject_wins() { 7143 match eval_global( 7144 "Promise.race([Promise.reject('err')])['catch'](function(e) { result = e; });", 7145 "result", 7146 ) 7147 .unwrap() 7148 { 7149 Value::String(s) => assert_eq!(s, "err"), 7150 v => panic!("expected 'err', got {v:?}"), 7151 } 7152 } 7153 7154 #[test] 7155 fn test_promise_any_first_fulfilled() { 7156 match eval_global( 7157 "Promise.any([Promise.reject('a'), Promise.resolve('b')]).then(function(v) { result = v; });", 7158 "result", 7159 ) 7160 .unwrap() 7161 { 7162 Value::String(s) => assert_eq!(s, "b"), 7163 v => panic!("expected 'b', got {v:?}"), 7164 } 7165 } 7166 7167 #[test] 7168 fn test_promise_any_all_rejected() { 7169 match eval_global( 7170 "Promise.any([Promise.reject('a'), Promise.reject('b')])['catch'](function(e) { result = e; });", 7171 "result", 7172 ) 7173 .unwrap() 7174 { 7175 Value::String(ref s) if s.contains("rejected") => {} 7176 v => panic!("expected AggregateError string, got {v:?}"), 7177 } 7178 } 7179 7180 #[test] 7181 fn test_promise_all_with_non_promises() { 7182 match eval_global( 7183 "Promise.all([1, 2, 3]).then(function(v) { result = v[0] + v[1] + v[2]; });", 7184 "result", 7185 ) 7186 .unwrap() 7187 { 7188 Value::Number(n) => assert_eq!(n, 6.0), 7189 v => panic!("expected 6, got {v:?}"), 7190 } 7191 } 7192 7193 #[test] 7194 fn test_promise_race_with_non_promise() { 7195 match eval_global( 7196 "Promise.race([42]).then(function(v) { result = v; });", 7197 "result", 7198 ) 7199 .unwrap() 7200 { 7201 Value::Number(n) => assert_eq!(n, 42.0), 7202 v => panic!("expected 42, got {v:?}"), 7203 } 7204 } 7205 7206 #[test] 7207 fn test_promise_then_identity_passthrough() { 7208 match eval_global( 7209 "Promise.resolve(7).then().then(function(v) { result = v; });", 7210 "result", 7211 ) 7212 .unwrap() 7213 { 7214 Value::Number(n) => assert_eq!(n, 7.0), 7215 v => panic!("expected 7, got {v:?}"), 7216 } 7217 } 7218 7219 #[test] 7220 fn test_promise_catch_passthrough() { 7221 match eval_global( 7222 "Promise.resolve(99)['catch'](function(e) { result = 'bad'; }).then(function(v) { result = v; });", 7223 "result", 7224 ) 7225 .unwrap() 7226 { 7227 Value::Number(n) => assert_eq!(n, 99.0), 7228 v => panic!("expected 99, got {v:?}"), 7229 } 7230 } 7231 7232 #[test] 7233 fn test_promise_all_settled() { 7234 match eval_global( 7235 "Promise.allSettled([Promise.resolve(1), Promise.reject('err')]).then(function(v) { result = v[0].status + ',' + v[1].status; });", 7236 "result", 7237 ) 7238 .unwrap() 7239 { 7240 Value::String(s) => assert_eq!(s, "fulfilled,rejected"), 7241 v => panic!("expected 'fulfilled,rejected', got {v:?}"), 7242 } 7243 } 7244 7245 // ── Iterator and for...of tests ──────────────────────── 7246 7247 #[test] 7248 fn test_for_of_array() { 7249 match eval( 7250 "var result = ''; var arr = [10, 20, 30]; for (var x of arr) { result = result + x + ','; } result", 7251 ) 7252 .unwrap() 7253 { 7254 Value::String(s) => assert_eq!(s, "10,20,30,"), 7255 v => panic!("expected '10,20,30,', got {v:?}"), 7256 } 7257 } 7258 7259 #[test] 7260 fn test_for_of_string() { 7261 match eval("var result = ''; for (var ch of 'abc') { result = result + ch; } result") 7262 .unwrap() 7263 { 7264 Value::String(s) => assert_eq!(s, "abc"), 7265 v => panic!("expected 'abc', got {v:?}"), 7266 } 7267 } 7268 7269 #[test] 7270 fn test_for_of_with_break() { 7271 match eval( 7272 "var result = 0; for (var x of [1, 2, 3, 4, 5]) { if (x === 3) break; result = result + x; } result", 7273 ) 7274 .unwrap() 7275 { 7276 Value::Number(n) => assert_eq!(n, 3.0), 7277 v => panic!("expected 3, got {v:?}"), 7278 } 7279 } 7280 7281 #[test] 7282 fn test_for_of_with_continue() { 7283 match eval( 7284 "var result = 0; for (var x of [1, 2, 3, 4, 5]) { if (x === 3) continue; result = result + x; } result", 7285 ) 7286 .unwrap() 7287 { 7288 Value::Number(n) => assert_eq!(n, 12.0), 7289 v => panic!("expected 12, got {v:?}"), 7290 } 7291 } 7292 7293 // ── Generator tests ──────────────────────────────────── 7294 7295 #[test] 7296 fn test_generator_typeof() { 7297 // First test: does gen() return an object? 7298 match eval("function* gen() { yield 1; } typeof gen()").unwrap() { 7299 Value::String(s) => assert_eq!(s, "object"), 7300 v => panic!("expected 'object', got {v:?}"), 7301 } 7302 } 7303 7304 #[test] 7305 fn test_generator_has_next() { 7306 // Test: does the generator have a next method? 7307 match eval("function* gen() { yield 1; } var g = gen(); typeof g.next").unwrap() { 7308 Value::String(s) => assert_eq!(s, "function"), 7309 v => panic!("expected 'function', got {v:?}"), 7310 } 7311 } 7312 7313 #[test] 7314 fn test_basic_generator() { 7315 match eval( 7316 "function* gen() { yield 1; yield 2; yield 3; } 7317 var g = gen(); 7318 var a = g.next(); 7319 a.value", 7320 ) 7321 .unwrap() 7322 { 7323 Value::Number(n) => assert_eq!(n, 1.0), 7324 v => panic!("expected 1, got {v:?}"), 7325 } 7326 } 7327 7328 #[test] 7329 fn test_generator_multiple_yields() { 7330 match eval( 7331 "function* gen() { yield 10; yield 20; yield 30; } 7332 var g = gen(); 7333 var r1 = g.next(); var r2 = g.next(); var r3 = g.next(); var r4 = g.next(); 7334 '' + r1.value + ',' + r2.value + ',' + r3.value + ',' + r4.done", 7335 ) 7336 .unwrap() 7337 { 7338 Value::String(s) => assert_eq!(s, "10,20,30,true"), 7339 v => panic!("expected '10,20,30,true', got {v:?}"), 7340 } 7341 } 7342 7343 #[test] 7344 fn test_generator_send_value() { 7345 match eval( 7346 "function* gen() { var x = yield 'hello'; yield x + ' world'; } 7347 var g = gen(); 7348 g.next(); 7349 var r = g.next('beautiful'); 7350 r.value", 7351 ) 7352 .unwrap() 7353 { 7354 Value::String(s) => assert_eq!(s, "beautiful world"), 7355 v => panic!("expected 'beautiful world', got {v:?}"), 7356 } 7357 } 7358 7359 #[test] 7360 fn test_generator_return() { 7361 match eval( 7362 "function* gen() { yield 1; yield 2; yield 3; } 7363 var g = gen(); 7364 g.next(); 7365 var r = g['return'](42); 7366 '' + r.value + ',' + r.done", 7367 ) 7368 .unwrap() 7369 { 7370 Value::String(s) => assert_eq!(s, "42,true"), 7371 v => panic!("expected '42,true', got {v:?}"), 7372 } 7373 } 7374 7375 #[test] 7376 fn test_generator_in_for_of() { 7377 match eval( 7378 "function* range(start, end) { 7379 for (var i = start; i < end; i = i + 1) { yield i; } 7380 } 7381 var result = 0; 7382 for (var n of range(1, 5)) { result = result + n; } 7383 result", 7384 ) 7385 .unwrap() 7386 { 7387 Value::Number(n) => assert_eq!(n, 10.0), 7388 v => panic!("expected 10, got {v:?}"), 7389 } 7390 } 7391 7392 #[test] 7393 fn test_generator_with_return_value() { 7394 match eval( 7395 "function* gen() { yield 1; return 'done'; } 7396 var g = gen(); 7397 var a = g.next(); 7398 var b = g.next(); 7399 '' + a.value + ',' + a.done + ',' + b.value + ',' + b.done", 7400 ) 7401 .unwrap() 7402 { 7403 Value::String(s) => assert_eq!(s, "1,false,done,true"), 7404 v => panic!("expected '1,false,done,true', got {v:?}"), 7405 } 7406 } 7407 7408 // ── Destructuring tests ──────────────────────────────── 7409 7410 #[test] 7411 fn test_array_destructuring_basic() { 7412 match eval("var [a, b, c] = [1, 2, 3]; a + b + c").unwrap() { 7413 Value::Number(n) => assert_eq!(n, 6.0), 7414 v => panic!("expected 6, got {v:?}"), 7415 } 7416 } 7417 7418 #[test] 7419 fn test_array_destructuring_rest() { 7420 match eval("var [first, ...rest] = [1, 2, 3, 4]; '' + first + ',' + rest.length").unwrap() { 7421 Value::String(s) => assert_eq!(s, "1,3"), 7422 v => panic!("expected '1,3', got {v:?}"), 7423 } 7424 } 7425 7426 #[test] 7427 fn test_array_destructuring_default() { 7428 match eval("var [a = 10, b = 20] = [1]; '' + a + ',' + b").unwrap() { 7429 Value::String(s) => assert_eq!(s, "1,20"), 7430 v => panic!("expected '1,20', got {v:?}"), 7431 } 7432 } 7433 7434 #[test] 7435 fn test_object_destructuring_basic() { 7436 match eval("var {x, y} = {x: 1, y: 2}; x + y").unwrap() { 7437 Value::Number(n) => assert_eq!(n, 3.0), 7438 v => panic!("expected 3, got {v:?}"), 7439 } 7440 } 7441 7442 #[test] 7443 fn test_object_destructuring_alias() { 7444 match eval("var {x: a, y: b} = {x: 10, y: 20}; a + b").unwrap() { 7445 Value::Number(n) => assert_eq!(n, 30.0), 7446 v => panic!("expected 30, got {v:?}"), 7447 } 7448 } 7449 7450 #[test] 7451 fn test_nested_destructuring() { 7452 match eval("var {a: {b}} = {a: {b: 42}}; b").unwrap() { 7453 Value::Number(n) => assert_eq!(n, 42.0), 7454 v => panic!("expected 42, got {v:?}"), 7455 } 7456 } 7457 7458 #[test] 7459 fn test_destructuring_in_for_of() { 7460 match eval( 7461 "var result = 0; var pairs = [[1, 2], [3, 4], [5, 6]]; for (var [a, b] of pairs) { result = result + a + b; } result", 7462 ) 7463 .unwrap() 7464 { 7465 Value::Number(n) => assert_eq!(n, 21.0), 7466 v => panic!("expected 21, got {v:?}"), 7467 } 7468 } 7469 7470 // ── Spread tests ─────────────────────────────────────── 7471 7472 #[test] 7473 fn test_spread_in_array() { 7474 match eval("var a = [1, 2, 3]; var b = [0, ...a, 4]; b.length").unwrap() { 7475 Value::Number(n) => assert_eq!(n, 5.0), 7476 v => panic!("expected 5, got {v:?}"), 7477 } 7478 } 7479 7480 #[test] 7481 fn test_spread_in_array_values() { 7482 match eval( 7483 "var a = [1, 2, 3]; var b = [0, ...a, 4]; '' + b[0] + ',' + b[1] + ',' + b[2] + ',' + b[3] + ',' + b[4]", 7484 ) 7485 .unwrap() 7486 { 7487 Value::String(s) => assert_eq!(s, "0,1,2,3,4"), 7488 v => panic!("expected '0,1,2,3,4', got {v:?}"), 7489 } 7490 } 7491 7492 // ── Custom iterable tests ────────────────────────────── 7493 7494 #[test] 7495 fn test_custom_iterable() { 7496 match eval( 7497 "var obj = {}; 7498 obj['@@iterator'] = function() { 7499 var i = 0; 7500 return { 7501 next: function() { 7502 i = i + 1; 7503 if (i <= 3) return {value: i, done: false}; 7504 return {value: undefined, done: true}; 7505 } 7506 }; 7507 }; 7508 var result = 0; 7509 for (var v of obj) { result = result + v; } 7510 result", 7511 ) 7512 .unwrap() 7513 { 7514 Value::Number(n) => assert_eq!(n, 6.0), 7515 v => panic!("expected 6, got {v:?}"), 7516 } 7517 } 7518 7519 // ── Array.from with iterables ────────────────────────── 7520 7521 #[test] 7522 fn test_array_keys_values_entries() { 7523 match eval( 7524 "var arr = [10, 20, 30]; var r = ''; for (var v of arr.values()) { r = r + v + ','; } r", 7525 ) 7526 .unwrap() 7527 { 7528 Value::String(s) => assert_eq!(s, "10,20,30,"), 7529 v => panic!("expected '10,20,30,', got {v:?}"), 7530 } 7531 } 7532 7533 // ── Async/await tests ──────────────────────────────────── 7534 7535 #[test] 7536 fn test_async_function_returns_promise() { 7537 match eval("async function f() { return 42; } typeof f()").unwrap() { 7538 Value::String(s) => assert_eq!(s, "object"), 7539 v => panic!("expected 'object', got {v:?}"), 7540 } 7541 } 7542 7543 #[test] 7544 fn test_async_function_resolves_value() { 7545 match eval_global( 7546 "async function f() { return 42; } f().then(function(v) { result = v; });", 7547 "result", 7548 ) 7549 .unwrap() 7550 { 7551 Value::Number(n) => assert_eq!(n, 42.0), 7552 v => panic!("expected 42, got {v:?}"), 7553 } 7554 } 7555 7556 #[test] 7557 fn test_await_resolved_promise() { 7558 match eval_global( 7559 "async function f() { var x = await Promise.resolve(10); return x + 5; } 7560 f().then(function(v) { result = v; });", 7561 "result", 7562 ) 7563 .unwrap() 7564 { 7565 Value::Number(n) => assert_eq!(n, 15.0), 7566 v => panic!("expected 15, got {v:?}"), 7567 } 7568 } 7569 7570 #[test] 7571 fn test_await_non_promise_value() { 7572 match eval_global( 7573 "async function f() { var x = await 7; return x * 3; } 7574 f().then(function(v) { result = v; });", 7575 "result", 7576 ) 7577 .unwrap() 7578 { 7579 Value::Number(n) => assert_eq!(n, 21.0), 7580 v => panic!("expected 21, got {v:?}"), 7581 } 7582 } 7583 7584 #[test] 7585 fn test_multiple_awaits_in_sequence() { 7586 match eval_global( 7587 "async function f() { 7588 var a = await Promise.resolve(1); 7589 var b = await Promise.resolve(2); 7590 var c = await Promise.resolve(3); 7591 return a + b + c; 7592 } 7593 f().then(function(v) { result = v; });", 7594 "result", 7595 ) 7596 .unwrap() 7597 { 7598 Value::Number(n) => assert_eq!(n, 6.0), 7599 v => panic!("expected 6, got {v:?}"), 7600 } 7601 } 7602 7603 #[test] 7604 fn test_await_rejected_promise_throws() { 7605 match eval_global( 7606 "async function f() { 7607 try { await Promise.reject('oops'); } catch(e) { return 'caught: ' + e; } 7608 } 7609 f().then(function(v) { result = v; });", 7610 "result", 7611 ) 7612 .unwrap() 7613 { 7614 Value::String(s) => assert_eq!(s, "caught: oops"), 7615 v => panic!("expected 'caught: oops', got {v:?}"), 7616 } 7617 } 7618 7619 #[test] 7620 fn test_async_function_throw_rejects() { 7621 match eval_global( 7622 "async function f() { throw 'error!'; } 7623 f()['catch'](function(e) { result = 'got: ' + e; });", 7624 "result", 7625 ) 7626 .unwrap() 7627 { 7628 Value::String(s) => assert_eq!(s, "got: error!"), 7629 v => panic!("expected 'got: error!', got {v:?}"), 7630 } 7631 } 7632 7633 #[test] 7634 fn test_async_function_expression() { 7635 match eval_global( 7636 "var f = async function() { var x = await Promise.resolve(99); return x; }; 7637 f().then(function(v) { result = v; });", 7638 "result", 7639 ) 7640 .unwrap() 7641 { 7642 Value::Number(n) => assert_eq!(n, 99.0), 7643 v => panic!("expected 99, got {v:?}"), 7644 } 7645 } 7646 7647 #[test] 7648 fn test_async_function_expression_with_args() { 7649 match eval_global( 7650 "var f = async function(x) { return x * 2; }; 7651 f(21).then(function(v) { result = v; });", 7652 "result", 7653 ) 7654 .unwrap() 7655 { 7656 Value::Number(n) => assert_eq!(n, 42.0), 7657 v => panic!("expected 42, got {v:?}"), 7658 } 7659 } 7660 7661 #[test] 7662 fn test_async_arrow_function() { 7663 match eval_global( 7664 "var f = async x => { var y = await Promise.resolve(x); return y + 1; }; 7665 f(10).then(function(v) { result = v; });", 7666 "result", 7667 ) 7668 .unwrap() 7669 { 7670 Value::Number(n) => assert_eq!(n, 11.0), 7671 v => panic!("expected 11, got {v:?}"), 7672 } 7673 } 7674 7675 #[test] 7676 fn test_async_arrow_concise_body() { 7677 match eval_global( 7678 "var f = async x => x * 3; 7679 f(7).then(function(v) { result = v; });", 7680 "result", 7681 ) 7682 .unwrap() 7683 { 7684 Value::Number(n) => assert_eq!(n, 21.0), 7685 v => panic!("expected 21, got {v:?}"), 7686 } 7687 } 7688 7689 #[test] 7690 fn test_await_chained_promises() { 7691 match eval_global( 7692 "async function f() { 7693 var p = Promise.resolve(5).then(function(v) { return v * 10; }); 7694 return await p; 7695 } 7696 f().then(function(v) { result = v; });", 7697 "result", 7698 ) 7699 .unwrap() 7700 { 7701 Value::Number(n) => assert_eq!(n, 50.0), 7702 v => panic!("expected 50, got {v:?}"), 7703 } 7704 } 7705 7706 #[test] 7707 fn test_async_function_with_closure() { 7708 match eval_global( 7709 "function make() { 7710 var x = 100; 7711 return async function() { var y = await Promise.resolve(23); return x + y; }; 7712 } 7713 make()().then(function(v) { result = v; });", 7714 "result", 7715 ) 7716 .unwrap() 7717 { 7718 Value::Number(n) => assert_eq!(n, 123.0), 7719 v => panic!("expected 123, got {v:?}"), 7720 } 7721 } 7722 7723 #[test] 7724 fn test_async_method_in_object() { 7725 match eval_global( 7726 "var obj = { async getValue() { return await Promise.resolve(42); } }; 7727 obj.getValue().then(function(v) { result = v; });", 7728 "result", 7729 ) 7730 .unwrap() 7731 { 7732 Value::Number(n) => assert_eq!(n, 42.0), 7733 v => panic!("expected 42, got {v:?}"), 7734 } 7735 } 7736 7737 #[test] 7738 fn test_async_generator_basic() { 7739 match eval_global( 7740 "async function* gen() { yield 1; yield 2; yield 3; } 7741 var it = gen(); 7742 it.next().then(function(r) { result = r.value; });", 7743 "result", 7744 ) 7745 .unwrap() 7746 { 7747 Value::Number(n) => assert_eq!(n, 1.0), 7748 v => panic!("expected 1, got {v:?}"), 7749 } 7750 } 7751 7752 #[test] 7753 fn test_for_await_of_parsing() { 7754 let prog = 7755 crate::parser::Parser::parse("async function f() { for await (let x of iter) { } }"); 7756 assert!(prog.is_ok()); 7757 } 7758 7759 // ── Console API tests ───────────────────────────────────── 7760 7761 use std::cell::RefCell; 7762 use std::rc::Rc; 7763 7764 /// A console output sink that captures messages for testing. 7765 struct CapturedConsole { 7766 log_messages: RefCell<Vec<String>>, 7767 error_messages: RefCell<Vec<String>>, 7768 warn_messages: RefCell<Vec<String>>, 7769 } 7770 7771 impl CapturedConsole { 7772 fn new() -> Self { 7773 Self { 7774 log_messages: RefCell::new(Vec::new()), 7775 error_messages: RefCell::new(Vec::new()), 7776 warn_messages: RefCell::new(Vec::new()), 7777 } 7778 } 7779 } 7780 7781 impl ConsoleOutput for CapturedConsole { 7782 fn log(&self, message: &str) { 7783 self.log_messages.borrow_mut().push(message.to_string()); 7784 } 7785 fn error(&self, message: &str) { 7786 self.error_messages.borrow_mut().push(message.to_string()); 7787 } 7788 fn warn(&self, message: &str) { 7789 self.warn_messages.borrow_mut().push(message.to_string()); 7790 } 7791 } 7792 7793 /// Helper: compile and execute JS, capturing console output. 7794 fn eval_with_console(source: &str) -> (Result<Value, RuntimeError>, Rc<CapturedConsole>) { 7795 let console = Rc::new(CapturedConsole::new()); 7796 let program = Parser::parse(source).expect("parse failed"); 7797 let func = compiler::compile(&program).expect("compile failed"); 7798 let mut vm = Vm::new(); 7799 vm.set_console_output(Box::new(RcConsole(console.clone()))); 7800 let result = vm.execute(&func); 7801 (result, console) 7802 } 7803 7804 /// Wrapper to use Rc<CapturedConsole> as Box<dyn ConsoleOutput>. 7805 struct RcConsole(Rc<CapturedConsole>); 7806 7807 impl ConsoleOutput for RcConsole { 7808 fn log(&self, message: &str) { 7809 self.0.log(message); 7810 } 7811 fn error(&self, message: &str) { 7812 self.0.error(message); 7813 } 7814 fn warn(&self, message: &str) { 7815 self.0.warn(message); 7816 } 7817 } 7818 7819 #[test] 7820 fn test_console_log_string() { 7821 let (result, console) = eval_with_console("console.log('hello')"); 7822 assert!(result.is_ok()); 7823 assert!(matches!(result.unwrap(), Value::Undefined)); 7824 let logs = console.log_messages.borrow(); 7825 assert_eq!(logs.len(), 1); 7826 assert_eq!(logs[0], "hello"); 7827 } 7828 7829 #[test] 7830 fn test_console_log_multiple_args() { 7831 let (_, console) = eval_with_console("console.log('a', 1, true)"); 7832 let logs = console.log_messages.borrow(); 7833 assert_eq!(logs[0], "a 1 true"); 7834 } 7835 7836 #[test] 7837 fn test_console_error_to_error_channel() { 7838 let (_, console) = eval_with_console("console.error('oops')"); 7839 assert!(console.log_messages.borrow().is_empty()); 7840 let errors = console.error_messages.borrow(); 7841 assert_eq!(errors.len(), 1); 7842 assert_eq!(errors[0], "oops"); 7843 } 7844 7845 #[test] 7846 fn test_console_warn_to_warn_channel() { 7847 let (_, console) = eval_with_console("console.warn('warning')"); 7848 assert!(console.log_messages.borrow().is_empty()); 7849 let warns = console.warn_messages.borrow(); 7850 assert_eq!(warns.len(), 1); 7851 assert_eq!(warns[0], "warning"); 7852 } 7853 7854 #[test] 7855 fn test_console_info_aliases_log() { 7856 let (_, console) = eval_with_console("console.info('info msg')"); 7857 let logs = console.log_messages.borrow(); 7858 assert_eq!(logs.len(), 1); 7859 assert_eq!(logs[0], "info msg"); 7860 } 7861 7862 #[test] 7863 fn test_console_debug_aliases_log() { 7864 let (_, console) = eval_with_console("console.debug('debug msg')"); 7865 let logs = console.log_messages.borrow(); 7866 assert_eq!(logs.len(), 1); 7867 assert_eq!(logs[0], "debug msg"); 7868 } 7869 7870 #[test] 7871 fn test_console_log_returns_undefined() { 7872 let result = eval("console.log('test')").unwrap(); 7873 assert!(matches!(result, Value::Undefined)); 7874 } 7875 7876 #[test] 7877 fn test_console_log_no_args() { 7878 let (_, console) = eval_with_console("console.log()"); 7879 let logs = console.log_messages.borrow(); 7880 assert_eq!(logs.len(), 1); 7881 assert_eq!(logs[0], ""); 7882 } 7883 7884 #[test] 7885 fn test_console_log_primitives() { 7886 let (_, console) = eval_with_console( 7887 "console.log(undefined); console.log(null); console.log(42); console.log(true);", 7888 ); 7889 let logs = console.log_messages.borrow(); 7890 assert_eq!(logs.len(), 4); 7891 assert_eq!(logs[0], "undefined"); 7892 assert_eq!(logs[1], "null"); 7893 assert_eq!(logs[2], "42"); 7894 assert_eq!(logs[3], "true"); 7895 } 7896 7897 #[test] 7898 fn test_console_log_array() { 7899 let (_, console) = eval_with_console("console.log([1, 2, 3])"); 7900 let logs = console.log_messages.borrow(); 7901 assert_eq!(logs[0], "[ 1, 2, 3 ]"); 7902 } 7903 7904 #[test] 7905 fn test_console_log_object() { 7906 let (_, console) = eval_with_console("console.log({x: 1})"); 7907 let logs = console.log_messages.borrow(); 7908 assert_eq!(logs[0], "{ x: 1 }"); 7909 } 7910 7911 #[test] 7912 fn test_console_typeof() { 7913 match eval("typeof console.log").unwrap() { 7914 Value::String(s) => assert_eq!(s, "function"), 7915 v => panic!("expected 'function', got {v:?}"), 7916 } 7917 } 7918 7919 #[test] 7920 fn test_console_is_object() { 7921 match eval("typeof console").unwrap() { 7922 Value::String(s) => assert_eq!(s, "object"), 7923 v => panic!("expected 'object', got {v:?}"), 7924 } 7925 } 7926 7927 #[test] 7928 fn test_console_never_throws() { 7929 let result = eval("console.log({x: 1}); console.log([1,2]); console.log(undefined); 'ok'"); 7930 assert!(result.is_ok()); 7931 match result.unwrap() { 7932 Value::String(s) => assert_eq!(s, "ok"), 7933 v => panic!("expected 'ok', got {v:?}"), 7934 } 7935 } 7936}