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