we (web engine): Experimental web browser project to understand the limits of Claude
at math-date-json 4195 lines 152 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, or a closure cell. 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} 22 23impl Traceable for HeapObject { 24 fn trace(&self, visitor: &mut dyn FnMut(GcRef)) { 25 match self { 26 HeapObject::Object(data) => { 27 for prop in data.properties.values() { 28 if let Some(r) = prop.value.gc_ref() { 29 visitor(r); 30 } 31 } 32 if let Some(proto) = data.prototype { 33 visitor(proto); 34 } 35 } 36 HeapObject::Function(fdata) => { 37 if let Some(proto) = fdata.prototype_obj { 38 visitor(proto); 39 } 40 for prop in fdata.properties.values() { 41 if let Some(r) = prop.value.gc_ref() { 42 visitor(r); 43 } 44 } 45 for &uv in &fdata.upvalues { 46 visitor(uv); 47 } 48 } 49 HeapObject::Cell(val) => { 50 if let Some(r) = val.gc_ref() { 51 visitor(r); 52 } 53 } 54 } 55 } 56} 57 58/// A property descriptor stored in an object's property map. 59#[derive(Clone)] 60pub struct Property { 61 /// The property's value (for data properties). 62 pub value: Value, 63 /// Whether the value can be changed via assignment. 64 pub writable: bool, 65 /// Whether the property shows up in `for...in` and `Object.keys`. 66 pub enumerable: bool, 67 /// Whether the property can be deleted or its attributes changed. 68 pub configurable: bool, 69} 70 71impl Property { 72 /// Create a new data property with all flags set to true (the JS default for 73 /// properties created by assignment). 74 pub fn data(value: Value) -> Self { 75 Self { 76 value, 77 writable: true, 78 enumerable: true, 79 configurable: true, 80 } 81 } 82 83 /// Create a non-enumerable, non-configurable property (e.g. built-in methods). 84 pub fn builtin(value: Value) -> Self { 85 Self { 86 value, 87 writable: true, 88 enumerable: false, 89 configurable: false, 90 } 91 } 92} 93 94/// A JS plain object (properties stored as a HashMap with descriptors). 95pub struct ObjectData { 96 pub properties: HashMap<String, Property>, 97 pub prototype: Option<GcRef>, 98 /// Whether new properties can be added (Object.preventExtensions). 99 pub extensible: bool, 100} 101 102impl ObjectData { 103 pub fn new() -> Self { 104 Self { 105 properties: HashMap::new(), 106 prototype: None, 107 extensible: true, 108 } 109 } 110} 111 112impl Default for ObjectData { 113 fn default() -> Self { 114 Self::new() 115 } 116} 117 118/// A runtime function value: either bytecode or native. 119/// 120/// In JavaScript, functions are objects and can have arbitrary properties 121/// (e.g. `assert.sameValue = function() {}`). 122pub struct FunctionData { 123 pub name: String, 124 pub kind: FunctionKind, 125 /// The `.prototype` property object (for use as a constructor with `instanceof`). 126 pub prototype_obj: Option<GcRef>, 127 /// Arbitrary properties set on this function (functions are objects in JS). 128 pub properties: HashMap<String, Property>, 129 /// Captured upvalue cells (GcRefs to HeapObject::Cell values). 130 pub upvalues: Vec<GcRef>, 131} 132 133#[derive(Clone)] 134pub enum FunctionKind { 135 /// Bytecode function. 136 Bytecode(BytecodeFunc), 137 /// Native (Rust) function. 138 Native(NativeFunc), 139} 140 141#[derive(Clone)] 142pub struct BytecodeFunc { 143 pub func: Function, 144} 145 146/// A native function callable from JS. 147#[derive(Clone)] 148pub struct NativeFunc { 149 pub callback: fn(&[Value], &mut NativeContext) -> Result<Value, RuntimeError>, 150} 151 152/// Context passed to native functions, providing GC access and `this` binding. 153pub struct NativeContext<'a> { 154 pub gc: &'a mut Gc<HeapObject>, 155 pub this: Value, 156} 157 158// ── JS Value ────────────────────────────────────────────────── 159 160/// A JavaScript runtime value. 161/// 162/// Primitive types (Undefined, Null, Boolean, Number, String) are stored 163/// inline. Objects and Functions are heap-allocated via the GC and referenced 164/// by a [`GcRef`] handle. 165#[derive(Clone)] 166pub enum Value { 167 Undefined, 168 Null, 169 Boolean(bool), 170 Number(f64), 171 String(String), 172 /// A GC-managed plain object. 173 Object(GcRef), 174 /// A GC-managed function. 175 Function(GcRef), 176} 177 178impl fmt::Debug for Value { 179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 180 match self { 181 Value::Undefined => write!(f, "undefined"), 182 Value::Null => write!(f, "null"), 183 Value::Boolean(b) => write!(f, "{b}"), 184 Value::Number(n) => write!(f, "{n}"), 185 Value::String(s) => write!(f, "\"{}\"", s), 186 Value::Object(_) => write!(f, "[object Object]"), 187 Value::Function(_) => write!(f, "[Function]"), 188 } 189 } 190} 191 192impl fmt::Display for Value { 193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 194 match self { 195 Value::Undefined => write!(f, "undefined"), 196 Value::Null => write!(f, "null"), 197 Value::Boolean(b) => write!(f, "{b}"), 198 Value::Number(n) => format_number(*n, f), 199 Value::String(s) => write!(f, "{s}"), 200 Value::Object(_) => write!(f, "[object Object]"), 201 Value::Function(_) => write!(f, "function() {{ [native code] }}"), 202 } 203 } 204} 205 206/// Format a number following JS conventions (no trailing .0 for integers). 207fn format_number(n: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result { 208 if n.is_nan() { 209 write!(f, "NaN") 210 } else if n.is_infinite() { 211 if n.is_sign_positive() { 212 write!(f, "Infinity") 213 } else { 214 write!(f, "-Infinity") 215 } 216 } else if n == 0.0 { 217 write!(f, "0") 218 } else if n.fract() == 0.0 && n.abs() < 1e20 { 219 write!(f, "{}", n as i64) 220 } else { 221 write!(f, "{n}") 222 } 223} 224 225impl Value { 226 /// Abstract `ToBoolean` (ECMA-262 §7.1.2). 227 pub fn to_boolean(&self) -> bool { 228 match self { 229 Value::Undefined | Value::Null => false, 230 Value::Boolean(b) => *b, 231 Value::Number(n) => *n != 0.0 && !n.is_nan(), 232 Value::String(s) => !s.is_empty(), 233 Value::Object(_) | Value::Function(_) => true, 234 } 235 } 236 237 /// Abstract `ToNumber` (ECMA-262 §7.1.3). 238 pub fn to_number(&self) -> f64 { 239 match self { 240 Value::Undefined => f64::NAN, 241 Value::Null => 0.0, 242 Value::Boolean(true) => 1.0, 243 Value::Boolean(false) => 0.0, 244 Value::Number(n) => *n, 245 Value::String(s) => { 246 let s = s.trim(); 247 if s.is_empty() { 248 0.0 249 } else if s == "Infinity" || s == "+Infinity" { 250 f64::INFINITY 251 } else if s == "-Infinity" { 252 f64::NEG_INFINITY 253 } else { 254 s.parse::<f64>().unwrap_or(f64::NAN) 255 } 256 } 257 Value::Object(_) | Value::Function(_) => f64::NAN, 258 } 259 } 260 261 /// Abstract `ToString` (ECMA-262 §7.1.12). 262 /// 263 /// Requires `&Gc` to look up function names for `Value::Function`. 264 pub fn to_js_string(&self, gc: &Gc<HeapObject>) -> String { 265 match self { 266 Value::Undefined => "undefined".to_string(), 267 Value::Null => "null".to_string(), 268 Value::Boolean(true) => "true".to_string(), 269 Value::Boolean(false) => "false".to_string(), 270 Value::Number(n) => js_number_to_string(*n), 271 Value::String(s) => s.clone(), 272 Value::Object(_) => "[object Object]".to_string(), 273 Value::Function(gc_ref) => gc 274 .get(*gc_ref) 275 .and_then(|obj| match obj { 276 HeapObject::Function(f) => { 277 Some(format!("function {}() {{ [native code] }}", f.name)) 278 } 279 _ => None, 280 }) 281 .unwrap_or_else(|| "function() { [native code] }".to_string()), 282 } 283 } 284 285 /// `typeof` operator result. 286 pub fn type_of(&self) -> &'static str { 287 match self { 288 Value::Undefined => "undefined", 289 Value::Null => "object", // yes, this is the spec 290 Value::Boolean(_) => "boolean", 291 Value::Number(_) => "number", 292 Value::String(_) => "string", 293 Value::Object(_) => "object", 294 Value::Function(_) => "function", 295 } 296 } 297 298 /// Is this value nullish (null or undefined)? 299 pub fn is_nullish(&self) -> bool { 300 matches!(self, Value::Undefined | Value::Null) 301 } 302 303 /// Extract the `GcRef` if this value is an Object or Function. 304 pub fn gc_ref(&self) -> Option<GcRef> { 305 match self { 306 Value::Object(r) | Value::Function(r) => Some(*r), 307 _ => None, 308 } 309 } 310} 311 312/// Format a number as JS would. 313pub(crate) fn js_number_to_string(n: f64) -> String { 314 if n.is_nan() { 315 "NaN".to_string() 316 } else if n.is_infinite() { 317 if n.is_sign_positive() { 318 "Infinity".to_string() 319 } else { 320 "-Infinity".to_string() 321 } 322 } else if n == 0.0 { 323 "0".to_string() 324 } else if n.fract() == 0.0 && n.abs() < 1e20 { 325 format!("{}", n as i64) 326 } else { 327 format!("{n}") 328 } 329} 330 331// ── Runtime errors ──────────────────────────────────────────── 332 333/// JavaScript runtime error types. 334#[derive(Debug, Clone)] 335pub struct RuntimeError { 336 pub kind: ErrorKind, 337 pub message: String, 338} 339 340#[derive(Debug, Clone, Copy, PartialEq, Eq)] 341pub enum ErrorKind { 342 TypeError, 343 ReferenceError, 344 RangeError, 345 SyntaxError, 346 Error, 347} 348 349impl fmt::Display for RuntimeError { 350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 351 let name = match self.kind { 352 ErrorKind::TypeError => "TypeError", 353 ErrorKind::ReferenceError => "ReferenceError", 354 ErrorKind::RangeError => "RangeError", 355 ErrorKind::SyntaxError => "SyntaxError", 356 ErrorKind::Error => "Error", 357 }; 358 write!(f, "{name}: {}", self.message) 359 } 360} 361 362impl RuntimeError { 363 pub fn type_error(msg: impl Into<String>) -> Self { 364 Self { 365 kind: ErrorKind::TypeError, 366 message: msg.into(), 367 } 368 } 369 370 pub fn reference_error(msg: impl Into<String>) -> Self { 371 Self { 372 kind: ErrorKind::ReferenceError, 373 message: msg.into(), 374 } 375 } 376 377 pub fn range_error(msg: impl Into<String>) -> Self { 378 Self { 379 kind: ErrorKind::RangeError, 380 message: msg.into(), 381 } 382 } 383 384 pub fn syntax_error(msg: impl Into<String>) -> Self { 385 Self { 386 kind: ErrorKind::SyntaxError, 387 message: msg.into(), 388 } 389 } 390 391 /// Convert to a JS Value (an error object). Allocates through the GC. 392 pub fn to_value(&self, gc: &mut Gc<HeapObject>) -> Value { 393 let mut obj = ObjectData::new(); 394 obj.properties.insert( 395 "message".to_string(), 396 Property::data(Value::String(self.message.clone())), 397 ); 398 let name = match self.kind { 399 ErrorKind::TypeError => "TypeError", 400 ErrorKind::ReferenceError => "ReferenceError", 401 ErrorKind::RangeError => "RangeError", 402 ErrorKind::SyntaxError => "SyntaxError", 403 ErrorKind::Error => "Error", 404 }; 405 obj.properties.insert( 406 "name".to_string(), 407 Property::data(Value::String(name.to_string())), 408 ); 409 Value::Object(gc.alloc(HeapObject::Object(obj))) 410 } 411} 412 413// ── Property access helpers ────────────────────────────────── 414 415/// Get a property from an object, walking the prototype chain. 416fn gc_get_property(gc: &Gc<HeapObject>, obj_ref: GcRef, key: &str) -> Value { 417 let proto = { 418 match gc.get(obj_ref) { 419 Some(HeapObject::Object(data)) => { 420 if let Some(prop) = data.properties.get(key) { 421 return prop.value.clone(); 422 } 423 data.prototype 424 } 425 Some(HeapObject::Function(fdata)) => { 426 // Check user-defined properties first. 427 if let Some(prop) = fdata.properties.get(key) { 428 return prop.value.clone(); 429 } 430 // Functions have a `.prototype` property. 431 if key == "prototype" { 432 if let Some(proto_ref) = fdata.prototype_obj { 433 return Value::Object(proto_ref); 434 } 435 return Value::Undefined; 436 } 437 None 438 } 439 _ => return Value::Undefined, 440 } 441 }; 442 if let Some(proto_ref) = proto { 443 gc_get_property(gc, proto_ref, key) 444 } else { 445 Value::Undefined 446 } 447} 448 449/// Check if an object has a property (own or inherited). 450fn gc_has_property(gc: &Gc<HeapObject>, obj_ref: GcRef, key: &str) -> bool { 451 let proto = { 452 match gc.get(obj_ref) { 453 Some(HeapObject::Object(data)) => { 454 if data.properties.contains_key(key) { 455 return true; 456 } 457 data.prototype 458 } 459 Some(HeapObject::Function(fdata)) => { 460 if fdata.properties.contains_key(key) || key == "prototype" { 461 return true; 462 } 463 None 464 } 465 _ => return false, 466 } 467 }; 468 if let Some(proto_ref) = proto { 469 gc_has_property(gc, proto_ref, key) 470 } else { 471 false 472 } 473} 474 475/// Collect all enumerable string keys of an object (own + inherited), in proper order. 476/// Integer indices first (sorted numerically), then string keys in insertion order. 477fn gc_enumerate_keys(gc: &Gc<HeapObject>, obj_ref: GcRef) -> Vec<String> { 478 let mut seen = std::collections::HashSet::new(); 479 let mut integer_keys: Vec<(u32, String)> = Vec::new(); 480 let mut string_keys: Vec<String> = Vec::new(); 481 let mut current = Some(obj_ref); 482 483 while let Some(cur_ref) = current { 484 match gc.get(cur_ref) { 485 Some(HeapObject::Object(data)) => { 486 for (key, prop) in &data.properties { 487 if prop.enumerable && seen.insert(key.clone()) { 488 if let Ok(idx) = key.parse::<u32>() { 489 integer_keys.push((idx, key.clone())); 490 } else { 491 string_keys.push(key.clone()); 492 } 493 } 494 } 495 current = data.prototype; 496 } 497 _ => break, 498 } 499 } 500 501 // Integer indices sorted numerically first, then string keys in collected order. 502 integer_keys.sort_by_key(|(idx, _)| *idx); 503 let mut result: Vec<String> = integer_keys.into_iter().map(|(_, k)| k).collect(); 504 result.extend(string_keys); 505 result 506} 507 508/// Check if `obj_ref` is an instance of the constructor at `ctor_ref`. 509/// Walks the prototype chain of `obj_ref` looking for `ctor.prototype`. 510fn gc_instanceof(gc: &Gc<HeapObject>, obj_ref: GcRef, ctor_ref: GcRef) -> bool { 511 // Get the constructor's .prototype object. 512 let ctor_proto = match gc.get(ctor_ref) { 513 Some(HeapObject::Function(fdata)) => match fdata.prototype_obj { 514 Some(p) => p, 515 None => return false, 516 }, 517 _ => return false, 518 }; 519 520 // Walk the prototype chain of obj_ref. 521 let mut current = match gc.get(obj_ref) { 522 Some(HeapObject::Object(data)) => data.prototype, 523 _ => None, 524 }; 525 526 while let Some(proto_ref) = current { 527 if proto_ref == ctor_proto { 528 return true; 529 } 530 current = match gc.get(proto_ref) { 531 Some(HeapObject::Object(data)) => data.prototype, 532 _ => None, 533 }; 534 } 535 false 536} 537 538/// Get a string property (length, index access). 539fn string_get_property(s: &str, key: &str) -> Value { 540 if key == "length" { 541 Value::Number(s.len() as f64) 542 } else if let Ok(idx) = key.parse::<usize>() { 543 s.chars() 544 .nth(idx) 545 .map(|c| Value::String(c.to_string())) 546 .unwrap_or(Value::Undefined) 547 } else { 548 Value::Undefined 549 } 550} 551 552// ── Type conversion helpers ────────────────────────────────── 553 554/// ToInt32 (ECMA-262 §7.1.5). 555fn to_int32(val: &Value) -> i32 { 556 let n = val.to_number(); 557 if n.is_nan() || n.is_infinite() || n == 0.0 { 558 return 0; 559 } 560 let i = n.trunc() as i64; 561 (i & 0xFFFF_FFFF) as i32 562} 563 564/// ToUint32 (ECMA-262 §7.1.6). 565fn to_uint32(val: &Value) -> u32 { 566 let n = val.to_number(); 567 if n.is_nan() || n.is_infinite() || n == 0.0 { 568 return 0; 569 } 570 let i = n.trunc() as i64; 571 (i & 0xFFFF_FFFF) as u32 572} 573 574// ── Equality ───────────────────────────────────────────────── 575 576/// Abstract equality comparison (==) per ECMA-262 §7.2.14. 577fn abstract_eq(x: &Value, y: &Value) -> bool { 578 match (x, y) { 579 (Value::Undefined, Value::Undefined) => true, 580 (Value::Null, Value::Null) => true, 581 (Value::Undefined, Value::Null) | (Value::Null, Value::Undefined) => true, 582 (Value::Number(a), Value::Number(b)) => a == b, 583 (Value::String(a), Value::String(b)) => a == b, 584 (Value::Boolean(a), Value::Boolean(b)) => a == b, 585 // Number / String → convert String to Number. 586 (Value::Number(_), Value::String(_)) => abstract_eq(x, &Value::Number(y.to_number())), 587 (Value::String(_), Value::Number(_)) => abstract_eq(&Value::Number(x.to_number()), y), 588 // Boolean → Number. 589 (Value::Boolean(_), _) => abstract_eq(&Value::Number(x.to_number()), y), 590 (_, Value::Boolean(_)) => abstract_eq(x, &Value::Number(y.to_number())), 591 // Same GcRef → equal. 592 (Value::Object(a), Value::Object(b)) => a == b, 593 (Value::Function(a), Value::Function(b)) => a == b, 594 _ => false, 595 } 596} 597 598/// Strict equality comparison (===) per ECMA-262 §7.2.15. 599fn strict_eq(x: &Value, y: &Value) -> bool { 600 match (x, y) { 601 (Value::Undefined, Value::Undefined) => true, 602 (Value::Null, Value::Null) => true, 603 (Value::Number(a), Value::Number(b)) => a == b, 604 (Value::String(a), Value::String(b)) => a == b, 605 (Value::Boolean(a), Value::Boolean(b)) => a == b, 606 // Reference identity for heap objects. 607 (Value::Object(a), Value::Object(b)) => a == b, 608 (Value::Function(a), Value::Function(b)) => a == b, 609 _ => false, 610 } 611} 612 613// ── Relational comparison ──────────────────────────────────── 614 615/// Abstract relational comparison. Returns false for NaN comparisons. 616fn abstract_relational( 617 lhs: &Value, 618 rhs: &Value, 619 predicate: fn(std::cmp::Ordering) -> bool, 620) -> bool { 621 // If both are strings, compare lexicographically. 622 if let (Value::String(a), Value::String(b)) = (lhs, rhs) { 623 return predicate(a.cmp(b)); 624 } 625 // Otherwise, compare as numbers. 626 let a = lhs.to_number(); 627 let b = rhs.to_number(); 628 if a.is_nan() || b.is_nan() { 629 return false; 630 } 631 predicate(a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)) 632} 633 634// ── Addition ───────────────────────────────────────────────── 635 636/// The + operator: string concat if either operand is a string, else numeric add. 637fn add_values(lhs: &Value, rhs: &Value, gc: &Gc<HeapObject>) -> Value { 638 match (lhs, rhs) { 639 (Value::String(a), _) => Value::String(format!("{a}{}", rhs.to_js_string(gc))), 640 (_, Value::String(b)) => Value::String(format!("{}{b}", lhs.to_js_string(gc))), 641 _ => Value::Number(lhs.to_number() + rhs.to_number()), 642 } 643} 644 645// ── Call frame ──────────────────────────────────────────────── 646 647/// A single call frame on the VM's call stack. 648struct CallFrame { 649 /// The function being executed. 650 func: Function, 651 /// Instruction pointer (byte offset into func.code). 652 ip: usize, 653 /// Base register index in the VM's register file. 654 base: usize, 655 /// Register to write the return value into (absolute index in register file). 656 return_reg: usize, 657 /// Exception handler stack for this frame. 658 exception_handlers: Vec<ExceptionHandler>, 659 /// Captured upvalue cells from the closure that created this call frame. 660 upvalues: Vec<GcRef>, 661} 662 663/// An exception handler entry (for try/catch). 664struct ExceptionHandler { 665 /// IP to jump to on exception (the catch block start). 666 catch_ip: usize, 667 /// Register to store the caught exception value. 668 catch_reg: Reg, 669} 670 671// ── VM ─────────────────────────────────────────────────────── 672 673/// The JavaScript virtual machine. 674pub struct Vm { 675 /// Register file (flat array shared across frames via base offsets). 676 registers: Vec<Value>, 677 /// Call stack. 678 frames: Vec<CallFrame>, 679 /// Global variables. 680 globals: HashMap<String, Value>, 681 /// Garbage collector managing heap objects. 682 pub gc: Gc<HeapObject>, 683 /// Optional instruction limit. If set, the VM will return an error after 684 /// executing this many instructions (prevents infinite loops). 685 instruction_limit: Option<u64>, 686 /// Number of instructions executed so far. 687 instructions_executed: u64, 688 /// Built-in Object.prototype (root of the prototype chain). 689 pub object_prototype: Option<GcRef>, 690 /// Built-in Array.prototype (set on newly created arrays). 691 pub array_prototype: Option<GcRef>, 692 /// Built-in String.prototype (for primitive auto-boxing). 693 pub string_prototype: Option<GcRef>, 694 /// Built-in Number.prototype (for primitive auto-boxing). 695 pub number_prototype: Option<GcRef>, 696 /// Built-in Boolean.prototype (for primitive auto-boxing). 697 pub boolean_prototype: Option<GcRef>, 698 /// Built-in Date.prototype (for Date constructor objects). 699 pub date_prototype: Option<GcRef>, 700} 701 702/// Maximum register file size. 703const MAX_REGISTERS: usize = 4096; 704/// Maximum call depth. 705const MAX_CALL_DEPTH: usize = 512; 706 707impl Vm { 708 pub fn new() -> Self { 709 let mut vm = Self { 710 registers: vec![Value::Undefined; 256], 711 frames: Vec::new(), 712 globals: HashMap::new(), 713 gc: Gc::new(), 714 instruction_limit: None, 715 instructions_executed: 0, 716 object_prototype: None, 717 array_prototype: None, 718 string_prototype: None, 719 number_prototype: None, 720 boolean_prototype: None, 721 date_prototype: None, 722 }; 723 crate::builtins::init_builtins(&mut vm); 724 vm 725 } 726 727 /// Set an instruction limit. The VM will return a RuntimeError after 728 /// executing this many instructions. 729 pub fn set_instruction_limit(&mut self, limit: u64) { 730 self.instruction_limit = Some(limit); 731 } 732 733 /// Execute a compiled top-level function and return the completion value. 734 pub fn execute(&mut self, func: &Function) -> Result<Value, RuntimeError> { 735 let reg_count = func.register_count as usize; 736 self.ensure_registers(reg_count); 737 738 self.frames.push(CallFrame { 739 func: func.clone(), 740 ip: 0, 741 base: 0, 742 return_reg: 0, 743 exception_handlers: Vec::new(), 744 upvalues: Vec::new(), 745 }); 746 747 self.run() 748 } 749 750 /// Ensure the register file has at least `needed` slots. 751 fn ensure_registers(&mut self, needed: usize) { 752 if needed > self.registers.len() { 753 if needed > MAX_REGISTERS { 754 return; 755 } 756 self.registers.resize(needed, Value::Undefined); 757 } 758 } 759 760 /// Read a u8 from the current frame's bytecode and advance IP. 761 #[inline] 762 fn read_u8(frame: &mut CallFrame) -> u8 { 763 let b = frame.func.code[frame.ip]; 764 frame.ip += 1; 765 b 766 } 767 768 /// Read a u16 (little-endian) from the current frame's bytecode and advance IP. 769 #[inline] 770 fn read_u16(frame: &mut CallFrame) -> u16 { 771 let lo = frame.func.code[frame.ip]; 772 let hi = frame.func.code[frame.ip + 1]; 773 frame.ip += 2; 774 u16::from_le_bytes([lo, hi]) 775 } 776 777 /// Read an i32 (little-endian) from the current frame's bytecode and advance IP. 778 #[inline] 779 fn read_i32(frame: &mut CallFrame) -> i32 { 780 let bytes = [ 781 frame.func.code[frame.ip], 782 frame.func.code[frame.ip + 1], 783 frame.func.code[frame.ip + 2], 784 frame.func.code[frame.ip + 3], 785 ]; 786 frame.ip += 4; 787 i32::from_le_bytes(bytes) 788 } 789 790 /// Collect all GcRef values reachable from the mutator (roots for GC). 791 fn collect_roots(&self) -> Vec<GcRef> { 792 let mut roots = Vec::new(); 793 for val in &self.registers { 794 if let Some(r) = val.gc_ref() { 795 roots.push(r); 796 } 797 } 798 for val in self.globals.values() { 799 if let Some(r) = val.gc_ref() { 800 roots.push(r); 801 } 802 } 803 for frame in &self.frames { 804 for &uv in &frame.upvalues { 805 roots.push(uv); 806 } 807 } 808 // Built-in prototype roots. 809 if let Some(r) = self.object_prototype { 810 roots.push(r); 811 } 812 if let Some(r) = self.array_prototype { 813 roots.push(r); 814 } 815 if let Some(r) = self.string_prototype { 816 roots.push(r); 817 } 818 if let Some(r) = self.number_prototype { 819 roots.push(r); 820 } 821 if let Some(r) = self.boolean_prototype { 822 roots.push(r); 823 } 824 roots 825 } 826 827 /// Main dispatch loop. 828 fn run(&mut self) -> Result<Value, RuntimeError> { 829 loop { 830 let fi = self.frames.len() - 1; 831 832 // Check if we've reached the end of bytecode. 833 if self.frames[fi].ip >= self.frames[fi].func.code.len() { 834 if self.frames.len() == 1 { 835 self.frames.pop(); 836 return Ok(Value::Undefined); 837 } 838 let old = self.frames.pop().unwrap(); 839 self.registers[old.return_reg] = Value::Undefined; 840 continue; 841 } 842 843 // Instruction limit check (for test harnesses). 844 if let Some(limit) = self.instruction_limit { 845 self.instructions_executed += 1; 846 if self.instructions_executed > limit { 847 return Err(RuntimeError { 848 kind: ErrorKind::Error, 849 message: "instruction limit exceeded".into(), 850 }); 851 } 852 } 853 854 let opcode_byte = self.frames[fi].func.code[self.frames[fi].ip]; 855 self.frames[fi].ip += 1; 856 857 let Some(op) = Op::from_byte(opcode_byte) else { 858 return Err(RuntimeError { 859 kind: ErrorKind::Error, 860 message: format!("unknown opcode: 0x{opcode_byte:02X}"), 861 }); 862 }; 863 864 match op { 865 // ── Register loads ────────────────────────────── 866 Op::LoadConst => { 867 let dst = Self::read_u8(&mut self.frames[fi]); 868 let idx = Self::read_u16(&mut self.frames[fi]) as usize; 869 let base = self.frames[fi].base; 870 let val = match &self.frames[fi].func.constants[idx] { 871 Constant::Number(n) => Value::Number(*n), 872 Constant::String(s) => Value::String(s.clone()), 873 }; 874 self.registers[base + dst as usize] = val; 875 } 876 Op::LoadNull => { 877 let dst = Self::read_u8(&mut self.frames[fi]); 878 let base = self.frames[fi].base; 879 self.registers[base + dst as usize] = Value::Null; 880 } 881 Op::LoadUndefined => { 882 let dst = Self::read_u8(&mut self.frames[fi]); 883 let base = self.frames[fi].base; 884 self.registers[base + dst as usize] = Value::Undefined; 885 } 886 Op::LoadTrue => { 887 let dst = Self::read_u8(&mut self.frames[fi]); 888 let base = self.frames[fi].base; 889 self.registers[base + dst as usize] = Value::Boolean(true); 890 } 891 Op::LoadFalse => { 892 let dst = Self::read_u8(&mut self.frames[fi]); 893 let base = self.frames[fi].base; 894 self.registers[base + dst as usize] = Value::Boolean(false); 895 } 896 Op::LoadInt8 => { 897 let dst = Self::read_u8(&mut self.frames[fi]); 898 let val = Self::read_u8(&mut self.frames[fi]) as i8; 899 let base = self.frames[fi].base; 900 self.registers[base + dst as usize] = Value::Number(val as f64); 901 } 902 Op::Move => { 903 let dst = Self::read_u8(&mut self.frames[fi]); 904 let src = Self::read_u8(&mut self.frames[fi]); 905 let base = self.frames[fi].base; 906 let val = self.registers[base + src as usize].clone(); 907 self.registers[base + dst as usize] = val; 908 } 909 910 // ── Global access ────────────────────────────── 911 Op::LoadGlobal => { 912 let dst = Self::read_u8(&mut self.frames[fi]); 913 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 914 let base = self.frames[fi].base; 915 let name = &self.frames[fi].func.names[name_idx]; 916 let val = self.globals.get(name).cloned().unwrap_or(Value::Undefined); 917 self.registers[base + dst as usize] = val; 918 } 919 Op::StoreGlobal => { 920 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 921 let src = Self::read_u8(&mut self.frames[fi]); 922 let base = self.frames[fi].base; 923 let name = self.frames[fi].func.names[name_idx].clone(); 924 let val = self.registers[base + src as usize].clone(); 925 self.globals.insert(name, val); 926 } 927 928 // ── Arithmetic ───────────────────────────────── 929 Op::Add => { 930 let dst = Self::read_u8(&mut self.frames[fi]); 931 let lhs_r = Self::read_u8(&mut self.frames[fi]); 932 let rhs_r = Self::read_u8(&mut self.frames[fi]); 933 let base = self.frames[fi].base; 934 let result = add_values( 935 &self.registers[base + lhs_r as usize], 936 &self.registers[base + rhs_r as usize], 937 &self.gc, 938 ); 939 self.registers[base + dst as usize] = result; 940 } 941 Op::Sub => { 942 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 943 let result = self.registers[base + lhs_r].to_number() 944 - self.registers[base + rhs_r].to_number(); 945 self.registers[base + dst] = Value::Number(result); 946 } 947 Op::Mul => { 948 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 949 let result = self.registers[base + lhs_r].to_number() 950 * self.registers[base + rhs_r].to_number(); 951 self.registers[base + dst] = Value::Number(result); 952 } 953 Op::Div => { 954 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 955 let result = self.registers[base + lhs_r].to_number() 956 / self.registers[base + rhs_r].to_number(); 957 self.registers[base + dst] = Value::Number(result); 958 } 959 Op::Rem => { 960 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 961 let result = self.registers[base + lhs_r].to_number() 962 % self.registers[base + rhs_r].to_number(); 963 self.registers[base + dst] = Value::Number(result); 964 } 965 Op::Exp => { 966 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 967 let result = self.registers[base + lhs_r] 968 .to_number() 969 .powf(self.registers[base + rhs_r].to_number()); 970 self.registers[base + dst] = Value::Number(result); 971 } 972 Op::Neg => { 973 let dst = Self::read_u8(&mut self.frames[fi]); 974 let src = Self::read_u8(&mut self.frames[fi]); 975 let base = self.frames[fi].base; 976 let result = -self.registers[base + src as usize].to_number(); 977 self.registers[base + dst as usize] = Value::Number(result); 978 } 979 980 // ── Bitwise ──────────────────────────────────── 981 Op::BitAnd => { 982 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 983 let a = to_int32(&self.registers[base + lhs_r]); 984 let b = to_int32(&self.registers[base + rhs_r]); 985 self.registers[base + dst] = Value::Number((a & b) as f64); 986 } 987 Op::BitOr => { 988 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 989 let a = to_int32(&self.registers[base + lhs_r]); 990 let b = to_int32(&self.registers[base + rhs_r]); 991 self.registers[base + dst] = Value::Number((a | b) as f64); 992 } 993 Op::BitXor => { 994 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 995 let a = to_int32(&self.registers[base + lhs_r]); 996 let b = to_int32(&self.registers[base + rhs_r]); 997 self.registers[base + dst] = Value::Number((a ^ b) as f64); 998 } 999 Op::ShiftLeft => { 1000 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1001 let a = to_int32(&self.registers[base + lhs_r]); 1002 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 1003 self.registers[base + dst] = Value::Number((a << b) as f64); 1004 } 1005 Op::ShiftRight => { 1006 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1007 let a = to_int32(&self.registers[base + lhs_r]); 1008 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 1009 self.registers[base + dst] = Value::Number((a >> b) as f64); 1010 } 1011 Op::UShiftRight => { 1012 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1013 let a = to_uint32(&self.registers[base + lhs_r]); 1014 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 1015 self.registers[base + dst] = Value::Number((a >> b) as f64); 1016 } 1017 Op::BitNot => { 1018 let dst = Self::read_u8(&mut self.frames[fi]); 1019 let src = Self::read_u8(&mut self.frames[fi]); 1020 let base = self.frames[fi].base; 1021 let result = !to_int32(&self.registers[base + src as usize]); 1022 self.registers[base + dst as usize] = Value::Number(result as f64); 1023 } 1024 1025 // ── Comparison ───────────────────────────────── 1026 Op::Eq => { 1027 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1028 let result = 1029 abstract_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 1030 self.registers[base + dst] = Value::Boolean(result); 1031 } 1032 Op::StrictEq => { 1033 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1034 let result = 1035 strict_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 1036 self.registers[base + dst] = Value::Boolean(result); 1037 } 1038 Op::NotEq => { 1039 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1040 let result = 1041 !abstract_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 1042 self.registers[base + dst] = Value::Boolean(result); 1043 } 1044 Op::StrictNotEq => { 1045 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1046 let result = 1047 !strict_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 1048 self.registers[base + dst] = Value::Boolean(result); 1049 } 1050 Op::LessThan => { 1051 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1052 let result = abstract_relational( 1053 &self.registers[base + lhs_r], 1054 &self.registers[base + rhs_r], 1055 |ord| ord == std::cmp::Ordering::Less, 1056 ); 1057 self.registers[base + dst] = Value::Boolean(result); 1058 } 1059 Op::LessEq => { 1060 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1061 let result = abstract_relational( 1062 &self.registers[base + lhs_r], 1063 &self.registers[base + rhs_r], 1064 |ord| ord != std::cmp::Ordering::Greater, 1065 ); 1066 self.registers[base + dst] = Value::Boolean(result); 1067 } 1068 Op::GreaterThan => { 1069 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1070 let result = abstract_relational( 1071 &self.registers[base + lhs_r], 1072 &self.registers[base + rhs_r], 1073 |ord| ord == std::cmp::Ordering::Greater, 1074 ); 1075 self.registers[base + dst] = Value::Boolean(result); 1076 } 1077 Op::GreaterEq => { 1078 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1079 let result = abstract_relational( 1080 &self.registers[base + lhs_r], 1081 &self.registers[base + rhs_r], 1082 |ord| ord != std::cmp::Ordering::Less, 1083 ); 1084 self.registers[base + dst] = Value::Boolean(result); 1085 } 1086 1087 // ── Logical / unary ──────────────────────────── 1088 Op::LogicalNot => { 1089 let dst = Self::read_u8(&mut self.frames[fi]); 1090 let src = Self::read_u8(&mut self.frames[fi]); 1091 let base = self.frames[fi].base; 1092 let result = !self.registers[base + src as usize].to_boolean(); 1093 self.registers[base + dst as usize] = Value::Boolean(result); 1094 } 1095 Op::TypeOf => { 1096 let dst = Self::read_u8(&mut self.frames[fi]); 1097 let src = Self::read_u8(&mut self.frames[fi]); 1098 let base = self.frames[fi].base; 1099 let t = self.registers[base + src as usize].type_of(); 1100 self.registers[base + dst as usize] = Value::String(t.to_string()); 1101 } 1102 Op::InstanceOf => { 1103 let dst = Self::read_u8(&mut self.frames[fi]); 1104 let lhs_r = Self::read_u8(&mut self.frames[fi]); 1105 let rhs_r = Self::read_u8(&mut self.frames[fi]); 1106 let base = self.frames[fi].base; 1107 let result = match ( 1108 &self.registers[base + lhs_r as usize], 1109 &self.registers[base + rhs_r as usize], 1110 ) { 1111 (Value::Object(obj_ref), Value::Function(ctor_ref)) => { 1112 gc_instanceof(&self.gc, *obj_ref, *ctor_ref) 1113 } 1114 (_, Value::Function(_)) => false, 1115 _ => { 1116 return Err(RuntimeError::type_error( 1117 "Right-hand side of instanceof is not callable", 1118 )); 1119 } 1120 }; 1121 self.registers[base + dst as usize] = Value::Boolean(result); 1122 } 1123 Op::In => { 1124 let dst = Self::read_u8(&mut self.frames[fi]); 1125 let key_r = Self::read_u8(&mut self.frames[fi]); 1126 let obj_r = Self::read_u8(&mut self.frames[fi]); 1127 let base = self.frames[fi].base; 1128 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 1129 let result = match self.registers[base + obj_r as usize] { 1130 Value::Object(gc_ref) => { 1131 Value::Boolean(gc_has_property(&self.gc, gc_ref, &key)) 1132 } 1133 _ => { 1134 return Err(RuntimeError::type_error( 1135 "Cannot use 'in' operator to search for property in non-object", 1136 )); 1137 } 1138 }; 1139 self.registers[base + dst as usize] = result; 1140 } 1141 Op::Void => { 1142 let dst = Self::read_u8(&mut self.frames[fi]); 1143 let _src = Self::read_u8(&mut self.frames[fi]); 1144 let base = self.frames[fi].base; 1145 self.registers[base + dst as usize] = Value::Undefined; 1146 } 1147 1148 // ── Control flow ─────────────────────────────── 1149 Op::Jump => { 1150 let offset = Self::read_i32(&mut self.frames[fi]); 1151 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 1152 } 1153 Op::JumpIfTrue => { 1154 let reg = Self::read_u8(&mut self.frames[fi]); 1155 let offset = Self::read_i32(&mut self.frames[fi]); 1156 let base = self.frames[fi].base; 1157 if self.registers[base + reg as usize].to_boolean() { 1158 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 1159 } 1160 } 1161 Op::JumpIfFalse => { 1162 let reg = Self::read_u8(&mut self.frames[fi]); 1163 let offset = Self::read_i32(&mut self.frames[fi]); 1164 let base = self.frames[fi].base; 1165 if !self.registers[base + reg as usize].to_boolean() { 1166 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 1167 } 1168 } 1169 Op::JumpIfNullish => { 1170 let reg = Self::read_u8(&mut self.frames[fi]); 1171 let offset = Self::read_i32(&mut self.frames[fi]); 1172 let base = self.frames[fi].base; 1173 if self.registers[base + reg as usize].is_nullish() { 1174 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 1175 } 1176 } 1177 1178 // ── Functions / calls ────────────────────────── 1179 Op::Call => { 1180 let dst = Self::read_u8(&mut self.frames[fi]); 1181 let func_r = Self::read_u8(&mut self.frames[fi]); 1182 let args_start = Self::read_u8(&mut self.frames[fi]); 1183 let arg_count = Self::read_u8(&mut self.frames[fi]); 1184 let base = self.frames[fi].base; 1185 1186 // Extract function GcRef. 1187 let func_gc_ref = match self.registers[base + func_r as usize] { 1188 Value::Function(r) => r, 1189 _ => { 1190 let desc = 1191 self.registers[base + func_r as usize].to_js_string(&self.gc); 1192 let err = RuntimeError::type_error(format!("{desc} is not a function")); 1193 let err_val = err.to_value(&mut self.gc); 1194 if !self.handle_exception(err_val) { 1195 return Err(err); 1196 } 1197 continue; 1198 } 1199 }; 1200 1201 // Collect arguments. 1202 let mut args = Vec::with_capacity(arg_count as usize); 1203 for i in 0..arg_count { 1204 args.push(self.registers[base + (args_start + i) as usize].clone()); 1205 } 1206 1207 // Read function data from GC (scoped borrow). 1208 let call_info = { 1209 match self.gc.get(func_gc_ref) { 1210 Some(HeapObject::Function(fdata)) => match &fdata.kind { 1211 FunctionKind::Native(n) => CallInfo::Native(n.callback), 1212 FunctionKind::Bytecode(bc) => { 1213 CallInfo::Bytecode(bc.func.clone(), fdata.upvalues.clone()) 1214 } 1215 }, 1216 _ => { 1217 let err = RuntimeError::type_error("not a function"); 1218 let err_val = err.to_value(&mut self.gc); 1219 if !self.handle_exception(err_val) { 1220 return Err(err); 1221 } 1222 continue; 1223 } 1224 } 1225 }; 1226 1227 match call_info { 1228 CallInfo::Native(callback) => { 1229 let this = self 1230 .globals 1231 .get("this") 1232 .cloned() 1233 .unwrap_or(Value::Undefined); 1234 let mut ctx = NativeContext { 1235 gc: &mut self.gc, 1236 this, 1237 }; 1238 match callback(&args, &mut ctx) { 1239 Ok(val) => { 1240 self.registers[base + dst as usize] = val; 1241 } 1242 Err(err) => { 1243 let err_val = err.to_value(&mut self.gc); 1244 if !self.handle_exception(err_val) { 1245 return Err(err); 1246 } 1247 } 1248 } 1249 } 1250 CallInfo::Bytecode(callee_func, callee_upvalues) => { 1251 if self.frames.len() >= MAX_CALL_DEPTH { 1252 let err = 1253 RuntimeError::range_error("Maximum call stack size exceeded"); 1254 let err_val = err.to_value(&mut self.gc); 1255 if !self.handle_exception(err_val) { 1256 return Err(err); 1257 } 1258 continue; 1259 } 1260 1261 let callee_base = base + self.frames[fi].func.register_count as usize; 1262 let callee_regs = callee_func.register_count as usize; 1263 self.ensure_registers(callee_base + callee_regs); 1264 1265 // Copy arguments into callee's registers. 1266 for i in 0..callee_func.param_count.min(arg_count) { 1267 self.registers[callee_base + i as usize] = args[i as usize].clone(); 1268 } 1269 // Fill remaining params with undefined. 1270 for i in arg_count..callee_func.param_count { 1271 self.registers[callee_base + i as usize] = Value::Undefined; 1272 } 1273 1274 self.frames.push(CallFrame { 1275 func: callee_func, 1276 ip: 0, 1277 base: callee_base, 1278 return_reg: base + dst as usize, 1279 exception_handlers: Vec::new(), 1280 upvalues: callee_upvalues, 1281 }); 1282 } 1283 } 1284 } 1285 Op::Return => { 1286 let reg = Self::read_u8(&mut self.frames[fi]); 1287 let base = self.frames[fi].base; 1288 let val = self.registers[base + reg as usize].clone(); 1289 1290 if self.frames.len() == 1 { 1291 self.frames.pop(); 1292 return Ok(val); 1293 } 1294 1295 let old = self.frames.pop().unwrap(); 1296 self.registers[old.return_reg] = val; 1297 } 1298 Op::Throw => { 1299 let reg = Self::read_u8(&mut self.frames[fi]); 1300 let base = self.frames[fi].base; 1301 let val = self.registers[base + reg as usize].clone(); 1302 1303 if !self.handle_exception(val) { 1304 let msg = self.registers[base + reg as usize].to_js_string(&self.gc); 1305 return Err(RuntimeError { 1306 kind: ErrorKind::Error, 1307 message: msg, 1308 }); 1309 } 1310 } 1311 Op::CreateClosure => { 1312 let dst = Self::read_u8(&mut self.frames[fi]); 1313 let func_idx = Self::read_u16(&mut self.frames[fi]) as usize; 1314 let base = self.frames[fi].base; 1315 let inner_func = self.frames[fi].func.functions[func_idx].clone(); 1316 let name = inner_func.name.clone(); 1317 1318 // Resolve upvalues from the parent scope. 1319 let mut upvalues = Vec::with_capacity(inner_func.upvalue_defs.len()); 1320 for def in &inner_func.upvalue_defs { 1321 let cell_ref = if def.is_local { 1322 // Parent has a cell in register `def.index`. 1323 match &self.registers[base + def.index as usize] { 1324 Value::Object(r) => *r, 1325 _ => { 1326 return Err(RuntimeError { 1327 kind: ErrorKind::Error, 1328 message: 1329 "CreateClosure: upvalue register does not hold a cell" 1330 .into(), 1331 }); 1332 } 1333 } 1334 } else { 1335 // Transitive: parent's own upvalue at `def.index`. 1336 self.frames[fi].upvalues[def.index as usize] 1337 }; 1338 upvalues.push(cell_ref); 1339 } 1340 1341 // Create a .prototype object for the function (for instanceof). 1342 let proto_obj = self.gc.alloc(HeapObject::Object(ObjectData::new())); 1343 let gc_ref = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1344 name, 1345 kind: FunctionKind::Bytecode(BytecodeFunc { func: inner_func }), 1346 prototype_obj: Some(proto_obj), 1347 properties: HashMap::new(), 1348 upvalues, 1349 }))); 1350 // Set .prototype.constructor = this function. 1351 if let Some(HeapObject::Object(data)) = self.gc.get_mut(proto_obj) { 1352 data.properties.insert( 1353 "constructor".to_string(), 1354 Property { 1355 value: Value::Function(gc_ref), 1356 writable: true, 1357 enumerable: false, 1358 configurable: true, 1359 }, 1360 ); 1361 } 1362 self.registers[base + dst as usize] = Value::Function(gc_ref); 1363 1364 // Trigger GC if needed. 1365 if self.gc.should_collect() { 1366 let roots = self.collect_roots(); 1367 self.gc.collect(&roots); 1368 } 1369 } 1370 1371 // ── Object / property ────────────────────────── 1372 Op::GetProperty => { 1373 let dst = Self::read_u8(&mut self.frames[fi]); 1374 let obj_r = Self::read_u8(&mut self.frames[fi]); 1375 let key_r = Self::read_u8(&mut self.frames[fi]); 1376 let base = self.frames[fi].base; 1377 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 1378 let val = match self.registers[base + obj_r as usize] { 1379 Value::Object(gc_ref) | Value::Function(gc_ref) => { 1380 gc_get_property(&self.gc, gc_ref, &key) 1381 } 1382 Value::String(ref s) => { 1383 let v = string_get_property(s, &key); 1384 if matches!(v, Value::Undefined) { 1385 self.string_prototype 1386 .map(|p| gc_get_property(&self.gc, p, &key)) 1387 .unwrap_or(Value::Undefined) 1388 } else { 1389 v 1390 } 1391 } 1392 Value::Number(_) => self 1393 .number_prototype 1394 .map(|p| gc_get_property(&self.gc, p, &key)) 1395 .unwrap_or(Value::Undefined), 1396 Value::Boolean(_) => self 1397 .boolean_prototype 1398 .map(|p| gc_get_property(&self.gc, p, &key)) 1399 .unwrap_or(Value::Undefined), 1400 _ => Value::Undefined, 1401 }; 1402 self.registers[base + dst as usize] = val; 1403 } 1404 Op::SetProperty => { 1405 let obj_r = Self::read_u8(&mut self.frames[fi]); 1406 let key_r = Self::read_u8(&mut self.frames[fi]); 1407 let val_r = Self::read_u8(&mut self.frames[fi]); 1408 let base = self.frames[fi].base; 1409 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 1410 let val = self.registers[base + val_r as usize].clone(); 1411 match self.registers[base + obj_r as usize] { 1412 Value::Object(gc_ref) => { 1413 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 1414 if let Some(prop) = data.properties.get_mut(&key) { 1415 if prop.writable { 1416 prop.value = val; 1417 } 1418 } else { 1419 data.properties.insert(key, Property::data(val)); 1420 } 1421 } 1422 } 1423 Value::Function(gc_ref) => { 1424 if let Some(HeapObject::Function(fdata)) = self.gc.get_mut(gc_ref) { 1425 if let Some(prop) = fdata.properties.get_mut(&key) { 1426 if prop.writable { 1427 prop.value = val; 1428 } 1429 } else { 1430 fdata.properties.insert(key, Property::data(val)); 1431 } 1432 } 1433 } 1434 _ => {} 1435 } 1436 } 1437 Op::CreateObject => { 1438 let dst = Self::read_u8(&mut self.frames[fi]); 1439 let base = self.frames[fi].base; 1440 let mut obj = ObjectData::new(); 1441 obj.prototype = self.object_prototype; 1442 let gc_ref = self.gc.alloc(HeapObject::Object(obj)); 1443 self.registers[base + dst as usize] = Value::Object(gc_ref); 1444 1445 if self.gc.should_collect() { 1446 let roots = self.collect_roots(); 1447 self.gc.collect(&roots); 1448 } 1449 } 1450 Op::CreateArray => { 1451 let dst = Self::read_u8(&mut self.frames[fi]); 1452 let base = self.frames[fi].base; 1453 let mut obj = ObjectData::new(); 1454 obj.prototype = self.array_prototype; 1455 obj.properties.insert( 1456 "length".to_string(), 1457 Property { 1458 value: Value::Number(0.0), 1459 writable: true, 1460 enumerable: false, 1461 configurable: false, 1462 }, 1463 ); 1464 let gc_ref = self.gc.alloc(HeapObject::Object(obj)); 1465 self.registers[base + dst as usize] = Value::Object(gc_ref); 1466 1467 if self.gc.should_collect() { 1468 let roots = self.collect_roots(); 1469 self.gc.collect(&roots); 1470 } 1471 } 1472 Op::GetPropertyByName => { 1473 let dst = Self::read_u8(&mut self.frames[fi]); 1474 let obj_r = Self::read_u8(&mut self.frames[fi]); 1475 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 1476 let base = self.frames[fi].base; 1477 let key = self.frames[fi].func.names[name_idx].clone(); 1478 let val = match self.registers[base + obj_r as usize] { 1479 Value::Object(gc_ref) | Value::Function(gc_ref) => { 1480 gc_get_property(&self.gc, gc_ref, &key) 1481 } 1482 Value::String(ref s) => { 1483 let v = string_get_property(s, &key); 1484 if matches!(v, Value::Undefined) { 1485 self.string_prototype 1486 .map(|p| gc_get_property(&self.gc, p, &key)) 1487 .unwrap_or(Value::Undefined) 1488 } else { 1489 v 1490 } 1491 } 1492 Value::Number(_) => self 1493 .number_prototype 1494 .map(|p| gc_get_property(&self.gc, p, &key)) 1495 .unwrap_or(Value::Undefined), 1496 Value::Boolean(_) => self 1497 .boolean_prototype 1498 .map(|p| gc_get_property(&self.gc, p, &key)) 1499 .unwrap_or(Value::Undefined), 1500 _ => Value::Undefined, 1501 }; 1502 self.registers[base + dst as usize] = val; 1503 } 1504 Op::SetPropertyByName => { 1505 let obj_r = Self::read_u8(&mut self.frames[fi]); 1506 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 1507 let val_r = Self::read_u8(&mut self.frames[fi]); 1508 let base = self.frames[fi].base; 1509 let key = self.frames[fi].func.names[name_idx].clone(); 1510 let val = self.registers[base + val_r as usize].clone(); 1511 match self.registers[base + obj_r as usize] { 1512 Value::Object(gc_ref) => { 1513 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 1514 if let Some(prop) = data.properties.get_mut(&key) { 1515 if prop.writable { 1516 prop.value = val; 1517 } 1518 } else { 1519 data.properties.insert(key, Property::data(val)); 1520 } 1521 } 1522 } 1523 Value::Function(gc_ref) => { 1524 if let Some(HeapObject::Function(fdata)) = self.gc.get_mut(gc_ref) { 1525 if let Some(prop) = fdata.properties.get_mut(&key) { 1526 if prop.writable { 1527 prop.value = val; 1528 } 1529 } else { 1530 fdata.properties.insert(key, Property::data(val)); 1531 } 1532 } 1533 } 1534 _ => {} 1535 } 1536 } 1537 1538 // ── Misc ─────────────────────────────────────── 1539 Op::Delete => { 1540 let dst = Self::read_u8(&mut self.frames[fi]); 1541 let obj_r = Self::read_u8(&mut self.frames[fi]); 1542 let key_r = Self::read_u8(&mut self.frames[fi]); 1543 let base = self.frames[fi].base; 1544 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 1545 let result = 1546 if let Value::Object(gc_ref) = self.registers[base + obj_r as usize] { 1547 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 1548 match data.properties.get(&key) { 1549 Some(prop) if !prop.configurable => false, 1550 Some(_) => { 1551 data.properties.remove(&key); 1552 true 1553 } 1554 None => true, 1555 } 1556 } else { 1557 true 1558 } 1559 } else { 1560 true 1561 }; 1562 self.registers[base + dst as usize] = Value::Boolean(result); 1563 } 1564 Op::ForInInit => { 1565 let dst = Self::read_u8(&mut self.frames[fi]); 1566 let obj_r = Self::read_u8(&mut self.frames[fi]); 1567 let base = self.frames[fi].base; 1568 let keys = match self.registers[base + obj_r as usize] { 1569 Value::Object(gc_ref) => gc_enumerate_keys(&self.gc, gc_ref), 1570 _ => Vec::new(), 1571 }; 1572 // Store keys as an array object. 1573 let mut arr = ObjectData::new(); 1574 for (i, key) in keys.iter().enumerate() { 1575 arr.properties 1576 .insert(i.to_string(), Property::data(Value::String(key.clone()))); 1577 } 1578 arr.properties.insert( 1579 "length".to_string(), 1580 Property { 1581 value: Value::Number(keys.len() as f64), 1582 writable: true, 1583 enumerable: false, 1584 configurable: false, 1585 }, 1586 ); 1587 let gc_ref = self.gc.alloc(HeapObject::Object(arr)); 1588 self.registers[base + dst as usize] = Value::Object(gc_ref); 1589 } 1590 Op::ForInNext => { 1591 let dst_val = Self::read_u8(&mut self.frames[fi]); 1592 let dst_done = Self::read_u8(&mut self.frames[fi]); 1593 let keys_r = Self::read_u8(&mut self.frames[fi]); 1594 let idx_r = Self::read_u8(&mut self.frames[fi]); 1595 let base = self.frames[fi].base; 1596 let idx = self.registers[base + idx_r as usize].to_number() as usize; 1597 let len = match self.registers[base + keys_r as usize] { 1598 Value::Object(gc_ref) => { 1599 gc_get_property(&self.gc, gc_ref, "length").to_number() as usize 1600 } 1601 _ => 0, 1602 }; 1603 if idx >= len { 1604 self.registers[base + dst_done as usize] = Value::Boolean(true); 1605 self.registers[base + dst_val as usize] = Value::Undefined; 1606 } else { 1607 let key_str = idx.to_string(); 1608 let key = match self.registers[base + keys_r as usize] { 1609 Value::Object(gc_ref) => gc_get_property(&self.gc, gc_ref, &key_str), 1610 _ => Value::Undefined, 1611 }; 1612 self.registers[base + dst_val as usize] = key; 1613 self.registers[base + dst_done as usize] = Value::Boolean(false); 1614 } 1615 } 1616 Op::SetPrototype => { 1617 let obj_r = Self::read_u8(&mut self.frames[fi]); 1618 let proto_r = Self::read_u8(&mut self.frames[fi]); 1619 let base = self.frames[fi].base; 1620 let proto = match &self.registers[base + proto_r as usize] { 1621 Value::Object(r) => Some(*r), 1622 Value::Null => None, 1623 _ => None, 1624 }; 1625 if let Value::Object(gc_ref) = self.registers[base + obj_r as usize] { 1626 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 1627 data.prototype = proto; 1628 } 1629 } 1630 } 1631 Op::GetPrototype => { 1632 let dst = Self::read_u8(&mut self.frames[fi]); 1633 let obj_r = Self::read_u8(&mut self.frames[fi]); 1634 let base = self.frames[fi].base; 1635 let proto = match self.registers[base + obj_r as usize] { 1636 Value::Object(gc_ref) => match self.gc.get(gc_ref) { 1637 Some(HeapObject::Object(data)) => { 1638 data.prototype.map(Value::Object).unwrap_or(Value::Null) 1639 } 1640 _ => Value::Null, 1641 }, 1642 _ => Value::Null, 1643 }; 1644 self.registers[base + dst as usize] = proto; 1645 } 1646 1647 // ── Exception handling ───────────────────────────── 1648 Op::PushExceptionHandler => { 1649 let catch_reg = Self::read_u8(&mut self.frames[fi]); 1650 let offset = Self::read_i32(&mut self.frames[fi]); 1651 let catch_ip = (self.frames[fi].ip as i32 + offset) as usize; 1652 self.frames[fi].exception_handlers.push(ExceptionHandler { 1653 catch_ip, 1654 catch_reg, 1655 }); 1656 } 1657 Op::PopExceptionHandler => { 1658 self.frames[fi].exception_handlers.pop(); 1659 } 1660 1661 // ── Closure / upvalue ops ───────────────────────── 1662 Op::NewCell => { 1663 let dst = Self::read_u8(&mut self.frames[fi]); 1664 let base = self.frames[fi].base; 1665 let cell = self.gc.alloc(HeapObject::Cell(Value::Undefined)); 1666 self.registers[base + dst as usize] = Value::Object(cell); 1667 1668 if self.gc.should_collect() { 1669 let roots = self.collect_roots(); 1670 self.gc.collect(&roots); 1671 } 1672 } 1673 Op::CellLoad => { 1674 let dst = Self::read_u8(&mut self.frames[fi]); 1675 let cell_reg = Self::read_u8(&mut self.frames[fi]); 1676 let base = self.frames[fi].base; 1677 let cell_ref = match &self.registers[base + cell_reg as usize] { 1678 Value::Object(r) => *r, 1679 _ => { 1680 return Err(RuntimeError { 1681 kind: ErrorKind::Error, 1682 message: "CellLoad: register does not hold a cell".into(), 1683 }); 1684 } 1685 }; 1686 let val = match self.gc.get(cell_ref) { 1687 Some(HeapObject::Cell(v)) => v.clone(), 1688 _ => Value::Undefined, 1689 }; 1690 self.registers[base + dst as usize] = val; 1691 } 1692 Op::CellStore => { 1693 let cell_reg = Self::read_u8(&mut self.frames[fi]); 1694 let src = Self::read_u8(&mut self.frames[fi]); 1695 let base = self.frames[fi].base; 1696 let cell_ref = match &self.registers[base + cell_reg as usize] { 1697 Value::Object(r) => *r, 1698 _ => { 1699 return Err(RuntimeError { 1700 kind: ErrorKind::Error, 1701 message: "CellStore: register does not hold a cell".into(), 1702 }); 1703 } 1704 }; 1705 let val = self.registers[base + src as usize].clone(); 1706 if let Some(HeapObject::Cell(cell_val)) = self.gc.get_mut(cell_ref) { 1707 *cell_val = val; 1708 } 1709 } 1710 Op::LoadUpvalue => { 1711 let dst = Self::read_u8(&mut self.frames[fi]); 1712 let idx = Self::read_u8(&mut self.frames[fi]) as usize; 1713 let base = self.frames[fi].base; 1714 let cell_ref = self.frames[fi].upvalues[idx]; 1715 let val = match self.gc.get(cell_ref) { 1716 Some(HeapObject::Cell(v)) => v.clone(), 1717 _ => Value::Undefined, 1718 }; 1719 self.registers[base + dst as usize] = val; 1720 } 1721 Op::StoreUpvalue => { 1722 let idx = Self::read_u8(&mut self.frames[fi]) as usize; 1723 let src = Self::read_u8(&mut self.frames[fi]); 1724 let base = self.frames[fi].base; 1725 let val = self.registers[base + src as usize].clone(); 1726 let cell_ref = self.frames[fi].upvalues[idx]; 1727 if let Some(HeapObject::Cell(cell_val)) = self.gc.get_mut(cell_ref) { 1728 *cell_val = val; 1729 } 1730 } 1731 } 1732 } 1733 } 1734 1735 /// Read 3 register operands and return (dst, lhs, rhs, base) as usize indices. 1736 fn read_3reg(&mut self, fi: usize) -> (usize, usize, usize, usize) { 1737 let dst = Self::read_u8(&mut self.frames[fi]) as usize; 1738 let lhs = Self::read_u8(&mut self.frames[fi]) as usize; 1739 let rhs = Self::read_u8(&mut self.frames[fi]) as usize; 1740 let base = self.frames[fi].base; 1741 (dst, lhs, rhs, base) 1742 } 1743 1744 /// Try to find an exception handler on the call stack. 1745 /// Returns true if a handler was found (execution resumes there). 1746 fn handle_exception(&mut self, value: Value) -> bool { 1747 while let Some(frame) = self.frames.last_mut() { 1748 if let Some(handler) = frame.exception_handlers.pop() { 1749 let base = frame.base; 1750 frame.ip = handler.catch_ip; 1751 self.registers[base + handler.catch_reg as usize] = value; 1752 return true; 1753 } 1754 if self.frames.len() == 1 { 1755 break; 1756 } 1757 self.frames.pop(); 1758 } 1759 false 1760 } 1761 1762 /// Register a native function as a global. 1763 pub fn define_native( 1764 &mut self, 1765 name: &str, 1766 callback: fn(&[Value], &mut NativeContext) -> Result<Value, RuntimeError>, 1767 ) { 1768 let gc_ref = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1769 name: name.to_string(), 1770 kind: FunctionKind::Native(NativeFunc { callback }), 1771 prototype_obj: None, 1772 properties: HashMap::new(), 1773 upvalues: Vec::new(), 1774 }))); 1775 self.globals 1776 .insert(name.to_string(), Value::Function(gc_ref)); 1777 } 1778 1779 /// Get a global variable value. 1780 pub fn get_global(&self, name: &str) -> Option<&Value> { 1781 self.globals.get(name) 1782 } 1783 1784 /// Set a global variable. 1785 pub fn set_global(&mut self, name: &str, val: Value) { 1786 self.globals.insert(name.to_string(), val); 1787 } 1788} 1789 1790impl Default for Vm { 1791 fn default() -> Self { 1792 Self::new() 1793 } 1794} 1795 1796/// Internal enum to avoid holding a GC borrow across the call setup. 1797enum CallInfo { 1798 Native(fn(&[Value], &mut NativeContext) -> Result<Value, RuntimeError>), 1799 Bytecode(Function, Vec<GcRef>), 1800} 1801 1802// ── Tests ──────────────────────────────────────────────────── 1803 1804#[cfg(test)] 1805mod tests { 1806 use super::*; 1807 use crate::bytecode::{BytecodeBuilder, Constant, Op}; 1808 use crate::compiler; 1809 use crate::parser::Parser; 1810 1811 /// Helper: compile and execute JS source, return the completion value. 1812 fn eval(source: &str) -> Result<Value, RuntimeError> { 1813 let program = Parser::parse(source).expect("parse failed"); 1814 let func = compiler::compile(&program).expect("compile failed"); 1815 let mut vm = Vm::new(); 1816 vm.execute(&func) 1817 } 1818 1819 // ── Value tests ───────────────────────────────────────── 1820 1821 #[test] 1822 fn test_to_boolean() { 1823 let mut gc: Gc<HeapObject> = Gc::new(); 1824 assert!(!Value::Undefined.to_boolean()); 1825 assert!(!Value::Null.to_boolean()); 1826 assert!(!Value::Boolean(false).to_boolean()); 1827 assert!(Value::Boolean(true).to_boolean()); 1828 assert!(!Value::Number(0.0).to_boolean()); 1829 assert!(!Value::Number(f64::NAN).to_boolean()); 1830 assert!(Value::Number(1.0).to_boolean()); 1831 assert!(!Value::String(String::new()).to_boolean()); 1832 assert!(Value::String("hello".to_string()).to_boolean()); 1833 let obj_ref = gc.alloc(HeapObject::Object(ObjectData::new())); 1834 assert!(Value::Object(obj_ref).to_boolean()); 1835 } 1836 1837 #[test] 1838 fn test_to_number() { 1839 assert!(Value::Undefined.to_number().is_nan()); 1840 assert_eq!(Value::Null.to_number(), 0.0); 1841 assert_eq!(Value::Boolean(true).to_number(), 1.0); 1842 assert_eq!(Value::Boolean(false).to_number(), 0.0); 1843 assert_eq!(Value::Number(42.0).to_number(), 42.0); 1844 assert_eq!(Value::String("42".to_string()).to_number(), 42.0); 1845 assert_eq!(Value::String("".to_string()).to_number(), 0.0); 1846 assert!(Value::String("abc".to_string()).to_number().is_nan()); 1847 } 1848 1849 #[test] 1850 fn test_type_of() { 1851 let mut gc: Gc<HeapObject> = Gc::new(); 1852 assert_eq!(Value::Undefined.type_of(), "undefined"); 1853 assert_eq!(Value::Null.type_of(), "object"); 1854 assert_eq!(Value::Boolean(true).type_of(), "boolean"); 1855 assert_eq!(Value::Number(1.0).type_of(), "number"); 1856 assert_eq!(Value::String("hi".to_string()).type_of(), "string"); 1857 let obj_ref = gc.alloc(HeapObject::Object(ObjectData::new())); 1858 assert_eq!(Value::Object(obj_ref).type_of(), "object"); 1859 } 1860 1861 // ── VM bytecode-level tests ───────────────────────────── 1862 1863 #[test] 1864 fn test_load_const_number() { 1865 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1866 b.func.register_count = 1; 1867 let ci = b.add_constant(Constant::Number(42.0)); 1868 b.emit_reg_u16(Op::LoadConst, 0, ci); 1869 b.emit_reg(Op::Return, 0); 1870 let func = b.finish(); 1871 1872 let mut vm = Vm::new(); 1873 let result = vm.execute(&func).unwrap(); 1874 match result { 1875 Value::Number(n) => assert_eq!(n, 42.0), 1876 _ => panic!("expected Number, got {result:?}"), 1877 } 1878 } 1879 1880 #[test] 1881 fn test_arithmetic_ops() { 1882 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1883 b.func.register_count = 3; 1884 let c10 = b.add_constant(Constant::Number(10.0)); 1885 let c3 = b.add_constant(Constant::Number(3.0)); 1886 b.emit_reg_u16(Op::LoadConst, 0, c10); 1887 b.emit_reg_u16(Op::LoadConst, 1, c3); 1888 b.emit_reg3(Op::Add, 2, 0, 1); 1889 b.emit_reg(Op::Return, 2); 1890 let func = b.finish(); 1891 1892 let mut vm = Vm::new(); 1893 match vm.execute(&func).unwrap() { 1894 Value::Number(n) => assert_eq!(n, 13.0), 1895 v => panic!("expected 13, got {v:?}"), 1896 } 1897 } 1898 1899 #[test] 1900 fn test_string_concat() { 1901 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1902 b.func.register_count = 3; 1903 let c1 = b.add_constant(Constant::String("hello".into())); 1904 let c2 = b.add_constant(Constant::String(" world".into())); 1905 b.emit_reg_u16(Op::LoadConst, 0, c1); 1906 b.emit_reg_u16(Op::LoadConst, 1, c2); 1907 b.emit_reg3(Op::Add, 2, 0, 1); 1908 b.emit_reg(Op::Return, 2); 1909 let func = b.finish(); 1910 1911 let mut vm = Vm::new(); 1912 match vm.execute(&func).unwrap() { 1913 Value::String(s) => assert_eq!(s, "hello world"), 1914 v => panic!("expected string, got {v:?}"), 1915 } 1916 } 1917 1918 #[test] 1919 fn test_jump_if_false() { 1920 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1921 b.func.register_count = 2; 1922 b.emit_reg(Op::LoadFalse, 0); 1923 let patch = b.emit_cond_jump(Op::JumpIfFalse, 0); 1924 b.emit_load_int8(1, 1); 1925 let skip = b.emit_jump(Op::Jump); 1926 b.patch_jump(patch); 1927 b.emit_load_int8(1, 2); 1928 b.patch_jump(skip); 1929 b.emit_reg(Op::Return, 1); 1930 let func = b.finish(); 1931 1932 let mut vm = Vm::new(); 1933 match vm.execute(&func).unwrap() { 1934 Value::Number(n) => assert_eq!(n, 2.0), 1935 v => panic!("expected 2, got {v:?}"), 1936 } 1937 } 1938 1939 #[test] 1940 fn test_globals() { 1941 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1942 b.func.register_count = 2; 1943 let name = b.add_name("x"); 1944 b.emit_load_int8(0, 42); 1945 b.emit_store_global(name, 0); 1946 b.emit_load_global(1, name); 1947 b.emit_reg(Op::Return, 1); 1948 let func = b.finish(); 1949 1950 let mut vm = Vm::new(); 1951 match vm.execute(&func).unwrap() { 1952 Value::Number(n) => assert_eq!(n, 42.0), 1953 v => panic!("expected 42, got {v:?}"), 1954 } 1955 } 1956 1957 #[test] 1958 fn test_function_call() { 1959 let mut inner_b = BytecodeBuilder::new("add1".into(), 1); 1960 inner_b.func.register_count = 2; 1961 inner_b.emit_load_int8(1, 1); 1962 inner_b.emit_reg3(Op::Add, 0, 0, 1); 1963 inner_b.emit_reg(Op::Return, 0); 1964 let inner = inner_b.finish(); 1965 1966 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1967 b.func.register_count = 4; 1968 let fi = b.add_function(inner); 1969 b.emit_reg_u16(Op::CreateClosure, 0, fi); 1970 b.emit_load_int8(1, 10); 1971 b.emit_call(2, 0, 1, 1); 1972 b.emit_reg(Op::Return, 2); 1973 let func = b.finish(); 1974 1975 let mut vm = Vm::new(); 1976 match vm.execute(&func).unwrap() { 1977 Value::Number(n) => assert_eq!(n, 11.0), 1978 v => panic!("expected 11, got {v:?}"), 1979 } 1980 } 1981 1982 #[test] 1983 fn test_native_function() { 1984 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1985 b.func.register_count = 3; 1986 let name = b.add_name("double"); 1987 b.emit_load_global(0, name); 1988 b.emit_load_int8(1, 21); 1989 b.emit_call(2, 0, 1, 1); 1990 b.emit_reg(Op::Return, 2); 1991 let func = b.finish(); 1992 1993 let mut vm = Vm::new(); 1994 vm.define_native("double", |args, _ctx| { 1995 let n = args.first().unwrap_or(&Value::Undefined).to_number(); 1996 Ok(Value::Number(n * 2.0)) 1997 }); 1998 match vm.execute(&func).unwrap() { 1999 Value::Number(n) => assert_eq!(n, 42.0), 2000 v => panic!("expected 42, got {v:?}"), 2001 } 2002 } 2003 2004 #[test] 2005 fn test_object_property() { 2006 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2007 b.func.register_count = 3; 2008 let name = b.add_name("x"); 2009 b.emit_reg(Op::CreateObject, 0); 2010 b.emit_load_int8(1, 42); 2011 b.emit_set_prop_name(0, name, 1); 2012 b.emit_get_prop_name(2, 0, name); 2013 b.emit_reg(Op::Return, 2); 2014 let func = b.finish(); 2015 2016 let mut vm = Vm::new(); 2017 match vm.execute(&func).unwrap() { 2018 Value::Number(n) => assert_eq!(n, 42.0), 2019 v => panic!("expected 42, got {v:?}"), 2020 } 2021 } 2022 2023 #[test] 2024 fn test_typeof_operator() { 2025 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2026 b.func.register_count = 2; 2027 b.emit_load_int8(0, 5); 2028 b.emit_reg_reg(Op::TypeOf, 1, 0); 2029 b.emit_reg(Op::Return, 1); 2030 let func = b.finish(); 2031 2032 let mut vm = Vm::new(); 2033 match vm.execute(&func).unwrap() { 2034 Value::String(s) => assert_eq!(s, "number"), 2035 v => panic!("expected 'number', got {v:?}"), 2036 } 2037 } 2038 2039 #[test] 2040 fn test_comparison() { 2041 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2042 b.func.register_count = 3; 2043 b.emit_load_int8(0, 5); 2044 b.emit_load_int8(1, 10); 2045 b.emit_reg3(Op::LessThan, 2, 0, 1); 2046 b.emit_reg(Op::Return, 2); 2047 let func = b.finish(); 2048 2049 let mut vm = Vm::new(); 2050 match vm.execute(&func).unwrap() { 2051 Value::Boolean(b) => assert!(b), 2052 v => panic!("expected true, got {v:?}"), 2053 } 2054 } 2055 2056 #[test] 2057 fn test_abstract_equality() { 2058 assert!(abstract_eq(&Value::Null, &Value::Undefined)); 2059 assert!(abstract_eq(&Value::Undefined, &Value::Null)); 2060 assert!(abstract_eq( 2061 &Value::Number(1.0), 2062 &Value::String("1".to_string()) 2063 )); 2064 assert!(abstract_eq(&Value::Boolean(true), &Value::Number(1.0))); 2065 assert!(!abstract_eq(&Value::Number(0.0), &Value::Null)); 2066 } 2067 2068 #[test] 2069 fn test_strict_equality() { 2070 assert!(strict_eq(&Value::Number(1.0), &Value::Number(1.0))); 2071 assert!(!strict_eq( 2072 &Value::Number(1.0), 2073 &Value::String("1".to_string()) 2074 )); 2075 assert!(strict_eq(&Value::Null, &Value::Null)); 2076 assert!(!strict_eq(&Value::Null, &Value::Undefined)); 2077 } 2078 2079 #[test] 2080 fn test_bitwise_ops() { 2081 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2082 b.func.register_count = 3; 2083 b.emit_load_int8(0, 0x0F); 2084 b.emit_load_int8(1, 0x03); 2085 b.emit_reg3(Op::BitAnd, 2, 0, 1); 2086 b.emit_reg(Op::Return, 2); 2087 let func = b.finish(); 2088 2089 let mut vm = Vm::new(); 2090 match vm.execute(&func).unwrap() { 2091 Value::Number(n) => assert_eq!(n, 3.0), 2092 v => panic!("expected 3, got {v:?}"), 2093 } 2094 } 2095 2096 #[test] 2097 fn test_throw_uncaught() { 2098 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2099 b.func.register_count = 1; 2100 let ci = b.add_constant(Constant::String("oops".into())); 2101 b.emit_reg_u16(Op::LoadConst, 0, ci); 2102 b.emit_reg(Op::Throw, 0); 2103 let func = b.finish(); 2104 2105 let mut vm = Vm::new(); 2106 let err = vm.execute(&func).unwrap_err(); 2107 assert_eq!(err.message, "oops"); 2108 } 2109 2110 #[test] 2111 fn test_loop_counting() { 2112 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2113 b.func.register_count = 3; 2114 b.emit_load_int8(0, 0); 2115 b.emit_load_int8(1, 5); 2116 let loop_start = b.offset(); 2117 b.emit_reg3(Op::LessThan, 2, 0, 1); 2118 let exit_patch = b.emit_cond_jump(Op::JumpIfFalse, 2); 2119 b.emit_load_int8(2, 1); 2120 b.emit_reg3(Op::Add, 0, 0, 2); 2121 b.emit_jump_to(loop_start); 2122 b.patch_jump(exit_patch); 2123 b.emit_reg(Op::Return, 0); 2124 let func = b.finish(); 2125 2126 let mut vm = Vm::new(); 2127 match vm.execute(&func).unwrap() { 2128 Value::Number(n) => assert_eq!(n, 5.0), 2129 v => panic!("expected 5, got {v:?}"), 2130 } 2131 } 2132 2133 // ── End-to-end (compile + execute) tests ──────────────── 2134 2135 #[test] 2136 fn test_e2e_arithmetic() { 2137 match eval("2 + 3 * 4").unwrap() { 2138 Value::Number(n) => assert_eq!(n, 14.0), 2139 v => panic!("expected 14, got {v:?}"), 2140 } 2141 } 2142 2143 #[test] 2144 fn test_e2e_variables() { 2145 match eval("var x = 10; var y = 20; x + y").unwrap() { 2146 Value::Number(n) => assert_eq!(n, 30.0), 2147 v => panic!("expected 30, got {v:?}"), 2148 } 2149 } 2150 2151 #[test] 2152 fn test_e2e_if_else() { 2153 match eval("var x = 5; if (x > 3) { x = 100; } x").unwrap() { 2154 Value::Number(n) => assert_eq!(n, 100.0), 2155 v => panic!("expected 100, got {v:?}"), 2156 } 2157 } 2158 2159 #[test] 2160 fn test_e2e_while_loop() { 2161 match eval("var i = 0; var sum = 0; while (i < 10) { sum = sum + i; i = i + 1; } sum") 2162 .unwrap() 2163 { 2164 Value::Number(n) => assert_eq!(n, 45.0), 2165 v => panic!("expected 45, got {v:?}"), 2166 } 2167 } 2168 2169 #[test] 2170 fn test_e2e_function_call() { 2171 match eval("function add(a, b) { return a + b; } add(3, 4)").unwrap() { 2172 Value::Number(n) => assert_eq!(n, 7.0), 2173 v => panic!("expected 7, got {v:?}"), 2174 } 2175 } 2176 2177 #[test] 2178 fn test_e2e_recursive_factorial() { 2179 let src = r#" 2180 function fact(n) { 2181 if (n <= 1) return 1; 2182 return n * fact(n - 1); 2183 } 2184 fact(6) 2185 "#; 2186 match eval(src).unwrap() { 2187 Value::Number(n) => assert_eq!(n, 720.0), 2188 v => panic!("expected 720, got {v:?}"), 2189 } 2190 } 2191 2192 #[test] 2193 fn test_e2e_string_concat() { 2194 match eval("'hello' + ' ' + 'world'").unwrap() { 2195 Value::String(s) => assert_eq!(s, "hello world"), 2196 v => panic!("expected 'hello world', got {v:?}"), 2197 } 2198 } 2199 2200 #[test] 2201 fn test_e2e_comparison_coercion() { 2202 match eval("1 == '1'").unwrap() { 2203 Value::Boolean(b) => assert!(b), 2204 v => panic!("expected true, got {v:?}"), 2205 } 2206 match eval("1 === '1'").unwrap() { 2207 Value::Boolean(b) => assert!(!b), 2208 v => panic!("expected false, got {v:?}"), 2209 } 2210 } 2211 2212 #[test] 2213 fn test_e2e_typeof() { 2214 match eval("typeof 42").unwrap() { 2215 Value::String(s) => assert_eq!(s, "number"), 2216 v => panic!("expected 'number', got {v:?}"), 2217 } 2218 match eval("typeof 'hello'").unwrap() { 2219 Value::String(s) => assert_eq!(s, "string"), 2220 v => panic!("expected 'string', got {v:?}"), 2221 } 2222 } 2223 2224 #[test] 2225 fn test_e2e_object_literal() { 2226 match eval("var o = { x: 10, y: 20 }; o.x + o.y").unwrap() { 2227 Value::Number(n) => assert_eq!(n, 30.0), 2228 v => panic!("expected 30, got {v:?}"), 2229 } 2230 } 2231 2232 #[test] 2233 fn test_e2e_for_loop() { 2234 match eval("var s = 0; for (var i = 0; i < 5; i = i + 1) { s = s + i; } s").unwrap() { 2235 Value::Number(n) => assert_eq!(n, 10.0), 2236 v => panic!("expected 10, got {v:?}"), 2237 } 2238 } 2239 2240 #[test] 2241 fn test_e2e_nested_functions() { 2242 // Note: closures (capturing parent scope vars) not yet supported. 2243 // This test verifies nested function declarations and calls work. 2244 let src = r#" 2245 function outer(x) { 2246 function inner(y) { 2247 return y + 1; 2248 } 2249 return inner(x); 2250 } 2251 outer(5) 2252 "#; 2253 match eval(src).unwrap() { 2254 Value::Number(n) => assert_eq!(n, 6.0), 2255 v => panic!("expected 6, got {v:?}"), 2256 } 2257 } 2258 2259 #[test] 2260 fn test_e2e_logical_operators() { 2261 match eval("true && false").unwrap() { 2262 Value::Boolean(b) => assert!(!b), 2263 v => panic!("expected false, got {v:?}"), 2264 } 2265 match eval("false || true").unwrap() { 2266 Value::Boolean(b) => assert!(b), 2267 v => panic!("expected true, got {v:?}"), 2268 } 2269 } 2270 2271 #[test] 2272 fn test_e2e_unary_neg() { 2273 match eval("-(5 + 3)").unwrap() { 2274 Value::Number(n) => assert_eq!(n, -8.0), 2275 v => panic!("expected -8, got {v:?}"), 2276 } 2277 } 2278 2279 #[test] 2280 fn test_e2e_ternary() { 2281 match eval("true ? 1 : 2").unwrap() { 2282 Value::Number(n) => assert_eq!(n, 1.0), 2283 v => panic!("expected 1, got {v:?}"), 2284 } 2285 match eval("false ? 1 : 2").unwrap() { 2286 Value::Number(n) => assert_eq!(n, 2.0), 2287 v => panic!("expected 2, got {v:?}"), 2288 } 2289 } 2290 2291 #[test] 2292 fn test_e2e_fibonacci() { 2293 let src = r#" 2294 function fib(n) { 2295 if (n <= 1) return n; 2296 return fib(n - 1) + fib(n - 2); 2297 } 2298 fib(10) 2299 "#; 2300 match eval(src).unwrap() { 2301 Value::Number(n) => assert_eq!(n, 55.0), 2302 v => panic!("expected 55, got {v:?}"), 2303 } 2304 } 2305 2306 // ── GC integration tests ──────────────────────────────── 2307 2308 #[test] 2309 fn test_gc_object_survives_collection() { 2310 let src = r#" 2311 var o = { x: 42 }; 2312 o.x 2313 "#; 2314 match eval(src).unwrap() { 2315 Value::Number(n) => assert_eq!(n, 42.0), 2316 v => panic!("expected 42, got {v:?}"), 2317 } 2318 } 2319 2320 #[test] 2321 fn test_gc_many_objects() { 2322 // Allocate many objects to trigger GC threshold. 2323 let src = r#" 2324 var sum = 0; 2325 var i = 0; 2326 while (i < 100) { 2327 var o = { val: i }; 2328 sum = sum + o.val; 2329 i = i + 1; 2330 } 2331 sum 2332 "#; 2333 match eval(src).unwrap() { 2334 Value::Number(n) => assert_eq!(n, 4950.0), 2335 v => panic!("expected 4950, got {v:?}"), 2336 } 2337 } 2338 2339 #[test] 2340 fn test_gc_reference_identity() { 2341 // With GC, object assignment is by reference. 2342 let mut gc: Gc<HeapObject> = Gc::new(); 2343 let r = gc.alloc(HeapObject::Object(ObjectData::new())); 2344 let a = Value::Object(r); 2345 let b = a.clone(); 2346 assert!(strict_eq(&a, &b)); // Same GcRef → strict equal. 2347 } 2348 2349 // ── Object model tests ────────────────────────────────── 2350 2351 #[test] 2352 fn test_prototype_chain_lookup() { 2353 // Property lookup walks the prototype chain. 2354 let src = r#" 2355 function Animal() {} 2356 var a = {}; 2357 a.sound = "woof"; 2358 a.sound 2359 "#; 2360 match eval(src).unwrap() { 2361 Value::String(s) => assert_eq!(s, "woof"), 2362 v => panic!("expected 'woof', got {v:?}"), 2363 } 2364 } 2365 2366 #[test] 2367 fn test_typeof_all_types() { 2368 // typeof returns correct strings for all types. 2369 let cases = [ 2370 ("typeof undefined", "undefined"), 2371 ("typeof null", "object"), 2372 ("typeof true", "boolean"), 2373 ("typeof 42", "number"), 2374 ("typeof 'hello'", "string"), 2375 ("typeof {}", "object"), 2376 ("typeof function(){}", "function"), 2377 ]; 2378 for (src, expected) in cases { 2379 match eval(src).unwrap() { 2380 Value::String(s) => assert_eq!(s, expected, "typeof failed for: {src}"), 2381 v => panic!("expected string for {src}, got {v:?}"), 2382 } 2383 } 2384 } 2385 2386 #[test] 2387 fn test_instanceof_basic() { 2388 let src = r#" 2389 function Foo() {} 2390 var f = {}; 2391 f instanceof Foo 2392 "#; 2393 // Plain object without prototype link → false 2394 match eval(src).unwrap() { 2395 Value::Boolean(b) => assert!(!b), 2396 v => panic!("expected false, got {v:?}"), 2397 } 2398 } 2399 2400 #[test] 2401 fn test_in_operator() { 2402 let src = r#" 2403 var o = { x: 1, y: 2 }; 2404 var r1 = "x" in o; 2405 var r2 = "z" in o; 2406 r1 === true && r2 === false 2407 "#; 2408 match eval(src).unwrap() { 2409 Value::Boolean(b) => assert!(b), 2410 v => panic!("expected true, got {v:?}"), 2411 } 2412 } 2413 2414 #[test] 2415 fn test_delete_property() { 2416 let src = r#" 2417 var o = { x: 1, y: 2 }; 2418 delete o.x; 2419 typeof o.x === "undefined" && o.y === 2 2420 "#; 2421 match eval(src).unwrap() { 2422 Value::Boolean(b) => assert!(b), 2423 v => panic!("expected true, got {v:?}"), 2424 } 2425 } 2426 2427 #[test] 2428 fn test_delete_computed_property() { 2429 let src = r#" 2430 var o = { a: 10, b: 20 }; 2431 var key = "a"; 2432 delete o[key]; 2433 typeof o.a === "undefined" && o.b === 20 2434 "#; 2435 match eval(src).unwrap() { 2436 Value::Boolean(b) => assert!(b), 2437 v => panic!("expected true, got {v:?}"), 2438 } 2439 } 2440 2441 #[test] 2442 fn test_delete_non_configurable() { 2443 // Array length is non-configurable, delete should return false. 2444 let mut gc: Gc<HeapObject> = Gc::new(); 2445 let mut obj = ObjectData::new(); 2446 obj.properties.insert( 2447 "x".to_string(), 2448 Property { 2449 value: Value::Number(1.0), 2450 writable: true, 2451 enumerable: true, 2452 configurable: false, 2453 }, 2454 ); 2455 let obj_ref = gc.alloc(HeapObject::Object(obj)); 2456 2457 // Try to delete the non-configurable property. 2458 match gc.get_mut(obj_ref) { 2459 Some(HeapObject::Object(data)) => { 2460 let prop = data.properties.get("x").unwrap(); 2461 assert!(!prop.configurable); 2462 // The property should still be there. 2463 assert!(data.properties.contains_key("x")); 2464 } 2465 _ => panic!("expected object"), 2466 } 2467 } 2468 2469 #[test] 2470 fn test_property_writable_flag() { 2471 // Setting a non-writable property should silently fail. 2472 let mut gc: Gc<HeapObject> = Gc::new(); 2473 let mut obj = ObjectData::new(); 2474 obj.properties.insert( 2475 "frozen".to_string(), 2476 Property { 2477 value: Value::Number(42.0), 2478 writable: false, 2479 enumerable: true, 2480 configurable: false, 2481 }, 2482 ); 2483 let obj_ref = gc.alloc(HeapObject::Object(obj)); 2484 2485 // Verify the property value. 2486 match gc.get(obj_ref) { 2487 Some(HeapObject::Object(data)) => { 2488 assert_eq!( 2489 data.properties.get("frozen").unwrap().value.to_number(), 2490 42.0 2491 ); 2492 } 2493 _ => panic!("expected object"), 2494 } 2495 } 2496 2497 #[test] 2498 fn test_for_in_basic() { 2499 let src = r#" 2500 var o = { a: 1, b: 2, c: 3 }; 2501 var sum = 0; 2502 for (var key in o) { 2503 sum = sum + o[key]; 2504 } 2505 sum 2506 "#; 2507 match eval(src).unwrap() { 2508 Value::Number(n) => assert_eq!(n, 6.0), 2509 v => panic!("expected 6, got {v:?}"), 2510 } 2511 } 2512 2513 #[test] 2514 fn test_for_in_collects_keys() { 2515 let src = r#" 2516 var o = { x: 10, y: 20 }; 2517 var keys = ""; 2518 for (var k in o) { 2519 keys = keys + k + ","; 2520 } 2521 keys 2522 "#; 2523 match eval(src).unwrap() { 2524 Value::String(s) => { 2525 // Both keys should appear (order may vary with HashMap). 2526 assert!(s.contains("x,")); 2527 assert!(s.contains("y,")); 2528 } 2529 v => panic!("expected string, got {v:?}"), 2530 } 2531 } 2532 2533 #[test] 2534 fn test_for_in_empty_object() { 2535 let src = r#" 2536 var o = {}; 2537 var count = 0; 2538 for (var k in o) { 2539 count = count + 1; 2540 } 2541 count 2542 "#; 2543 match eval(src).unwrap() { 2544 Value::Number(n) => assert_eq!(n, 0.0), 2545 v => panic!("expected 0, got {v:?}"), 2546 } 2547 } 2548 2549 #[test] 2550 fn test_property_enumerable_flag() { 2551 // Array "length" is non-enumerable, should not appear in for-in. 2552 let src = r#" 2553 var arr = [10, 20, 30]; 2554 var keys = ""; 2555 for (var k in arr) { 2556 keys = keys + k + ","; 2557 } 2558 keys 2559 "#; 2560 match eval(src).unwrap() { 2561 Value::String(s) => { 2562 // "length" should NOT be in the keys (it's non-enumerable). 2563 assert!(!s.contains("length")); 2564 // Array indices should be present. 2565 assert!(s.contains("0,")); 2566 assert!(s.contains("1,")); 2567 assert!(s.contains("2,")); 2568 } 2569 v => panic!("expected string, got {v:?}"), 2570 } 2571 } 2572 2573 #[test] 2574 fn test_object_reference_semantics() { 2575 // Objects have reference semantics (shared via GcRef). 2576 let src = r#" 2577 var a = { x: 1 }; 2578 var b = a; 2579 b.x = 42; 2580 a.x 2581 "#; 2582 match eval(src).unwrap() { 2583 Value::Number(n) => assert_eq!(n, 42.0), 2584 v => panic!("expected 42, got {v:?}"), 2585 } 2586 } 2587 2588 #[test] 2589 fn test_gc_enumerate_keys_order() { 2590 // Integer keys should come first, sorted numerically. 2591 let mut gc: Gc<HeapObject> = Gc::new(); 2592 let mut obj = ObjectData::new(); 2593 obj.properties 2594 .insert("b".to_string(), Property::data(Value::Number(2.0))); 2595 obj.properties 2596 .insert("2".to_string(), Property::data(Value::Number(3.0))); 2597 obj.properties 2598 .insert("a".to_string(), Property::data(Value::Number(1.0))); 2599 obj.properties 2600 .insert("0".to_string(), Property::data(Value::Number(0.0))); 2601 let obj_ref = gc.alloc(HeapObject::Object(obj)); 2602 2603 let keys = gc_enumerate_keys(&gc, obj_ref); 2604 // Integer keys first (sorted), then string keys. 2605 let int_part: Vec<&str> = keys.iter().take(2).map(|s| s.as_str()).collect(); 2606 assert_eq!(int_part, vec!["0", "2"]); 2607 // Remaining keys are string keys (order depends on HashMap iteration). 2608 let str_part: Vec<&str> = keys.iter().skip(2).map(|s| s.as_str()).collect(); 2609 assert!(str_part.contains(&"a")); 2610 assert!(str_part.contains(&"b")); 2611 } 2612 2613 #[test] 2614 fn test_instanceof_with_gc() { 2615 // Direct test of gc_instanceof. 2616 let mut gc: Gc<HeapObject> = Gc::new(); 2617 2618 // Create a constructor function with a .prototype object. 2619 let proto = gc.alloc(HeapObject::Object(ObjectData::new())); 2620 let ctor = gc.alloc(HeapObject::Function(Box::new(FunctionData { 2621 name: "Foo".to_string(), 2622 kind: FunctionKind::Native(NativeFunc { 2623 callback: |_, _ctx| Ok(Value::Undefined), 2624 }), 2625 prototype_obj: Some(proto), 2626 properties: HashMap::new(), 2627 upvalues: Vec::new(), 2628 }))); 2629 2630 // Create an object whose [[Prototype]] is the constructor's .prototype. 2631 let mut obj_data = ObjectData::new(); 2632 obj_data.prototype = Some(proto); 2633 let obj = gc.alloc(HeapObject::Object(obj_data)); 2634 2635 assert!(gc_instanceof(&gc, obj, ctor)); 2636 2637 // An unrelated object should not match. 2638 let other = gc.alloc(HeapObject::Object(ObjectData::new())); 2639 assert!(!gc_instanceof(&gc, other, ctor)); 2640 } 2641 2642 #[test] 2643 fn test_try_catch_basic() { 2644 // Simple try/catch should catch a thrown value. 2645 let src = r#" 2646 var caught = false; 2647 try { throw "err"; } catch (e) { caught = true; } 2648 caught 2649 "#; 2650 match eval(src).unwrap() { 2651 Value::Boolean(true) => {} 2652 v => panic!("expected true, got {v:?}"), 2653 } 2654 } 2655 2656 #[test] 2657 fn test_try_catch_nested_call() { 2658 // try/catch should catch errors thrown from called functions. 2659 let src = r#" 2660 function thrower() { throw "err"; } 2661 var caught = false; 2662 try { thrower(); } catch (e) { caught = true; } 2663 caught 2664 "#; 2665 match eval(src).unwrap() { 2666 Value::Boolean(true) => {} 2667 v => panic!("expected true, got {v:?}"), 2668 } 2669 } 2670 2671 // ── Closure tests ──────────────────────────────────────── 2672 2673 #[test] 2674 fn test_closure_basic() { 2675 // Basic closure: inner function reads outer variable. 2676 let src = r#" 2677 function outer() { 2678 var x = 10; 2679 function inner() { 2680 return x; 2681 } 2682 return inner(); 2683 } 2684 outer() 2685 "#; 2686 match eval(src).unwrap() { 2687 Value::Number(n) => assert_eq!(n, 10.0), 2688 v => panic!("expected 10, got {v:?}"), 2689 } 2690 } 2691 2692 #[test] 2693 fn test_closure_return_function() { 2694 // Closure survives the outer function's return. 2695 let src = r#" 2696 function makeAdder(x) { 2697 return function(y) { return x + y; }; 2698 } 2699 var add5 = makeAdder(5); 2700 add5(3) 2701 "#; 2702 match eval(src).unwrap() { 2703 Value::Number(n) => assert_eq!(n, 8.0), 2704 v => panic!("expected 8, got {v:?}"), 2705 } 2706 } 2707 2708 #[test] 2709 fn test_closure_mutation() { 2710 // Closures share live references — mutation is visible. 2711 let src = r#" 2712 function counter() { 2713 var n = 0; 2714 return function() { n = n + 1; return n; }; 2715 } 2716 var c = counter(); 2717 c(); 2718 c(); 2719 c() 2720 "#; 2721 match eval(src).unwrap() { 2722 Value::Number(n) => assert_eq!(n, 3.0), 2723 v => panic!("expected 3, got {v:?}"), 2724 } 2725 } 2726 2727 #[test] 2728 fn test_closure_shared_variable() { 2729 // Two closures from the same scope share the same variable. 2730 let src = r#" 2731 function make() { 2732 var x = 0; 2733 function inc() { x = x + 1; } 2734 function get() { return x; } 2735 inc(); 2736 inc(); 2737 return get(); 2738 } 2739 make() 2740 "#; 2741 match eval(src).unwrap() { 2742 Value::Number(n) => assert_eq!(n, 2.0), 2743 v => panic!("expected 2, got {v:?}"), 2744 } 2745 } 2746 2747 #[test] 2748 fn test_closure_arrow() { 2749 // Arrow function captures outer variable. 2750 let src = r#" 2751 function outer() { 2752 var x = 42; 2753 var f = () => x; 2754 return f(); 2755 } 2756 outer() 2757 "#; 2758 match eval(src).unwrap() { 2759 Value::Number(n) => assert_eq!(n, 42.0), 2760 v => panic!("expected 42, got {v:?}"), 2761 } 2762 } 2763 2764 #[test] 2765 fn test_closure_nested() { 2766 // Transitive capture: grandchild function reads grandparent variable. 2767 let src = r#" 2768 function outer() { 2769 var x = 100; 2770 function middle() { 2771 function inner() { 2772 return x; 2773 } 2774 return inner(); 2775 } 2776 return middle(); 2777 } 2778 outer() 2779 "#; 2780 match eval(src).unwrap() { 2781 Value::Number(n) => assert_eq!(n, 100.0), 2782 v => panic!("expected 100, got {v:?}"), 2783 } 2784 } 2785 2786 #[test] 2787 fn test_closure_param_capture() { 2788 // Closure captures a function parameter. 2789 let src = r#" 2790 function multiply(factor) { 2791 return function(x) { return x * factor; }; 2792 } 2793 var double = multiply(2); 2794 double(7) 2795 "#; 2796 match eval(src).unwrap() { 2797 Value::Number(n) => assert_eq!(n, 14.0), 2798 v => panic!("expected 14, got {v:?}"), 2799 } 2800 } 2801 2802 // ── const tests ────────────────────────────────────────── 2803 2804 #[test] 2805 fn test_const_basic() { 2806 let src = "const x = 42; x"; 2807 match eval(src).unwrap() { 2808 Value::Number(n) => assert_eq!(n, 42.0), 2809 v => panic!("expected 42, got {v:?}"), 2810 } 2811 } 2812 2813 #[test] 2814 fn test_const_reassignment_error() { 2815 let src = "const x = 1; x = 2;"; 2816 let program = crate::parser::Parser::parse(src).expect("parse ok"); 2817 let result = crate::compiler::compile(&program); 2818 assert!( 2819 result.is_err(), 2820 "const reassignment should be a compile error" 2821 ); 2822 } 2823 2824 #[test] 2825 fn test_const_missing_init_error() { 2826 let src = "const x;"; 2827 let program = crate::parser::Parser::parse(src).expect("parse ok"); 2828 let result = crate::compiler::compile(&program); 2829 assert!( 2830 result.is_err(), 2831 "const without initializer should be a compile error" 2832 ); 2833 } 2834 2835 // ── this binding tests ─────────────────────────────────── 2836 2837 #[test] 2838 fn test_method_call_this() { 2839 let src = r#" 2840 var obj = {}; 2841 obj.x = 10; 2842 obj.getX = function() { return this.x; }; 2843 obj.getX() 2844 "#; 2845 match eval(src).unwrap() { 2846 Value::Number(n) => assert_eq!(n, 10.0), 2847 v => panic!("expected 10, got {v:?}"), 2848 } 2849 } 2850 2851 // ── Object built-in tests ──────────────────────────────── 2852 2853 #[test] 2854 fn test_object_keys() { 2855 let src = r#" 2856 var obj = {}; 2857 obj.a = 1; 2858 obj.b = 2; 2859 obj.c = 3; 2860 var k = Object.keys(obj); 2861 k.length 2862 "#; 2863 match eval(src).unwrap() { 2864 Value::Number(n) => assert_eq!(n, 3.0), 2865 v => panic!("expected 3, got {v:?}"), 2866 } 2867 } 2868 2869 #[test] 2870 fn test_object_values() { 2871 let src = r#" 2872 var obj = {}; 2873 obj.x = 10; 2874 var v = Object.values(obj); 2875 v[0] 2876 "#; 2877 match eval(src).unwrap() { 2878 Value::Number(n) => assert_eq!(n, 10.0), 2879 v => panic!("expected 10, got {v:?}"), 2880 } 2881 } 2882 2883 #[test] 2884 fn test_object_entries() { 2885 let src = r#" 2886 var obj = {}; 2887 obj.x = 42; 2888 var e = Object.entries(obj); 2889 e[0][1] 2890 "#; 2891 match eval(src).unwrap() { 2892 Value::Number(n) => assert_eq!(n, 42.0), 2893 v => panic!("expected 42, got {v:?}"), 2894 } 2895 } 2896 2897 #[test] 2898 fn test_object_assign() { 2899 let src = r#" 2900 var a = {}; 2901 a.x = 1; 2902 var b = {}; 2903 b.y = 2; 2904 var c = Object.assign(a, b); 2905 c.y 2906 "#; 2907 match eval(src).unwrap() { 2908 Value::Number(n) => assert_eq!(n, 2.0), 2909 v => panic!("expected 2, got {v:?}"), 2910 } 2911 } 2912 2913 #[test] 2914 fn test_object_create() { 2915 let src = r#" 2916 var proto = {}; 2917 proto.greet = "hello"; 2918 var child = Object.create(proto); 2919 child.greet 2920 "#; 2921 match eval(src).unwrap() { 2922 Value::String(s) => assert_eq!(s, "hello"), 2923 v => panic!("expected 'hello', got {v:?}"), 2924 } 2925 } 2926 2927 #[test] 2928 fn test_object_is() { 2929 let src = "Object.is(NaN, NaN)"; 2930 match eval(src).unwrap() { 2931 Value::Boolean(b) => assert!(b), 2932 v => panic!("expected true, got {v:?}"), 2933 } 2934 } 2935 2936 #[test] 2937 fn test_object_freeze() { 2938 let src = r#" 2939 var obj = {}; 2940 obj.x = 1; 2941 Object.freeze(obj); 2942 Object.isFrozen(obj) 2943 "#; 2944 match eval(src).unwrap() { 2945 Value::Boolean(b) => assert!(b), 2946 v => panic!("expected true, got {v:?}"), 2947 } 2948 } 2949 2950 #[test] 2951 fn test_object_has_own_property() { 2952 let src = r#" 2953 var obj = {}; 2954 obj.x = 1; 2955 obj.hasOwnProperty("x") 2956 "#; 2957 match eval(src).unwrap() { 2958 Value::Boolean(b) => assert!(b), 2959 v => panic!("expected true, got {v:?}"), 2960 } 2961 } 2962 2963 // ── Array built-in tests ───────────────────────────────── 2964 2965 #[test] 2966 fn test_array_push_pop() { 2967 let src = r#" 2968 var arr = [1, 2, 3]; 2969 arr.push(4); 2970 arr.pop() 2971 "#; 2972 match eval(src).unwrap() { 2973 Value::Number(n) => assert_eq!(n, 4.0), 2974 v => panic!("expected 4, got {v:?}"), 2975 } 2976 } 2977 2978 #[test] 2979 fn test_array_push_length() { 2980 let src = r#" 2981 var arr = []; 2982 arr.push(10); 2983 arr.push(20); 2984 arr.length 2985 "#; 2986 match eval(src).unwrap() { 2987 Value::Number(n) => assert_eq!(n, 2.0), 2988 v => panic!("expected 2, got {v:?}"), 2989 } 2990 } 2991 2992 #[test] 2993 fn test_array_shift_unshift() { 2994 let src = r#" 2995 var arr = [1, 2, 3]; 2996 arr.unshift(0); 2997 arr.shift() 2998 "#; 2999 match eval(src).unwrap() { 3000 Value::Number(n) => assert_eq!(n, 0.0), 3001 v => panic!("expected 0, got {v:?}"), 3002 } 3003 } 3004 3005 #[test] 3006 fn test_array_index_of() { 3007 let src = r#" 3008 var arr = [10, 20, 30]; 3009 arr.indexOf(20) 3010 "#; 3011 match eval(src).unwrap() { 3012 Value::Number(n) => assert_eq!(n, 1.0), 3013 v => panic!("expected 1, got {v:?}"), 3014 } 3015 } 3016 3017 #[test] 3018 fn test_array_includes() { 3019 let src = r#" 3020 var arr = [1, 2, 3]; 3021 arr.includes(2) 3022 "#; 3023 match eval(src).unwrap() { 3024 Value::Boolean(b) => assert!(b), 3025 v => panic!("expected true, got {v:?}"), 3026 } 3027 } 3028 3029 #[test] 3030 fn test_array_join() { 3031 let src = r#" 3032 var arr = [1, 2, 3]; 3033 arr.join("-") 3034 "#; 3035 match eval(src).unwrap() { 3036 Value::String(s) => assert_eq!(s, "1-2-3"), 3037 v => panic!("expected '1-2-3', got {v:?}"), 3038 } 3039 } 3040 3041 #[test] 3042 fn test_array_slice() { 3043 let src = r#" 3044 var arr = [1, 2, 3, 4, 5]; 3045 var s = arr.slice(1, 3); 3046 s.length 3047 "#; 3048 match eval(src).unwrap() { 3049 Value::Number(n) => assert_eq!(n, 2.0), 3050 v => panic!("expected 2, got {v:?}"), 3051 } 3052 } 3053 3054 #[test] 3055 fn test_array_concat() { 3056 let src = r#" 3057 var a = [1, 2]; 3058 var b = [3, 4]; 3059 var c = a.concat(b); 3060 c.length 3061 "#; 3062 match eval(src).unwrap() { 3063 Value::Number(n) => assert_eq!(n, 4.0), 3064 v => panic!("expected 4, got {v:?}"), 3065 } 3066 } 3067 3068 #[test] 3069 fn test_array_reverse() { 3070 let src = r#" 3071 var arr = [1, 2, 3]; 3072 arr.reverse(); 3073 arr[0] 3074 "#; 3075 match eval(src).unwrap() { 3076 Value::Number(n) => assert_eq!(n, 3.0), 3077 v => panic!("expected 3, got {v:?}"), 3078 } 3079 } 3080 3081 #[test] 3082 fn test_array_splice() { 3083 let src = r#" 3084 var arr = [1, 2, 3, 4, 5]; 3085 var removed = arr.splice(1, 2); 3086 removed.length 3087 "#; 3088 match eval(src).unwrap() { 3089 Value::Number(n) => assert_eq!(n, 2.0), 3090 v => panic!("expected 2, got {v:?}"), 3091 } 3092 } 3093 3094 #[test] 3095 fn test_array_is_array() { 3096 let src = "Array.isArray([1, 2, 3])"; 3097 match eval(src).unwrap() { 3098 Value::Boolean(b) => assert!(b), 3099 v => panic!("expected true, got {v:?}"), 3100 } 3101 } 3102 3103 #[test] 3104 fn test_array_map() { 3105 let src = r#" 3106 var arr = [1, 2, 3]; 3107 var doubled = arr.map(function(x) { return x * 2; }); 3108 doubled[1] 3109 "#; 3110 match eval(src).unwrap() { 3111 Value::Number(n) => assert_eq!(n, 4.0), 3112 v => panic!("expected 4, got {v:?}"), 3113 } 3114 } 3115 3116 #[test] 3117 fn test_array_filter() { 3118 let src = r#" 3119 var arr = [1, 2, 3, 4, 5]; 3120 var evens = arr.filter(function(x) { return x % 2 === 0; }); 3121 evens.length 3122 "#; 3123 match eval(src).unwrap() { 3124 Value::Number(n) => assert_eq!(n, 2.0), 3125 v => panic!("expected 2, got {v:?}"), 3126 } 3127 } 3128 3129 #[test] 3130 fn test_array_reduce() { 3131 let src = r#" 3132 var arr = [1, 2, 3, 4]; 3133 arr.reduce(function(acc, x) { return acc + x; }, 0) 3134 "#; 3135 match eval(src).unwrap() { 3136 Value::Number(n) => assert_eq!(n, 10.0), 3137 v => panic!("expected 10, got {v:?}"), 3138 } 3139 } 3140 3141 #[test] 3142 fn test_array_foreach() { 3143 let src = r#" 3144 var arr = [1, 2, 3]; 3145 var sum = 0; 3146 arr.forEach(function(x) { sum = sum + x; }); 3147 sum 3148 "#; 3149 match eval(src).unwrap() { 3150 Value::Number(n) => assert_eq!(n, 6.0), 3151 v => panic!("expected 6, got {v:?}"), 3152 } 3153 } 3154 3155 #[test] 3156 fn test_array_find() { 3157 let src = r#" 3158 var arr = [1, 2, 3, 4]; 3159 arr.find(function(x) { return x > 2; }) 3160 "#; 3161 match eval(src).unwrap() { 3162 Value::Number(n) => assert_eq!(n, 3.0), 3163 v => panic!("expected 3, got {v:?}"), 3164 } 3165 } 3166 3167 #[test] 3168 fn test_array_find_index() { 3169 let src = r#" 3170 var arr = [1, 2, 3, 4]; 3171 arr.findIndex(function(x) { return x > 2; }) 3172 "#; 3173 match eval(src).unwrap() { 3174 Value::Number(n) => assert_eq!(n, 2.0), 3175 v => panic!("expected 2, got {v:?}"), 3176 } 3177 } 3178 3179 #[test] 3180 fn test_array_some_every() { 3181 let src = r#" 3182 var arr = [2, 4, 6]; 3183 var all_even = arr.every(function(x) { return x % 2 === 0; }); 3184 var has_big = arr.some(function(x) { return x > 10; }); 3185 all_even && !has_big 3186 "#; 3187 match eval(src).unwrap() { 3188 Value::Boolean(b) => assert!(b), 3189 v => panic!("expected true, got {v:?}"), 3190 } 3191 } 3192 3193 #[test] 3194 fn test_array_sort() { 3195 let src = r#" 3196 var arr = [3, 1, 2]; 3197 arr.sort(); 3198 arr[0] 3199 "#; 3200 match eval(src).unwrap() { 3201 Value::Number(n) => assert_eq!(n, 1.0), 3202 v => panic!("expected 1, got {v:?}"), 3203 } 3204 } 3205 3206 #[test] 3207 fn test_array_sort_custom() { 3208 let src = r#" 3209 var arr = [3, 1, 2]; 3210 arr.sort(function(a, b) { return a - b; }); 3211 arr[2] 3212 "#; 3213 match eval(src).unwrap() { 3214 Value::Number(n) => assert_eq!(n, 3.0), 3215 v => panic!("expected 3, got {v:?}"), 3216 } 3217 } 3218 3219 #[test] 3220 fn test_array_from() { 3221 let src = r#" 3222 var arr = Array.from("abc"); 3223 arr.length 3224 "#; 3225 match eval(src).unwrap() { 3226 Value::Number(n) => assert_eq!(n, 3.0), 3227 v => panic!("expected 3, got {v:?}"), 3228 } 3229 } 3230 3231 #[test] 3232 fn test_array_from_array() { 3233 let src = r#" 3234 var orig = [10, 20, 30]; 3235 var copy = Array.from(orig); 3236 copy[2] 3237 "#; 3238 match eval(src).unwrap() { 3239 Value::Number(n) => assert_eq!(n, 30.0), 3240 v => panic!("expected 30, got {v:?}"), 3241 } 3242 } 3243 3244 #[test] 3245 fn test_array_flat() { 3246 let src = r#" 3247 var arr = [[1, 2], [3, 4]]; 3248 var flat = arr.flat(); 3249 flat.length 3250 "#; 3251 match eval(src).unwrap() { 3252 Value::Number(n) => assert_eq!(n, 4.0), 3253 v => panic!("expected 4, got {v:?}"), 3254 } 3255 } 3256 3257 // ── Error built-in tests ───────────────────────────────── 3258 3259 #[test] 3260 fn test_error_constructor() { 3261 let src = r#" 3262 var e = new Error("oops"); 3263 e.message 3264 "#; 3265 match eval(src).unwrap() { 3266 Value::String(s) => assert_eq!(s, "oops"), 3267 v => panic!("expected 'oops', got {v:?}"), 3268 } 3269 } 3270 3271 #[test] 3272 fn test_type_error_constructor() { 3273 let src = r#" 3274 var e = new TypeError("bad type"); 3275 e.message 3276 "#; 3277 match eval(src).unwrap() { 3278 Value::String(s) => assert_eq!(s, "bad type"), 3279 v => panic!("expected 'bad type', got {v:?}"), 3280 } 3281 } 3282 3283 // ── Global function tests ──────────────────────────────── 3284 3285 #[test] 3286 fn test_parse_int() { 3287 let src = "parseInt('42')"; 3288 match eval(src).unwrap() { 3289 Value::Number(n) => assert_eq!(n, 42.0), 3290 v => panic!("expected 42, got {v:?}"), 3291 } 3292 } 3293 3294 #[test] 3295 fn test_parse_int_hex() { 3296 let src = "parseInt('0xFF', 16)"; 3297 match eval(src).unwrap() { 3298 Value::Number(n) => assert_eq!(n, 255.0), 3299 v => panic!("expected 255, got {v:?}"), 3300 } 3301 } 3302 3303 #[test] 3304 fn test_is_nan() { 3305 let src = "isNaN(NaN)"; 3306 match eval(src).unwrap() { 3307 Value::Boolean(b) => assert!(b), 3308 v => panic!("expected true, got {v:?}"), 3309 } 3310 } 3311 3312 #[test] 3313 fn test_is_finite() { 3314 let src = "isFinite(42)"; 3315 match eval(src).unwrap() { 3316 Value::Boolean(b) => assert!(b), 3317 v => panic!("expected true, got {v:?}"), 3318 } 3319 } 3320 3321 // ── String built-in tests ───────────────────────────────── 3322 3323 #[test] 3324 fn test_string_constructor() { 3325 match eval("String(42)").unwrap() { 3326 Value::String(s) => assert_eq!(s, "42"), 3327 v => panic!("expected '42', got {v:?}"), 3328 } 3329 match eval("String(true)").unwrap() { 3330 Value::String(s) => assert_eq!(s, "true"), 3331 v => panic!("expected 'true', got {v:?}"), 3332 } 3333 match eval("String()").unwrap() { 3334 Value::String(s) => assert_eq!(s, ""), 3335 v => panic!("expected '', got {v:?}"), 3336 } 3337 } 3338 3339 #[test] 3340 fn test_string_length() { 3341 match eval("'hello'.length").unwrap() { 3342 Value::Number(n) => assert_eq!(n, 5.0), 3343 v => panic!("expected 5, got {v:?}"), 3344 } 3345 } 3346 3347 #[test] 3348 fn test_string_char_at() { 3349 match eval("'hello'.charAt(1)").unwrap() { 3350 Value::String(s) => assert_eq!(s, "e"), 3351 v => panic!("expected 'e', got {v:?}"), 3352 } 3353 match eval("'hello'.charAt(10)").unwrap() { 3354 Value::String(s) => assert_eq!(s, ""), 3355 v => panic!("expected '', got {v:?}"), 3356 } 3357 } 3358 3359 #[test] 3360 fn test_string_char_code_at() { 3361 match eval("'A'.charCodeAt(0)").unwrap() { 3362 Value::Number(n) => assert_eq!(n, 65.0), 3363 v => panic!("expected 65, got {v:?}"), 3364 } 3365 } 3366 3367 #[test] 3368 fn test_string_proto_concat() { 3369 match eval("'hello'.concat(' ', 'world')").unwrap() { 3370 Value::String(s) => assert_eq!(s, "hello world"), 3371 v => panic!("expected 'hello world', got {v:?}"), 3372 } 3373 } 3374 3375 #[test] 3376 fn test_string_slice() { 3377 match eval("'hello world'.slice(6)").unwrap() { 3378 Value::String(s) => assert_eq!(s, "world"), 3379 v => panic!("expected 'world', got {v:?}"), 3380 } 3381 match eval("'hello'.slice(1, 3)").unwrap() { 3382 Value::String(s) => assert_eq!(s, "el"), 3383 v => panic!("expected 'el', got {v:?}"), 3384 } 3385 match eval("'hello'.slice(-3)").unwrap() { 3386 Value::String(s) => assert_eq!(s, "llo"), 3387 v => panic!("expected 'llo', got {v:?}"), 3388 } 3389 } 3390 3391 #[test] 3392 fn test_string_substring() { 3393 match eval("'hello'.substring(1, 3)").unwrap() { 3394 Value::String(s) => assert_eq!(s, "el"), 3395 v => panic!("expected 'el', got {v:?}"), 3396 } 3397 // substring swaps args if start > end 3398 match eval("'hello'.substring(3, 1)").unwrap() { 3399 Value::String(s) => assert_eq!(s, "el"), 3400 v => panic!("expected 'el', got {v:?}"), 3401 } 3402 } 3403 3404 #[test] 3405 fn test_string_index_of() { 3406 match eval("'hello world'.indexOf('world')").unwrap() { 3407 Value::Number(n) => assert_eq!(n, 6.0), 3408 v => panic!("expected 6, got {v:?}"), 3409 } 3410 match eval("'hello'.indexOf('xyz')").unwrap() { 3411 Value::Number(n) => assert_eq!(n, -1.0), 3412 v => panic!("expected -1, got {v:?}"), 3413 } 3414 } 3415 3416 #[test] 3417 fn test_string_last_index_of() { 3418 match eval("'abcabc'.lastIndexOf('abc')").unwrap() { 3419 Value::Number(n) => assert_eq!(n, 3.0), 3420 v => panic!("expected 3, got {v:?}"), 3421 } 3422 } 3423 3424 #[test] 3425 fn test_string_includes() { 3426 match eval("'hello world'.includes('world')").unwrap() { 3427 Value::Boolean(b) => assert!(b), 3428 v => panic!("expected true, got {v:?}"), 3429 } 3430 match eval("'hello'.includes('xyz')").unwrap() { 3431 Value::Boolean(b) => assert!(!b), 3432 v => panic!("expected false, got {v:?}"), 3433 } 3434 } 3435 3436 #[test] 3437 fn test_string_starts_ends_with() { 3438 match eval("'hello'.startsWith('hel')").unwrap() { 3439 Value::Boolean(b) => assert!(b), 3440 v => panic!("expected true, got {v:?}"), 3441 } 3442 match eval("'hello'.endsWith('llo')").unwrap() { 3443 Value::Boolean(b) => assert!(b), 3444 v => panic!("expected true, got {v:?}"), 3445 } 3446 } 3447 3448 #[test] 3449 fn test_string_trim() { 3450 match eval("' hello '.trim()").unwrap() { 3451 Value::String(s) => assert_eq!(s, "hello"), 3452 v => panic!("expected 'hello', got {v:?}"), 3453 } 3454 match eval("' hello '.trimStart()").unwrap() { 3455 Value::String(s) => assert_eq!(s, "hello "), 3456 v => panic!("expected 'hello ', got {v:?}"), 3457 } 3458 match eval("' hello '.trimEnd()").unwrap() { 3459 Value::String(s) => assert_eq!(s, " hello"), 3460 v => panic!("expected ' hello', got {v:?}"), 3461 } 3462 } 3463 3464 #[test] 3465 fn test_string_pad() { 3466 match eval("'5'.padStart(3, '0')").unwrap() { 3467 Value::String(s) => assert_eq!(s, "005"), 3468 v => panic!("expected '005', got {v:?}"), 3469 } 3470 match eval("'5'.padEnd(3, '0')").unwrap() { 3471 Value::String(s) => assert_eq!(s, "500"), 3472 v => panic!("expected '500', got {v:?}"), 3473 } 3474 } 3475 3476 #[test] 3477 fn test_string_repeat() { 3478 match eval("'ab'.repeat(3)").unwrap() { 3479 Value::String(s) => assert_eq!(s, "ababab"), 3480 v => panic!("expected 'ababab', got {v:?}"), 3481 } 3482 } 3483 3484 #[test] 3485 fn test_string_split() { 3486 // split returns an array; verify length and elements. 3487 match eval("'a,b,c'.split(',').length").unwrap() { 3488 Value::Number(n) => assert_eq!(n, 3.0), 3489 v => panic!("expected 3, got {v:?}"), 3490 } 3491 match eval("'a,b,c'.split(',')[0]").unwrap() { 3492 Value::String(s) => assert_eq!(s, "a"), 3493 v => panic!("expected 'a', got {v:?}"), 3494 } 3495 match eval("'a,b,c'.split(',')[2]").unwrap() { 3496 Value::String(s) => assert_eq!(s, "c"), 3497 v => panic!("expected 'c', got {v:?}"), 3498 } 3499 } 3500 3501 #[test] 3502 fn test_string_replace() { 3503 match eval("'hello world'.replace('world', 'there')").unwrap() { 3504 Value::String(s) => assert_eq!(s, "hello there"), 3505 v => panic!("expected 'hello there', got {v:?}"), 3506 } 3507 } 3508 3509 #[test] 3510 fn test_string_replace_all() { 3511 match eval("'aabbcc'.replaceAll('b', 'x')").unwrap() { 3512 Value::String(s) => assert_eq!(s, "aaxxcc"), 3513 v => panic!("expected 'aaxxcc', got {v:?}"), 3514 } 3515 } 3516 3517 #[test] 3518 fn test_string_case() { 3519 match eval("'Hello'.toLowerCase()").unwrap() { 3520 Value::String(s) => assert_eq!(s, "hello"), 3521 v => panic!("expected 'hello', got {v:?}"), 3522 } 3523 match eval("'Hello'.toUpperCase()").unwrap() { 3524 Value::String(s) => assert_eq!(s, "HELLO"), 3525 v => panic!("expected 'HELLO', got {v:?}"), 3526 } 3527 } 3528 3529 #[test] 3530 fn test_string_at() { 3531 match eval("'hello'.at(0)").unwrap() { 3532 Value::String(s) => assert_eq!(s, "h"), 3533 v => panic!("expected 'h', got {v:?}"), 3534 } 3535 match eval("'hello'.at(-1)").unwrap() { 3536 Value::String(s) => assert_eq!(s, "o"), 3537 v => panic!("expected 'o', got {v:?}"), 3538 } 3539 } 3540 3541 #[test] 3542 fn test_string_from_char_code() { 3543 match eval("String.fromCharCode(72, 101, 108)").unwrap() { 3544 Value::String(s) => assert_eq!(s, "Hel"), 3545 v => panic!("expected 'Hel', got {v:?}"), 3546 } 3547 } 3548 3549 #[test] 3550 fn test_string_from_code_point() { 3551 match eval("String.fromCodePoint(65, 66, 67)").unwrap() { 3552 Value::String(s) => assert_eq!(s, "ABC"), 3553 v => panic!("expected 'ABC', got {v:?}"), 3554 } 3555 } 3556 3557 // ── Number built-in tests ───────────────────────────────── 3558 3559 #[test] 3560 fn test_number_constructor() { 3561 match eval("Number('42')").unwrap() { 3562 Value::Number(n) => assert_eq!(n, 42.0), 3563 v => panic!("expected 42, got {v:?}"), 3564 } 3565 match eval("Number(true)").unwrap() { 3566 Value::Number(n) => assert_eq!(n, 1.0), 3567 v => panic!("expected 1, got {v:?}"), 3568 } 3569 match eval("Number()").unwrap() { 3570 Value::Number(n) => assert_eq!(n, 0.0), 3571 v => panic!("expected 0, got {v:?}"), 3572 } 3573 } 3574 3575 #[test] 3576 fn test_number_is_nan() { 3577 match eval("Number.isNaN(NaN)").unwrap() { 3578 Value::Boolean(b) => assert!(b), 3579 v => panic!("expected true, got {v:?}"), 3580 } 3581 match eval("Number.isNaN(42)").unwrap() { 3582 Value::Boolean(b) => assert!(!b), 3583 v => panic!("expected false, got {v:?}"), 3584 } 3585 // Number.isNaN doesn't coerce — string "NaN" is not NaN. 3586 match eval("Number.isNaN('NaN')").unwrap() { 3587 Value::Boolean(b) => assert!(!b), 3588 v => panic!("expected false, got {v:?}"), 3589 } 3590 } 3591 3592 #[test] 3593 fn test_number_is_finite() { 3594 match eval("Number.isFinite(42)").unwrap() { 3595 Value::Boolean(b) => assert!(b), 3596 v => panic!("expected true, got {v:?}"), 3597 } 3598 match eval("Number.isFinite(Infinity)").unwrap() { 3599 Value::Boolean(b) => assert!(!b), 3600 v => panic!("expected false, got {v:?}"), 3601 } 3602 } 3603 3604 #[test] 3605 fn test_number_is_integer() { 3606 match eval("Number.isInteger(42)").unwrap() { 3607 Value::Boolean(b) => assert!(b), 3608 v => panic!("expected true, got {v:?}"), 3609 } 3610 match eval("Number.isInteger(42.5)").unwrap() { 3611 Value::Boolean(b) => assert!(!b), 3612 v => panic!("expected false, got {v:?}"), 3613 } 3614 } 3615 3616 #[test] 3617 fn test_number_is_safe_integer() { 3618 match eval("Number.isSafeInteger(42)").unwrap() { 3619 Value::Boolean(b) => assert!(b), 3620 v => panic!("expected true, got {v:?}"), 3621 } 3622 match eval("Number.isSafeInteger(9007199254740992)").unwrap() { 3623 Value::Boolean(b) => assert!(!b), 3624 v => panic!("expected false, got {v:?}"), 3625 } 3626 } 3627 3628 #[test] 3629 fn test_number_constants() { 3630 match eval("Number.MAX_SAFE_INTEGER").unwrap() { 3631 Value::Number(n) => assert_eq!(n, 9007199254740991.0), 3632 v => panic!("expected MAX_SAFE_INTEGER, got {v:?}"), 3633 } 3634 match eval("Number.EPSILON").unwrap() { 3635 Value::Number(n) => assert_eq!(n, f64::EPSILON), 3636 v => panic!("expected EPSILON, got {v:?}"), 3637 } 3638 } 3639 3640 #[test] 3641 fn test_number_to_fixed() { 3642 match eval("var n = 3.14159; n.toFixed(2)").unwrap() { 3643 Value::String(s) => assert_eq!(s, "3.14"), 3644 v => panic!("expected '3.14', got {v:?}"), 3645 } 3646 } 3647 3648 #[test] 3649 fn test_number_to_string_radix() { 3650 match eval("var n = 255; n.toString(16)").unwrap() { 3651 Value::String(s) => assert_eq!(s, "ff"), 3652 v => panic!("expected 'ff', got {v:?}"), 3653 } 3654 match eval("var n = 10; n.toString(2)").unwrap() { 3655 Value::String(s) => assert_eq!(s, "1010"), 3656 v => panic!("expected '1010', got {v:?}"), 3657 } 3658 } 3659 3660 #[test] 3661 fn test_number_parse_int() { 3662 match eval("Number.parseInt('42')").unwrap() { 3663 Value::Number(n) => assert_eq!(n, 42.0), 3664 v => panic!("expected 42, got {v:?}"), 3665 } 3666 } 3667 3668 // ── Boolean built-in tests ──────────────────────────────── 3669 3670 #[test] 3671 fn test_boolean_constructor() { 3672 match eval("Boolean(1)").unwrap() { 3673 Value::Boolean(b) => assert!(b), 3674 v => panic!("expected true, got {v:?}"), 3675 } 3676 match eval("Boolean(0)").unwrap() { 3677 Value::Boolean(b) => assert!(!b), 3678 v => panic!("expected false, got {v:?}"), 3679 } 3680 match eval("Boolean('')").unwrap() { 3681 Value::Boolean(b) => assert!(!b), 3682 v => panic!("expected false, got {v:?}"), 3683 } 3684 match eval("Boolean('hello')").unwrap() { 3685 Value::Boolean(b) => assert!(b), 3686 v => panic!("expected true, got {v:?}"), 3687 } 3688 } 3689 3690 #[test] 3691 fn test_boolean_to_string() { 3692 match eval("true.toString()").unwrap() { 3693 Value::String(s) => assert_eq!(s, "true"), 3694 v => panic!("expected 'true', got {v:?}"), 3695 } 3696 match eval("false.toString()").unwrap() { 3697 Value::String(s) => assert_eq!(s, "false"), 3698 v => panic!("expected 'false', got {v:?}"), 3699 } 3700 } 3701 3702 // ── Symbol built-in tests ───────────────────────────────── 3703 3704 #[test] 3705 fn test_symbol_uniqueness() { 3706 // Each Symbol() call should produce a unique value. 3707 match eval("var a = Symbol('x'); var b = Symbol('x'); a === b").unwrap() { 3708 Value::Boolean(b) => assert!(!b), 3709 v => panic!("expected false, got {v:?}"), 3710 } 3711 } 3712 3713 #[test] 3714 fn test_symbol_well_known() { 3715 match eval("typeof Symbol.iterator").unwrap() { 3716 Value::String(s) => assert_eq!(s, "string"), 3717 v => panic!("expected 'string', got {v:?}"), 3718 } 3719 match eval("Symbol.iterator").unwrap() { 3720 Value::String(s) => assert_eq!(s, "@@iterator"), 3721 v => panic!("expected '@@iterator', got {v:?}"), 3722 } 3723 } 3724 3725 #[test] 3726 fn test_symbol_for_and_key_for() { 3727 // "for" is a keyword, so use bracket notation: Symbol["for"](...). 3728 match eval("Symbol['for']('test') === Symbol['for']('test')").unwrap() { 3729 Value::Boolean(b) => assert!(b), 3730 v => panic!("expected true, got {v:?}"), 3731 } 3732 match eval("Symbol.keyFor(Symbol['for']('mykey'))").unwrap() { 3733 Value::String(s) => assert_eq!(s, "mykey"), 3734 v => panic!("expected 'mykey', got {v:?}"), 3735 } 3736 } 3737 3738 // ── Primitive auto-boxing tests ─────────────────────────── 3739 3740 #[test] 3741 fn test_string_method_chaining() { 3742 match eval("' Hello World '.trim().toLowerCase()").unwrap() { 3743 Value::String(s) => assert_eq!(s, "hello world"), 3744 v => panic!("expected 'hello world', got {v:?}"), 3745 } 3746 } 3747 3748 #[test] 3749 fn test_string_substr() { 3750 match eval("'hello world'.substr(6, 5)").unwrap() { 3751 Value::String(s) => assert_eq!(s, "world"), 3752 v => panic!("expected 'world', got {v:?}"), 3753 } 3754 } 3755 3756 // ── Math built-in ───────────────────────────────────────── 3757 3758 #[test] 3759 fn test_math_constants() { 3760 let r = eval("Math.PI").unwrap(); 3761 match r { 3762 Value::Number(n) => assert!((n - std::f64::consts::PI).abs() < 1e-10), 3763 _ => panic!("Expected number"), 3764 } 3765 let r = eval("Math.E").unwrap(); 3766 match r { 3767 Value::Number(n) => assert!((n - std::f64::consts::E).abs() < 1e-10), 3768 _ => panic!("Expected number"), 3769 } 3770 let r = eval("Math.SQRT2").unwrap(); 3771 match r { 3772 Value::Number(n) => assert!((n - std::f64::consts::SQRT_2).abs() < 1e-10), 3773 _ => panic!("Expected number"), 3774 } 3775 } 3776 3777 #[test] 3778 fn test_math_abs() { 3779 assert_eq!(eval("Math.abs(-5)").unwrap().to_number(), 5.0); 3780 assert_eq!(eval("Math.abs(3)").unwrap().to_number(), 3.0); 3781 assert_eq!(eval("Math.abs(0)").unwrap().to_number(), 0.0); 3782 } 3783 3784 #[test] 3785 fn test_math_floor_ceil_round_trunc() { 3786 assert_eq!(eval("Math.floor(4.7)").unwrap().to_number(), 4.0); 3787 assert_eq!(eval("Math.ceil(4.2)").unwrap().to_number(), 5.0); 3788 assert_eq!(eval("Math.round(4.5)").unwrap().to_number(), 5.0); 3789 assert_eq!(eval("Math.round(4.4)").unwrap().to_number(), 4.0); 3790 assert_eq!(eval("Math.trunc(4.7)").unwrap().to_number(), 4.0); 3791 assert_eq!(eval("Math.trunc(-4.7)").unwrap().to_number(), -4.0); 3792 } 3793 3794 #[test] 3795 fn test_math_max_min() { 3796 assert_eq!(eval("Math.max(1, 3, 2)").unwrap().to_number(), 3.0); 3797 assert_eq!(eval("Math.min(1, 3, 2)").unwrap().to_number(), 1.0); 3798 assert_eq!(eval("Math.max()").unwrap().to_number(), f64::NEG_INFINITY); 3799 assert_eq!(eval("Math.min()").unwrap().to_number(), f64::INFINITY); 3800 } 3801 3802 #[test] 3803 fn test_math_pow_sqrt() { 3804 assert_eq!(eval("Math.pow(2, 10)").unwrap().to_number(), 1024.0); 3805 assert_eq!(eval("Math.sqrt(9)").unwrap().to_number(), 3.0); 3806 let cbrt = eval("Math.cbrt(27)").unwrap().to_number(); 3807 assert!((cbrt - 3.0).abs() < 1e-10); 3808 } 3809 3810 #[test] 3811 fn test_math_trig() { 3812 let sin = eval("Math.sin(0)").unwrap().to_number(); 3813 assert!(sin.abs() < 1e-10); 3814 let cos = eval("Math.cos(0)").unwrap().to_number(); 3815 assert!((cos - 1.0).abs() < 1e-10); 3816 let atan2 = eval("Math.atan2(1, 1)").unwrap().to_number(); 3817 assert!((atan2 - std::f64::consts::FRAC_PI_4).abs() < 1e-10); 3818 } 3819 3820 #[test] 3821 fn test_math_log_exp() { 3822 let exp = eval("Math.exp(1)").unwrap().to_number(); 3823 assert!((exp - std::f64::consts::E).abs() < 1e-10); 3824 let log = eval("Math.log(Math.E)").unwrap().to_number(); 3825 assert!((log - 1.0).abs() < 1e-10); 3826 let log2 = eval("Math.log2(8)").unwrap().to_number(); 3827 assert!((log2 - 3.0).abs() < 1e-10); 3828 let log10 = eval("Math.log10(1000)").unwrap().to_number(); 3829 assert!((log10 - 3.0).abs() < 1e-10); 3830 } 3831 3832 #[test] 3833 fn test_math_sign() { 3834 assert_eq!(eval("Math.sign(5)").unwrap().to_number(), 1.0); 3835 assert_eq!(eval("Math.sign(-5)").unwrap().to_number(), -1.0); 3836 assert_eq!(eval("Math.sign(0)").unwrap().to_number(), 0.0); 3837 } 3838 3839 #[test] 3840 fn test_math_clz32() { 3841 assert_eq!(eval("Math.clz32(1)").unwrap().to_number(), 31.0); 3842 assert_eq!(eval("Math.clz32(0)").unwrap().to_number(), 32.0); 3843 } 3844 3845 #[test] 3846 fn test_math_imul() { 3847 assert_eq!(eval("Math.imul(3, 4)").unwrap().to_number(), 12.0); 3848 assert_eq!(eval("Math.imul(0xffffffff, 5)").unwrap().to_number(), -5.0); 3849 } 3850 3851 #[test] 3852 fn test_math_hypot() { 3853 assert_eq!(eval("Math.hypot(3, 4)").unwrap().to_number(), 5.0); 3854 assert_eq!(eval("Math.hypot()").unwrap().to_number(), 0.0); 3855 } 3856 3857 #[test] 3858 fn test_math_random() { 3859 match eval("var r = Math.random(); r >= 0 && r < 1").unwrap() { 3860 Value::Boolean(b) => assert!(b), 3861 v => panic!("expected true, got {v:?}"), 3862 } 3863 } 3864 3865 #[test] 3866 fn test_math_fround() { 3867 let r = eval("Math.fround(5.5)").unwrap().to_number(); 3868 assert_eq!(r, 5.5f32 as f64); 3869 } 3870 3871 // ── Date built-in ───────────────────────────────────────── 3872 3873 #[test] 3874 fn test_date_now() { 3875 let r = eval("Date.now()").unwrap().to_number(); 3876 assert!(r > 1_577_836_800_000.0); 3877 } 3878 3879 #[test] 3880 fn test_date_utc() { 3881 let r = eval("Date.UTC(2020, 0, 1)").unwrap().to_number(); 3882 assert_eq!(r, 1_577_836_800_000.0); 3883 } 3884 3885 #[test] 3886 fn test_date_parse() { 3887 let r = eval("Date.parse('2020-01-01T00:00:00.000Z')") 3888 .unwrap() 3889 .to_number(); 3890 assert_eq!(r, 1_577_836_800_000.0); 3891 } 3892 3893 #[test] 3894 fn test_date_constructor_ms() { 3895 let r = eval("var d = new Date(1577836800000); d.getFullYear()") 3896 .unwrap() 3897 .to_number(); 3898 assert_eq!(r, 2020.0); 3899 } 3900 3901 #[test] 3902 fn test_date_constructor_components() { 3903 let r = eval("var d = new Date(2020, 0, 1, 0, 0, 0, 0); d.getTime()") 3904 .unwrap() 3905 .to_number(); 3906 assert_eq!(r, 1_577_836_800_000.0); 3907 } 3908 3909 #[test] 3910 fn test_date_getters() { 3911 let src = r#" 3912 var d = new Date(1577836800000); 3913 var results = [ 3914 d.getFullYear(), 3915 d.getMonth(), 3916 d.getDate(), 3917 d.getHours(), 3918 d.getMinutes(), 3919 d.getSeconds(), 3920 d.getMilliseconds(), 3921 d.getDay() 3922 ]; 3923 results[0] === 2020 && results[1] === 0 && results[2] === 1 && 3924 results[3] === 0 && results[4] === 0 && results[5] === 0 && 3925 results[6] === 0 && results[7] === 3 3926 "#; 3927 match eval(src).unwrap() { 3928 Value::Boolean(b) => assert!(b), 3929 v => panic!("expected true, got {v:?}"), 3930 } 3931 } 3932 3933 #[test] 3934 fn test_date_setters() { 3935 let src = r#" 3936 var d = new Date(1577836800000); 3937 d.setFullYear(2025); 3938 d.getFullYear() 3939 "#; 3940 assert_eq!(eval(src).unwrap().to_number(), 2025.0); 3941 } 3942 3943 #[test] 3944 fn test_date_to_iso_string() { 3945 match eval("var d = new Date(1577836800000); d.toISOString()").unwrap() { 3946 Value::String(s) => assert_eq!(s, "2020-01-01T00:00:00.000Z"), 3947 v => panic!("expected ISO string, got {v:?}"), 3948 } 3949 } 3950 3951 #[test] 3952 fn test_date_value_of() { 3953 let r = eval("var d = new Date(1577836800000); d.valueOf()") 3954 .unwrap() 3955 .to_number(); 3956 assert_eq!(r, 1_577_836_800_000.0); 3957 } 3958 3959 #[test] 3960 fn test_date_to_string() { 3961 let r = eval("var d = new Date(1577836800000); d.toString()").unwrap(); 3962 match r { 3963 Value::String(s) => assert!(s.contains("2020") && s.contains("GMT")), 3964 _ => panic!("Expected string"), 3965 } 3966 } 3967 3968 #[test] 3969 fn test_date_to_json() { 3970 match eval("var d = new Date(1577836800000); d.toJSON()").unwrap() { 3971 Value::String(s) => assert_eq!(s, "2020-01-01T00:00:00.000Z"), 3972 v => panic!("expected ISO string, got {v:?}"), 3973 } 3974 } 3975 3976 #[test] 3977 fn test_date_constructor_string() { 3978 let r = eval("var d = new Date('2020-06-15T12:30:00Z'); d.getMonth()") 3979 .unwrap() 3980 .to_number(); 3981 assert_eq!(r, 5.0); 3982 } 3983 3984 // ── JSON built-in ───────────────────────────────────────── 3985 3986 #[test] 3987 fn test_json_parse_primitives() { 3988 assert!(matches!(eval("JSON.parse('null')").unwrap(), Value::Null)); 3989 match eval("JSON.parse('true')").unwrap() { 3990 Value::Boolean(b) => assert!(b), 3991 v => panic!("expected true, got {v:?}"), 3992 } 3993 match eval("JSON.parse('false')").unwrap() { 3994 Value::Boolean(b) => assert!(!b), 3995 v => panic!("expected false, got {v:?}"), 3996 } 3997 assert_eq!(eval("JSON.parse('42')").unwrap().to_number(), 42.0); 3998 match eval(r#"JSON.parse('"hello"')"#).unwrap() { 3999 Value::String(s) => assert_eq!(s, "hello"), 4000 v => panic!("expected 'hello', got {v:?}"), 4001 } 4002 } 4003 4004 #[test] 4005 fn test_json_parse_array() { 4006 let src = r#" 4007 var a = JSON.parse('[1, 2, 3]'); 4008 a.length === 3 && a[0] === 1 && a[1] === 2 && a[2] === 3 4009 "#; 4010 match eval(src).unwrap() { 4011 Value::Boolean(b) => assert!(b), 4012 v => panic!("expected true, got {v:?}"), 4013 } 4014 } 4015 4016 #[test] 4017 fn test_json_parse_object() { 4018 let src = r#" 4019 var o = JSON.parse('{"name":"test","value":42}'); 4020 o.name === "test" && o.value === 42 4021 "#; 4022 match eval(src).unwrap() { 4023 Value::Boolean(b) => assert!(b), 4024 v => panic!("expected true, got {v:?}"), 4025 } 4026 } 4027 4028 #[test] 4029 fn test_json_parse_nested() { 4030 let src = r#" 4031 var o = JSON.parse('{"a":[1,{"b":2}]}'); 4032 o.a[1].b === 2 4033 "#; 4034 match eval(src).unwrap() { 4035 Value::Boolean(b) => assert!(b), 4036 v => panic!("expected true, got {v:?}"), 4037 } 4038 } 4039 4040 #[test] 4041 fn test_json_parse_invalid() { 4042 assert!(eval("JSON.parse('{invalid}')").is_err()); 4043 assert!(eval("JSON.parse('')").is_err()); 4044 } 4045 4046 #[test] 4047 fn test_json_stringify_primitives() { 4048 match eval("JSON.stringify(null)").unwrap() { 4049 Value::String(s) => assert_eq!(s, "null"), 4050 v => panic!("expected 'null', got {v:?}"), 4051 } 4052 match eval("JSON.stringify(true)").unwrap() { 4053 Value::String(s) => assert_eq!(s, "true"), 4054 v => panic!("expected 'true', got {v:?}"), 4055 } 4056 match eval("JSON.stringify(42)").unwrap() { 4057 Value::String(s) => assert_eq!(s, "42"), 4058 v => panic!("expected '42', got {v:?}"), 4059 } 4060 match eval(r#"JSON.stringify("hello")"#).unwrap() { 4061 Value::String(s) => assert_eq!(s, "\"hello\""), 4062 v => panic!("expected quoted hello, got {v:?}"), 4063 } 4064 } 4065 4066 #[test] 4067 fn test_json_stringify_array() { 4068 match eval("JSON.stringify([1, 2, 3])").unwrap() { 4069 Value::String(s) => assert_eq!(s, "[1,2,3]"), 4070 v => panic!("expected '[1,2,3]', got {v:?}"), 4071 } 4072 } 4073 4074 #[test] 4075 fn test_json_stringify_object() { 4076 let src = r#" 4077 var o = {a: 1, b: "hello"}; 4078 JSON.stringify(o) 4079 "#; 4080 let r = eval(src).unwrap(); 4081 match r { 4082 Value::String(s) => { 4083 assert!(s.contains("\"a\"") && s.contains("\"b\"")); 4084 assert!(s.contains('1') && s.contains("\"hello\"")); 4085 } 4086 _ => panic!("Expected string"), 4087 } 4088 } 4089 4090 #[test] 4091 fn test_json_stringify_nested() { 4092 let src = r#" 4093 JSON.stringify({a: [1, 2], b: {c: 3}}) 4094 "#; 4095 let r = eval(src).unwrap(); 4096 match r { 4097 Value::String(s) => { 4098 assert!(s.contains("[1,2]")); 4099 assert!(s.contains("\"c\":3") || s.contains("\"c\": 3")); 4100 } 4101 _ => panic!("Expected string"), 4102 } 4103 } 4104 4105 #[test] 4106 fn test_json_stringify_special_values() { 4107 match eval("JSON.stringify(NaN)").unwrap() { 4108 Value::String(s) => assert_eq!(s, "null"), 4109 v => panic!("expected 'null', got {v:?}"), 4110 } 4111 match eval("JSON.stringify(Infinity)").unwrap() { 4112 Value::String(s) => assert_eq!(s, "null"), 4113 v => panic!("expected 'null', got {v:?}"), 4114 } 4115 assert!(matches!( 4116 eval("JSON.stringify(undefined)").unwrap(), 4117 Value::Undefined 4118 )); 4119 } 4120 4121 #[test] 4122 fn test_json_stringify_with_indent() { 4123 let src = r#"JSON.stringify([1, 2], null, 2)"#; 4124 let r = eval(src).unwrap(); 4125 match r { 4126 Value::String(s) => { 4127 assert!(s.contains('\n')); 4128 assert!(s.contains(" 1")); 4129 } 4130 _ => panic!("Expected string"), 4131 } 4132 } 4133 4134 #[test] 4135 fn test_json_parse_escape_sequences() { 4136 let src = r#"JSON.parse('"hello\\nworld"')"#; 4137 match eval(src).unwrap() { 4138 Value::String(s) => assert_eq!(s, "hello\nworld"), 4139 v => panic!("expected escaped string, got {v:?}"), 4140 } 4141 } 4142 4143 #[test] 4144 fn test_json_roundtrip() { 4145 let src = r#" 4146 var original = {name: "test", values: [1, 2, 3], nested: {ok: true}}; 4147 var json = JSON.stringify(original); 4148 var parsed = JSON.parse(json); 4149 parsed.name === "test" && parsed.values[1] === 2 && parsed.nested.ok === true 4150 "#; 4151 match eval(src).unwrap() { 4152 Value::Boolean(b) => assert!(b), 4153 v => panic!("expected true, got {v:?}"), 4154 } 4155 } 4156 4157 #[test] 4158 fn test_json_stringify_circular_detection() { 4159 let src = r#" 4160 var obj = {}; 4161 obj.self = obj; 4162 try { 4163 JSON.stringify(obj); 4164 false; 4165 } catch (e) { 4166 e.message.indexOf("circular") !== -1; 4167 } 4168 "#; 4169 match eval(src).unwrap() { 4170 Value::Boolean(b) => assert!(b), 4171 v => panic!("expected true, got {v:?}"), 4172 } 4173 } 4174 4175 #[test] 4176 fn test_json_parse_unicode_escape() { 4177 let src = r#"JSON.parse('"\\u0041"')"#; 4178 match eval(src).unwrap() { 4179 Value::String(s) => assert_eq!(s, "A"), 4180 v => panic!("expected 'A', got {v:?}"), 4181 } 4182 } 4183 4184 #[test] 4185 fn test_json_stringify_empty() { 4186 match eval("JSON.stringify([])").unwrap() { 4187 Value::String(s) => assert_eq!(s, "[]"), 4188 v => panic!("expected '[]', got {v:?}"), 4189 } 4190 match eval("JSON.stringify({})").unwrap() { 4191 Value::String(s) => assert_eq!(s, "{}"), 4192 v => panic!("expected '{{}}', got {v:?}"), 4193 } 4194 } 4195}