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