we (web engine): Experimental web browser project to understand the limits of Claude
at map-set 4819 lines 174 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 /// Built-in RegExp.prototype (for RegExp constructor objects). 701 pub regexp_prototype: Option<GcRef>, 702} 703 704/// Maximum register file size. 705const MAX_REGISTERS: usize = 4096; 706/// Maximum call depth. 707const MAX_CALL_DEPTH: usize = 512; 708 709impl Vm { 710 pub fn new() -> Self { 711 let mut vm = Self { 712 registers: vec![Value::Undefined; 256], 713 frames: Vec::new(), 714 globals: HashMap::new(), 715 gc: Gc::new(), 716 instruction_limit: None, 717 instructions_executed: 0, 718 object_prototype: None, 719 array_prototype: None, 720 string_prototype: None, 721 number_prototype: None, 722 boolean_prototype: None, 723 date_prototype: None, 724 regexp_prototype: None, 725 }; 726 crate::builtins::init_builtins(&mut vm); 727 vm 728 } 729 730 /// Set an instruction limit. The VM will return a RuntimeError after 731 /// executing this many instructions. 732 pub fn set_instruction_limit(&mut self, limit: u64) { 733 self.instruction_limit = Some(limit); 734 } 735 736 /// Execute a compiled top-level function and return the completion value. 737 pub fn execute(&mut self, func: &Function) -> Result<Value, RuntimeError> { 738 let reg_count = func.register_count as usize; 739 self.ensure_registers(reg_count); 740 741 self.frames.push(CallFrame { 742 func: func.clone(), 743 ip: 0, 744 base: 0, 745 return_reg: 0, 746 exception_handlers: Vec::new(), 747 upvalues: Vec::new(), 748 }); 749 750 self.run() 751 } 752 753 /// Ensure the register file has at least `needed` slots. 754 fn ensure_registers(&mut self, needed: usize) { 755 if needed > self.registers.len() { 756 if needed > MAX_REGISTERS { 757 return; 758 } 759 self.registers.resize(needed, Value::Undefined); 760 } 761 } 762 763 /// Read a u8 from the current frame's bytecode and advance IP. 764 #[inline] 765 fn read_u8(frame: &mut CallFrame) -> u8 { 766 let b = frame.func.code[frame.ip]; 767 frame.ip += 1; 768 b 769 } 770 771 /// Read a u16 (little-endian) from the current frame's bytecode and advance IP. 772 #[inline] 773 fn read_u16(frame: &mut CallFrame) -> u16 { 774 let lo = frame.func.code[frame.ip]; 775 let hi = frame.func.code[frame.ip + 1]; 776 frame.ip += 2; 777 u16::from_le_bytes([lo, hi]) 778 } 779 780 /// Read an i32 (little-endian) from the current frame's bytecode and advance IP. 781 #[inline] 782 fn read_i32(frame: &mut CallFrame) -> i32 { 783 let bytes = [ 784 frame.func.code[frame.ip], 785 frame.func.code[frame.ip + 1], 786 frame.func.code[frame.ip + 2], 787 frame.func.code[frame.ip + 3], 788 ]; 789 frame.ip += 4; 790 i32::from_le_bytes(bytes) 791 } 792 793 /// Collect all GcRef values reachable from the mutator (roots for GC). 794 fn collect_roots(&self) -> Vec<GcRef> { 795 let mut roots = Vec::new(); 796 for val in &self.registers { 797 if let Some(r) = val.gc_ref() { 798 roots.push(r); 799 } 800 } 801 for val in self.globals.values() { 802 if let Some(r) = val.gc_ref() { 803 roots.push(r); 804 } 805 } 806 for frame in &self.frames { 807 for &uv in &frame.upvalues { 808 roots.push(uv); 809 } 810 } 811 // Built-in prototype roots. 812 if let Some(r) = self.object_prototype { 813 roots.push(r); 814 } 815 if let Some(r) = self.array_prototype { 816 roots.push(r); 817 } 818 if let Some(r) = self.string_prototype { 819 roots.push(r); 820 } 821 if let Some(r) = self.number_prototype { 822 roots.push(r); 823 } 824 if let Some(r) = self.boolean_prototype { 825 roots.push(r); 826 } 827 roots 828 } 829 830 /// Main dispatch loop. 831 fn run(&mut self) -> Result<Value, RuntimeError> { 832 loop { 833 let fi = self.frames.len() - 1; 834 835 // Check if we've reached the end of bytecode. 836 if self.frames[fi].ip >= self.frames[fi].func.code.len() { 837 if self.frames.len() == 1 { 838 self.frames.pop(); 839 return Ok(Value::Undefined); 840 } 841 let old = self.frames.pop().unwrap(); 842 self.registers[old.return_reg] = Value::Undefined; 843 continue; 844 } 845 846 // Instruction limit check (for test harnesses). 847 if let Some(limit) = self.instruction_limit { 848 self.instructions_executed += 1; 849 if self.instructions_executed > limit { 850 return Err(RuntimeError { 851 kind: ErrorKind::Error, 852 message: "instruction limit exceeded".into(), 853 }); 854 } 855 } 856 857 let opcode_byte = self.frames[fi].func.code[self.frames[fi].ip]; 858 self.frames[fi].ip += 1; 859 860 let Some(op) = Op::from_byte(opcode_byte) else { 861 return Err(RuntimeError { 862 kind: ErrorKind::Error, 863 message: format!("unknown opcode: 0x{opcode_byte:02X}"), 864 }); 865 }; 866 867 match op { 868 // ── Register loads ────────────────────────────── 869 Op::LoadConst => { 870 let dst = Self::read_u8(&mut self.frames[fi]); 871 let idx = Self::read_u16(&mut self.frames[fi]) as usize; 872 let base = self.frames[fi].base; 873 let val = match &self.frames[fi].func.constants[idx] { 874 Constant::Number(n) => Value::Number(*n), 875 Constant::String(s) => Value::String(s.clone()), 876 }; 877 self.registers[base + dst as usize] = val; 878 } 879 Op::LoadNull => { 880 let dst = Self::read_u8(&mut self.frames[fi]); 881 let base = self.frames[fi].base; 882 self.registers[base + dst as usize] = Value::Null; 883 } 884 Op::LoadUndefined => { 885 let dst = Self::read_u8(&mut self.frames[fi]); 886 let base = self.frames[fi].base; 887 self.registers[base + dst as usize] = Value::Undefined; 888 } 889 Op::LoadTrue => { 890 let dst = Self::read_u8(&mut self.frames[fi]); 891 let base = self.frames[fi].base; 892 self.registers[base + dst as usize] = Value::Boolean(true); 893 } 894 Op::LoadFalse => { 895 let dst = Self::read_u8(&mut self.frames[fi]); 896 let base = self.frames[fi].base; 897 self.registers[base + dst as usize] = Value::Boolean(false); 898 } 899 Op::LoadInt8 => { 900 let dst = Self::read_u8(&mut self.frames[fi]); 901 let val = Self::read_u8(&mut self.frames[fi]) as i8; 902 let base = self.frames[fi].base; 903 self.registers[base + dst as usize] = Value::Number(val as f64); 904 } 905 Op::Move => { 906 let dst = Self::read_u8(&mut self.frames[fi]); 907 let src = Self::read_u8(&mut self.frames[fi]); 908 let base = self.frames[fi].base; 909 let val = self.registers[base + src as usize].clone(); 910 self.registers[base + dst as usize] = val; 911 } 912 913 // ── Global access ────────────────────────────── 914 Op::LoadGlobal => { 915 let dst = Self::read_u8(&mut self.frames[fi]); 916 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 917 let base = self.frames[fi].base; 918 let name = &self.frames[fi].func.names[name_idx]; 919 let val = self.globals.get(name).cloned().unwrap_or(Value::Undefined); 920 self.registers[base + dst as usize] = val; 921 } 922 Op::StoreGlobal => { 923 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 924 let src = Self::read_u8(&mut self.frames[fi]); 925 let base = self.frames[fi].base; 926 let name = self.frames[fi].func.names[name_idx].clone(); 927 let val = self.registers[base + src as usize].clone(); 928 self.globals.insert(name, val); 929 } 930 931 // ── Arithmetic ───────────────────────────────── 932 Op::Add => { 933 let dst = Self::read_u8(&mut self.frames[fi]); 934 let lhs_r = Self::read_u8(&mut self.frames[fi]); 935 let rhs_r = Self::read_u8(&mut self.frames[fi]); 936 let base = self.frames[fi].base; 937 let result = add_values( 938 &self.registers[base + lhs_r as usize], 939 &self.registers[base + rhs_r as usize], 940 &self.gc, 941 ); 942 self.registers[base + dst as usize] = result; 943 } 944 Op::Sub => { 945 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 946 let result = self.registers[base + lhs_r].to_number() 947 - self.registers[base + rhs_r].to_number(); 948 self.registers[base + dst] = Value::Number(result); 949 } 950 Op::Mul => { 951 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 952 let result = self.registers[base + lhs_r].to_number() 953 * self.registers[base + rhs_r].to_number(); 954 self.registers[base + dst] = Value::Number(result); 955 } 956 Op::Div => { 957 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 958 let result = self.registers[base + lhs_r].to_number() 959 / self.registers[base + rhs_r].to_number(); 960 self.registers[base + dst] = Value::Number(result); 961 } 962 Op::Rem => { 963 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 964 let result = self.registers[base + lhs_r].to_number() 965 % self.registers[base + rhs_r].to_number(); 966 self.registers[base + dst] = Value::Number(result); 967 } 968 Op::Exp => { 969 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 970 let result = self.registers[base + lhs_r] 971 .to_number() 972 .powf(self.registers[base + rhs_r].to_number()); 973 self.registers[base + dst] = Value::Number(result); 974 } 975 Op::Neg => { 976 let dst = Self::read_u8(&mut self.frames[fi]); 977 let src = Self::read_u8(&mut self.frames[fi]); 978 let base = self.frames[fi].base; 979 let result = -self.registers[base + src as usize].to_number(); 980 self.registers[base + dst as usize] = Value::Number(result); 981 } 982 983 // ── Bitwise ──────────────────────────────────── 984 Op::BitAnd => { 985 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 986 let a = to_int32(&self.registers[base + lhs_r]); 987 let b = to_int32(&self.registers[base + rhs_r]); 988 self.registers[base + dst] = Value::Number((a & b) as f64); 989 } 990 Op::BitOr => { 991 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 992 let a = to_int32(&self.registers[base + lhs_r]); 993 let b = to_int32(&self.registers[base + rhs_r]); 994 self.registers[base + dst] = Value::Number((a | b) as f64); 995 } 996 Op::BitXor => { 997 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 998 let a = to_int32(&self.registers[base + lhs_r]); 999 let b = to_int32(&self.registers[base + rhs_r]); 1000 self.registers[base + dst] = Value::Number((a ^ b) as f64); 1001 } 1002 Op::ShiftLeft => { 1003 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1004 let a = to_int32(&self.registers[base + lhs_r]); 1005 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 1006 self.registers[base + dst] = Value::Number((a << b) as f64); 1007 } 1008 Op::ShiftRight => { 1009 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1010 let a = to_int32(&self.registers[base + lhs_r]); 1011 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 1012 self.registers[base + dst] = Value::Number((a >> b) as f64); 1013 } 1014 Op::UShiftRight => { 1015 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1016 let a = to_uint32(&self.registers[base + lhs_r]); 1017 let b = to_uint32(&self.registers[base + rhs_r]) & 0x1F; 1018 self.registers[base + dst] = Value::Number((a >> b) as f64); 1019 } 1020 Op::BitNot => { 1021 let dst = Self::read_u8(&mut self.frames[fi]); 1022 let src = Self::read_u8(&mut self.frames[fi]); 1023 let base = self.frames[fi].base; 1024 let result = !to_int32(&self.registers[base + src as usize]); 1025 self.registers[base + dst as usize] = Value::Number(result as f64); 1026 } 1027 1028 // ── Comparison ───────────────────────────────── 1029 Op::Eq => { 1030 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1031 let result = 1032 abstract_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 1033 self.registers[base + dst] = Value::Boolean(result); 1034 } 1035 Op::StrictEq => { 1036 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1037 let result = 1038 strict_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 1039 self.registers[base + dst] = Value::Boolean(result); 1040 } 1041 Op::NotEq => { 1042 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1043 let result = 1044 !abstract_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 1045 self.registers[base + dst] = Value::Boolean(result); 1046 } 1047 Op::StrictNotEq => { 1048 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1049 let result = 1050 !strict_eq(&self.registers[base + lhs_r], &self.registers[base + rhs_r]); 1051 self.registers[base + dst] = Value::Boolean(result); 1052 } 1053 Op::LessThan => { 1054 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1055 let result = abstract_relational( 1056 &self.registers[base + lhs_r], 1057 &self.registers[base + rhs_r], 1058 |ord| ord == std::cmp::Ordering::Less, 1059 ); 1060 self.registers[base + dst] = Value::Boolean(result); 1061 } 1062 Op::LessEq => { 1063 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1064 let result = abstract_relational( 1065 &self.registers[base + lhs_r], 1066 &self.registers[base + rhs_r], 1067 |ord| ord != std::cmp::Ordering::Greater, 1068 ); 1069 self.registers[base + dst] = Value::Boolean(result); 1070 } 1071 Op::GreaterThan => { 1072 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1073 let result = abstract_relational( 1074 &self.registers[base + lhs_r], 1075 &self.registers[base + rhs_r], 1076 |ord| ord == std::cmp::Ordering::Greater, 1077 ); 1078 self.registers[base + dst] = Value::Boolean(result); 1079 } 1080 Op::GreaterEq => { 1081 let (dst, lhs_r, rhs_r, base) = self.read_3reg(fi); 1082 let result = abstract_relational( 1083 &self.registers[base + lhs_r], 1084 &self.registers[base + rhs_r], 1085 |ord| ord != std::cmp::Ordering::Less, 1086 ); 1087 self.registers[base + dst] = Value::Boolean(result); 1088 } 1089 1090 // ── Logical / unary ──────────────────────────── 1091 Op::LogicalNot => { 1092 let dst = Self::read_u8(&mut self.frames[fi]); 1093 let src = Self::read_u8(&mut self.frames[fi]); 1094 let base = self.frames[fi].base; 1095 let result = !self.registers[base + src as usize].to_boolean(); 1096 self.registers[base + dst as usize] = Value::Boolean(result); 1097 } 1098 Op::TypeOf => { 1099 let dst = Self::read_u8(&mut self.frames[fi]); 1100 let src = Self::read_u8(&mut self.frames[fi]); 1101 let base = self.frames[fi].base; 1102 let t = self.registers[base + src as usize].type_of(); 1103 self.registers[base + dst as usize] = Value::String(t.to_string()); 1104 } 1105 Op::InstanceOf => { 1106 let dst = Self::read_u8(&mut self.frames[fi]); 1107 let lhs_r = Self::read_u8(&mut self.frames[fi]); 1108 let rhs_r = Self::read_u8(&mut self.frames[fi]); 1109 let base = self.frames[fi].base; 1110 let result = match ( 1111 &self.registers[base + lhs_r as usize], 1112 &self.registers[base + rhs_r as usize], 1113 ) { 1114 (Value::Object(obj_ref), Value::Function(ctor_ref)) => { 1115 gc_instanceof(&self.gc, *obj_ref, *ctor_ref) 1116 } 1117 (_, Value::Function(_)) => false, 1118 _ => { 1119 return Err(RuntimeError::type_error( 1120 "Right-hand side of instanceof is not callable", 1121 )); 1122 } 1123 }; 1124 self.registers[base + dst as usize] = Value::Boolean(result); 1125 } 1126 Op::In => { 1127 let dst = Self::read_u8(&mut self.frames[fi]); 1128 let key_r = Self::read_u8(&mut self.frames[fi]); 1129 let obj_r = Self::read_u8(&mut self.frames[fi]); 1130 let base = self.frames[fi].base; 1131 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 1132 let result = match self.registers[base + obj_r as usize] { 1133 Value::Object(gc_ref) => { 1134 Value::Boolean(gc_has_property(&self.gc, gc_ref, &key)) 1135 } 1136 _ => { 1137 return Err(RuntimeError::type_error( 1138 "Cannot use 'in' operator to search for property in non-object", 1139 )); 1140 } 1141 }; 1142 self.registers[base + dst as usize] = result; 1143 } 1144 Op::Void => { 1145 let dst = Self::read_u8(&mut self.frames[fi]); 1146 let _src = Self::read_u8(&mut self.frames[fi]); 1147 let base = self.frames[fi].base; 1148 self.registers[base + dst as usize] = Value::Undefined; 1149 } 1150 1151 // ── Control flow ─────────────────────────────── 1152 Op::Jump => { 1153 let offset = Self::read_i32(&mut self.frames[fi]); 1154 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 1155 } 1156 Op::JumpIfTrue => { 1157 let reg = Self::read_u8(&mut self.frames[fi]); 1158 let offset = Self::read_i32(&mut self.frames[fi]); 1159 let base = self.frames[fi].base; 1160 if self.registers[base + reg as usize].to_boolean() { 1161 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 1162 } 1163 } 1164 Op::JumpIfFalse => { 1165 let reg = Self::read_u8(&mut self.frames[fi]); 1166 let offset = Self::read_i32(&mut self.frames[fi]); 1167 let base = self.frames[fi].base; 1168 if !self.registers[base + reg as usize].to_boolean() { 1169 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 1170 } 1171 } 1172 Op::JumpIfNullish => { 1173 let reg = Self::read_u8(&mut self.frames[fi]); 1174 let offset = Self::read_i32(&mut self.frames[fi]); 1175 let base = self.frames[fi].base; 1176 if self.registers[base + reg as usize].is_nullish() { 1177 self.frames[fi].ip = (self.frames[fi].ip as i64 + offset as i64) as usize; 1178 } 1179 } 1180 1181 // ── Functions / calls ────────────────────────── 1182 Op::Call => { 1183 let dst = Self::read_u8(&mut self.frames[fi]); 1184 let func_r = Self::read_u8(&mut self.frames[fi]); 1185 let args_start = Self::read_u8(&mut self.frames[fi]); 1186 let arg_count = Self::read_u8(&mut self.frames[fi]); 1187 let base = self.frames[fi].base; 1188 1189 // Extract function GcRef. 1190 let func_gc_ref = match self.registers[base + func_r as usize] { 1191 Value::Function(r) => r, 1192 _ => { 1193 let desc = 1194 self.registers[base + func_r as usize].to_js_string(&self.gc); 1195 let err = RuntimeError::type_error(format!("{desc} is not a function")); 1196 let err_val = err.to_value(&mut self.gc); 1197 if !self.handle_exception(err_val) { 1198 return Err(err); 1199 } 1200 continue; 1201 } 1202 }; 1203 1204 // Collect arguments. 1205 let mut args = Vec::with_capacity(arg_count as usize); 1206 for i in 0..arg_count { 1207 args.push(self.registers[base + (args_start + i) as usize].clone()); 1208 } 1209 1210 // Read function data from GC (scoped borrow). 1211 let call_info = { 1212 match self.gc.get(func_gc_ref) { 1213 Some(HeapObject::Function(fdata)) => match &fdata.kind { 1214 FunctionKind::Native(n) => CallInfo::Native(n.callback), 1215 FunctionKind::Bytecode(bc) => { 1216 CallInfo::Bytecode(bc.func.clone(), fdata.upvalues.clone()) 1217 } 1218 }, 1219 _ => { 1220 let err = RuntimeError::type_error("not a function"); 1221 let err_val = err.to_value(&mut self.gc); 1222 if !self.handle_exception(err_val) { 1223 return Err(err); 1224 } 1225 continue; 1226 } 1227 } 1228 }; 1229 1230 match call_info { 1231 CallInfo::Native(callback) => { 1232 let this = self 1233 .globals 1234 .get("this") 1235 .cloned() 1236 .unwrap_or(Value::Undefined); 1237 let mut ctx = NativeContext { 1238 gc: &mut self.gc, 1239 this, 1240 }; 1241 match callback(&args, &mut ctx) { 1242 Ok(val) => { 1243 self.registers[base + dst as usize] = val; 1244 } 1245 Err(err) => { 1246 let err_val = err.to_value(&mut self.gc); 1247 if !self.handle_exception(err_val) { 1248 return Err(err); 1249 } 1250 } 1251 } 1252 } 1253 CallInfo::Bytecode(callee_func, callee_upvalues) => { 1254 if self.frames.len() >= MAX_CALL_DEPTH { 1255 let err = 1256 RuntimeError::range_error("Maximum call stack size exceeded"); 1257 let err_val = err.to_value(&mut self.gc); 1258 if !self.handle_exception(err_val) { 1259 return Err(err); 1260 } 1261 continue; 1262 } 1263 1264 let callee_base = base + self.frames[fi].func.register_count as usize; 1265 let callee_regs = callee_func.register_count as usize; 1266 self.ensure_registers(callee_base + callee_regs); 1267 1268 // Copy arguments into callee's registers. 1269 for i in 0..callee_func.param_count.min(arg_count) { 1270 self.registers[callee_base + i as usize] = args[i as usize].clone(); 1271 } 1272 // Fill remaining params with undefined. 1273 for i in arg_count..callee_func.param_count { 1274 self.registers[callee_base + i as usize] = Value::Undefined; 1275 } 1276 1277 self.frames.push(CallFrame { 1278 func: callee_func, 1279 ip: 0, 1280 base: callee_base, 1281 return_reg: base + dst as usize, 1282 exception_handlers: Vec::new(), 1283 upvalues: callee_upvalues, 1284 }); 1285 } 1286 } 1287 } 1288 Op::Return => { 1289 let reg = Self::read_u8(&mut self.frames[fi]); 1290 let base = self.frames[fi].base; 1291 let val = self.registers[base + reg as usize].clone(); 1292 1293 if self.frames.len() == 1 { 1294 self.frames.pop(); 1295 return Ok(val); 1296 } 1297 1298 let old = self.frames.pop().unwrap(); 1299 self.registers[old.return_reg] = val; 1300 } 1301 Op::Throw => { 1302 let reg = Self::read_u8(&mut self.frames[fi]); 1303 let base = self.frames[fi].base; 1304 let val = self.registers[base + reg as usize].clone(); 1305 1306 if !self.handle_exception(val) { 1307 let msg = self.registers[base + reg as usize].to_js_string(&self.gc); 1308 return Err(RuntimeError { 1309 kind: ErrorKind::Error, 1310 message: msg, 1311 }); 1312 } 1313 } 1314 Op::CreateClosure => { 1315 let dst = Self::read_u8(&mut self.frames[fi]); 1316 let func_idx = Self::read_u16(&mut self.frames[fi]) as usize; 1317 let base = self.frames[fi].base; 1318 let inner_func = self.frames[fi].func.functions[func_idx].clone(); 1319 let name = inner_func.name.clone(); 1320 1321 // Resolve upvalues from the parent scope. 1322 let mut upvalues = Vec::with_capacity(inner_func.upvalue_defs.len()); 1323 for def in &inner_func.upvalue_defs { 1324 let cell_ref = if def.is_local { 1325 // Parent has a cell in register `def.index`. 1326 match &self.registers[base + def.index as usize] { 1327 Value::Object(r) => *r, 1328 _ => { 1329 return Err(RuntimeError { 1330 kind: ErrorKind::Error, 1331 message: 1332 "CreateClosure: upvalue register does not hold a cell" 1333 .into(), 1334 }); 1335 } 1336 } 1337 } else { 1338 // Transitive: parent's own upvalue at `def.index`. 1339 self.frames[fi].upvalues[def.index as usize] 1340 }; 1341 upvalues.push(cell_ref); 1342 } 1343 1344 // Create a .prototype object for the function (for instanceof). 1345 let proto_obj = self.gc.alloc(HeapObject::Object(ObjectData::new())); 1346 let gc_ref = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1347 name, 1348 kind: FunctionKind::Bytecode(BytecodeFunc { func: inner_func }), 1349 prototype_obj: Some(proto_obj), 1350 properties: HashMap::new(), 1351 upvalues, 1352 }))); 1353 // Set .prototype.constructor = this function. 1354 if let Some(HeapObject::Object(data)) = self.gc.get_mut(proto_obj) { 1355 data.properties.insert( 1356 "constructor".to_string(), 1357 Property { 1358 value: Value::Function(gc_ref), 1359 writable: true, 1360 enumerable: false, 1361 configurable: true, 1362 }, 1363 ); 1364 } 1365 self.registers[base + dst as usize] = Value::Function(gc_ref); 1366 1367 // Trigger GC if needed. 1368 if self.gc.should_collect() { 1369 let roots = self.collect_roots(); 1370 self.gc.collect(&roots); 1371 } 1372 } 1373 1374 // ── Object / property ────────────────────────── 1375 Op::GetProperty => { 1376 let dst = Self::read_u8(&mut self.frames[fi]); 1377 let obj_r = Self::read_u8(&mut self.frames[fi]); 1378 let key_r = Self::read_u8(&mut self.frames[fi]); 1379 let base = self.frames[fi].base; 1380 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 1381 let val = match self.registers[base + obj_r as usize] { 1382 Value::Object(gc_ref) | Value::Function(gc_ref) => { 1383 gc_get_property(&self.gc, gc_ref, &key) 1384 } 1385 Value::String(ref s) => { 1386 let v = string_get_property(s, &key); 1387 if matches!(v, Value::Undefined) { 1388 self.string_prototype 1389 .map(|p| gc_get_property(&self.gc, p, &key)) 1390 .unwrap_or(Value::Undefined) 1391 } else { 1392 v 1393 } 1394 } 1395 Value::Number(_) => self 1396 .number_prototype 1397 .map(|p| gc_get_property(&self.gc, p, &key)) 1398 .unwrap_or(Value::Undefined), 1399 Value::Boolean(_) => self 1400 .boolean_prototype 1401 .map(|p| gc_get_property(&self.gc, p, &key)) 1402 .unwrap_or(Value::Undefined), 1403 _ => Value::Undefined, 1404 }; 1405 self.registers[base + dst as usize] = val; 1406 } 1407 Op::SetProperty => { 1408 let obj_r = Self::read_u8(&mut self.frames[fi]); 1409 let key_r = Self::read_u8(&mut self.frames[fi]); 1410 let val_r = Self::read_u8(&mut self.frames[fi]); 1411 let base = self.frames[fi].base; 1412 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 1413 let val = self.registers[base + val_r as usize].clone(); 1414 match self.registers[base + obj_r as usize] { 1415 Value::Object(gc_ref) => { 1416 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 1417 if let Some(prop) = data.properties.get_mut(&key) { 1418 if prop.writable { 1419 prop.value = val; 1420 } 1421 } else { 1422 data.properties.insert(key, Property::data(val)); 1423 } 1424 } 1425 } 1426 Value::Function(gc_ref) => { 1427 if let Some(HeapObject::Function(fdata)) = self.gc.get_mut(gc_ref) { 1428 if let Some(prop) = fdata.properties.get_mut(&key) { 1429 if prop.writable { 1430 prop.value = val; 1431 } 1432 } else { 1433 fdata.properties.insert(key, Property::data(val)); 1434 } 1435 } 1436 } 1437 _ => {} 1438 } 1439 } 1440 Op::CreateObject => { 1441 let dst = Self::read_u8(&mut self.frames[fi]); 1442 let base = self.frames[fi].base; 1443 let mut obj = ObjectData::new(); 1444 obj.prototype = self.object_prototype; 1445 let gc_ref = self.gc.alloc(HeapObject::Object(obj)); 1446 self.registers[base + dst as usize] = Value::Object(gc_ref); 1447 1448 if self.gc.should_collect() { 1449 let roots = self.collect_roots(); 1450 self.gc.collect(&roots); 1451 } 1452 } 1453 Op::CreateArray => { 1454 let dst = Self::read_u8(&mut self.frames[fi]); 1455 let base = self.frames[fi].base; 1456 let mut obj = ObjectData::new(); 1457 obj.prototype = self.array_prototype; 1458 obj.properties.insert( 1459 "length".to_string(), 1460 Property { 1461 value: Value::Number(0.0), 1462 writable: true, 1463 enumerable: false, 1464 configurable: false, 1465 }, 1466 ); 1467 let gc_ref = self.gc.alloc(HeapObject::Object(obj)); 1468 self.registers[base + dst as usize] = Value::Object(gc_ref); 1469 1470 if self.gc.should_collect() { 1471 let roots = self.collect_roots(); 1472 self.gc.collect(&roots); 1473 } 1474 } 1475 Op::GetPropertyByName => { 1476 let dst = Self::read_u8(&mut self.frames[fi]); 1477 let obj_r = Self::read_u8(&mut self.frames[fi]); 1478 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 1479 let base = self.frames[fi].base; 1480 let key = self.frames[fi].func.names[name_idx].clone(); 1481 let val = match self.registers[base + obj_r as usize] { 1482 Value::Object(gc_ref) | Value::Function(gc_ref) => { 1483 gc_get_property(&self.gc, gc_ref, &key) 1484 } 1485 Value::String(ref s) => { 1486 let v = string_get_property(s, &key); 1487 if matches!(v, Value::Undefined) { 1488 self.string_prototype 1489 .map(|p| gc_get_property(&self.gc, p, &key)) 1490 .unwrap_or(Value::Undefined) 1491 } else { 1492 v 1493 } 1494 } 1495 Value::Number(_) => self 1496 .number_prototype 1497 .map(|p| gc_get_property(&self.gc, p, &key)) 1498 .unwrap_or(Value::Undefined), 1499 Value::Boolean(_) => self 1500 .boolean_prototype 1501 .map(|p| gc_get_property(&self.gc, p, &key)) 1502 .unwrap_or(Value::Undefined), 1503 _ => Value::Undefined, 1504 }; 1505 self.registers[base + dst as usize] = val; 1506 } 1507 Op::SetPropertyByName => { 1508 let obj_r = Self::read_u8(&mut self.frames[fi]); 1509 let name_idx = Self::read_u16(&mut self.frames[fi]) as usize; 1510 let val_r = Self::read_u8(&mut self.frames[fi]); 1511 let base = self.frames[fi].base; 1512 let key = self.frames[fi].func.names[name_idx].clone(); 1513 let val = self.registers[base + val_r as usize].clone(); 1514 match self.registers[base + obj_r as usize] { 1515 Value::Object(gc_ref) => { 1516 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 1517 if let Some(prop) = data.properties.get_mut(&key) { 1518 if prop.writable { 1519 prop.value = val; 1520 } 1521 } else { 1522 data.properties.insert(key, Property::data(val)); 1523 } 1524 } 1525 } 1526 Value::Function(gc_ref) => { 1527 if let Some(HeapObject::Function(fdata)) = self.gc.get_mut(gc_ref) { 1528 if let Some(prop) = fdata.properties.get_mut(&key) { 1529 if prop.writable { 1530 prop.value = val; 1531 } 1532 } else { 1533 fdata.properties.insert(key, Property::data(val)); 1534 } 1535 } 1536 } 1537 _ => {} 1538 } 1539 } 1540 1541 // ── Misc ─────────────────────────────────────── 1542 Op::Delete => { 1543 let dst = Self::read_u8(&mut self.frames[fi]); 1544 let obj_r = Self::read_u8(&mut self.frames[fi]); 1545 let key_r = Self::read_u8(&mut self.frames[fi]); 1546 let base = self.frames[fi].base; 1547 let key = self.registers[base + key_r as usize].to_js_string(&self.gc); 1548 let result = 1549 if let Value::Object(gc_ref) = self.registers[base + obj_r as usize] { 1550 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 1551 match data.properties.get(&key) { 1552 Some(prop) if !prop.configurable => false, 1553 Some(_) => { 1554 data.properties.remove(&key); 1555 true 1556 } 1557 None => true, 1558 } 1559 } else { 1560 true 1561 } 1562 } else { 1563 true 1564 }; 1565 self.registers[base + dst as usize] = Value::Boolean(result); 1566 } 1567 Op::ForInInit => { 1568 let dst = Self::read_u8(&mut self.frames[fi]); 1569 let obj_r = Self::read_u8(&mut self.frames[fi]); 1570 let base = self.frames[fi].base; 1571 let keys = match self.registers[base + obj_r as usize] { 1572 Value::Object(gc_ref) => gc_enumerate_keys(&self.gc, gc_ref), 1573 _ => Vec::new(), 1574 }; 1575 // Store keys as an array object. 1576 let mut arr = ObjectData::new(); 1577 for (i, key) in keys.iter().enumerate() { 1578 arr.properties 1579 .insert(i.to_string(), Property::data(Value::String(key.clone()))); 1580 } 1581 arr.properties.insert( 1582 "length".to_string(), 1583 Property { 1584 value: Value::Number(keys.len() as f64), 1585 writable: true, 1586 enumerable: false, 1587 configurable: false, 1588 }, 1589 ); 1590 let gc_ref = self.gc.alloc(HeapObject::Object(arr)); 1591 self.registers[base + dst as usize] = Value::Object(gc_ref); 1592 } 1593 Op::ForInNext => { 1594 let dst_val = Self::read_u8(&mut self.frames[fi]); 1595 let dst_done = Self::read_u8(&mut self.frames[fi]); 1596 let keys_r = Self::read_u8(&mut self.frames[fi]); 1597 let idx_r = Self::read_u8(&mut self.frames[fi]); 1598 let base = self.frames[fi].base; 1599 let idx = self.registers[base + idx_r as usize].to_number() as usize; 1600 let len = match self.registers[base + keys_r as usize] { 1601 Value::Object(gc_ref) => { 1602 gc_get_property(&self.gc, gc_ref, "length").to_number() as usize 1603 } 1604 _ => 0, 1605 }; 1606 if idx >= len { 1607 self.registers[base + dst_done as usize] = Value::Boolean(true); 1608 self.registers[base + dst_val as usize] = Value::Undefined; 1609 } else { 1610 let key_str = idx.to_string(); 1611 let key = match self.registers[base + keys_r as usize] { 1612 Value::Object(gc_ref) => gc_get_property(&self.gc, gc_ref, &key_str), 1613 _ => Value::Undefined, 1614 }; 1615 self.registers[base + dst_val as usize] = key; 1616 self.registers[base + dst_done as usize] = Value::Boolean(false); 1617 } 1618 } 1619 Op::SetPrototype => { 1620 let obj_r = Self::read_u8(&mut self.frames[fi]); 1621 let proto_r = Self::read_u8(&mut self.frames[fi]); 1622 let base = self.frames[fi].base; 1623 let proto = match &self.registers[base + proto_r as usize] { 1624 Value::Object(r) => Some(*r), 1625 Value::Null => None, 1626 _ => None, 1627 }; 1628 if let Value::Object(gc_ref) = self.registers[base + obj_r as usize] { 1629 if let Some(HeapObject::Object(data)) = self.gc.get_mut(gc_ref) { 1630 data.prototype = proto; 1631 } 1632 } 1633 } 1634 Op::GetPrototype => { 1635 let dst = Self::read_u8(&mut self.frames[fi]); 1636 let obj_r = Self::read_u8(&mut self.frames[fi]); 1637 let base = self.frames[fi].base; 1638 let proto = match self.registers[base + obj_r as usize] { 1639 Value::Object(gc_ref) => match self.gc.get(gc_ref) { 1640 Some(HeapObject::Object(data)) => { 1641 data.prototype.map(Value::Object).unwrap_or(Value::Null) 1642 } 1643 _ => Value::Null, 1644 }, 1645 _ => Value::Null, 1646 }; 1647 self.registers[base + dst as usize] = proto; 1648 } 1649 1650 // ── Exception handling ───────────────────────────── 1651 Op::PushExceptionHandler => { 1652 let catch_reg = Self::read_u8(&mut self.frames[fi]); 1653 let offset = Self::read_i32(&mut self.frames[fi]); 1654 let catch_ip = (self.frames[fi].ip as i32 + offset) as usize; 1655 self.frames[fi].exception_handlers.push(ExceptionHandler { 1656 catch_ip, 1657 catch_reg, 1658 }); 1659 } 1660 Op::PopExceptionHandler => { 1661 self.frames[fi].exception_handlers.pop(); 1662 } 1663 1664 // ── Closure / upvalue ops ───────────────────────── 1665 Op::NewCell => { 1666 let dst = Self::read_u8(&mut self.frames[fi]); 1667 let base = self.frames[fi].base; 1668 let cell = self.gc.alloc(HeapObject::Cell(Value::Undefined)); 1669 self.registers[base + dst as usize] = Value::Object(cell); 1670 1671 if self.gc.should_collect() { 1672 let roots = self.collect_roots(); 1673 self.gc.collect(&roots); 1674 } 1675 } 1676 Op::CellLoad => { 1677 let dst = Self::read_u8(&mut self.frames[fi]); 1678 let cell_reg = Self::read_u8(&mut self.frames[fi]); 1679 let base = self.frames[fi].base; 1680 let cell_ref = match &self.registers[base + cell_reg as usize] { 1681 Value::Object(r) => *r, 1682 _ => { 1683 return Err(RuntimeError { 1684 kind: ErrorKind::Error, 1685 message: "CellLoad: register does not hold a cell".into(), 1686 }); 1687 } 1688 }; 1689 let val = match self.gc.get(cell_ref) { 1690 Some(HeapObject::Cell(v)) => v.clone(), 1691 _ => Value::Undefined, 1692 }; 1693 self.registers[base + dst as usize] = val; 1694 } 1695 Op::CellStore => { 1696 let cell_reg = Self::read_u8(&mut self.frames[fi]); 1697 let src = Self::read_u8(&mut self.frames[fi]); 1698 let base = self.frames[fi].base; 1699 let cell_ref = match &self.registers[base + cell_reg as usize] { 1700 Value::Object(r) => *r, 1701 _ => { 1702 return Err(RuntimeError { 1703 kind: ErrorKind::Error, 1704 message: "CellStore: register does not hold a cell".into(), 1705 }); 1706 } 1707 }; 1708 let val = self.registers[base + src as usize].clone(); 1709 if let Some(HeapObject::Cell(cell_val)) = self.gc.get_mut(cell_ref) { 1710 *cell_val = val; 1711 } 1712 } 1713 Op::LoadUpvalue => { 1714 let dst = Self::read_u8(&mut self.frames[fi]); 1715 let idx = Self::read_u8(&mut self.frames[fi]) as usize; 1716 let base = self.frames[fi].base; 1717 let cell_ref = self.frames[fi].upvalues[idx]; 1718 let val = match self.gc.get(cell_ref) { 1719 Some(HeapObject::Cell(v)) => v.clone(), 1720 _ => Value::Undefined, 1721 }; 1722 self.registers[base + dst as usize] = val; 1723 } 1724 Op::StoreUpvalue => { 1725 let idx = Self::read_u8(&mut self.frames[fi]) as usize; 1726 let src = Self::read_u8(&mut self.frames[fi]); 1727 let base = self.frames[fi].base; 1728 let val = self.registers[base + src as usize].clone(); 1729 let cell_ref = self.frames[fi].upvalues[idx]; 1730 if let Some(HeapObject::Cell(cell_val)) = self.gc.get_mut(cell_ref) { 1731 *cell_val = val; 1732 } 1733 } 1734 } 1735 } 1736 } 1737 1738 /// Read 3 register operands and return (dst, lhs, rhs, base) as usize indices. 1739 fn read_3reg(&mut self, fi: usize) -> (usize, usize, usize, usize) { 1740 let dst = Self::read_u8(&mut self.frames[fi]) as usize; 1741 let lhs = Self::read_u8(&mut self.frames[fi]) as usize; 1742 let rhs = Self::read_u8(&mut self.frames[fi]) as usize; 1743 let base = self.frames[fi].base; 1744 (dst, lhs, rhs, base) 1745 } 1746 1747 /// Try to find an exception handler on the call stack. 1748 /// Returns true if a handler was found (execution resumes there). 1749 fn handle_exception(&mut self, value: Value) -> bool { 1750 while let Some(frame) = self.frames.last_mut() { 1751 if let Some(handler) = frame.exception_handlers.pop() { 1752 let base = frame.base; 1753 frame.ip = handler.catch_ip; 1754 self.registers[base + handler.catch_reg as usize] = value; 1755 return true; 1756 } 1757 if self.frames.len() == 1 { 1758 break; 1759 } 1760 self.frames.pop(); 1761 } 1762 false 1763 } 1764 1765 /// Register a native function as a global. 1766 pub fn define_native( 1767 &mut self, 1768 name: &str, 1769 callback: fn(&[Value], &mut NativeContext) -> Result<Value, RuntimeError>, 1770 ) { 1771 let gc_ref = self.gc.alloc(HeapObject::Function(Box::new(FunctionData { 1772 name: name.to_string(), 1773 kind: FunctionKind::Native(NativeFunc { callback }), 1774 prototype_obj: None, 1775 properties: HashMap::new(), 1776 upvalues: Vec::new(), 1777 }))); 1778 self.globals 1779 .insert(name.to_string(), Value::Function(gc_ref)); 1780 } 1781 1782 /// Get a global variable value. 1783 pub fn get_global(&self, name: &str) -> Option<&Value> { 1784 self.globals.get(name) 1785 } 1786 1787 /// Set a global variable. 1788 pub fn set_global(&mut self, name: &str, val: Value) { 1789 self.globals.insert(name.to_string(), val); 1790 } 1791} 1792 1793impl Default for Vm { 1794 fn default() -> Self { 1795 Self::new() 1796 } 1797} 1798 1799/// Internal enum to avoid holding a GC borrow across the call setup. 1800enum CallInfo { 1801 Native(fn(&[Value], &mut NativeContext) -> Result<Value, RuntimeError>), 1802 Bytecode(Function, Vec<GcRef>), 1803} 1804 1805// ── Tests ──────────────────────────────────────────────────── 1806 1807#[cfg(test)] 1808mod tests { 1809 use super::*; 1810 use crate::bytecode::{BytecodeBuilder, Constant, Op}; 1811 use crate::compiler; 1812 use crate::parser::Parser; 1813 1814 /// Helper: compile and execute JS source, return the completion value. 1815 fn eval(source: &str) -> Result<Value, RuntimeError> { 1816 let program = Parser::parse(source).expect("parse failed"); 1817 let func = compiler::compile(&program).expect("compile failed"); 1818 let mut vm = Vm::new(); 1819 vm.execute(&func) 1820 } 1821 1822 // ── Value tests ───────────────────────────────────────── 1823 1824 #[test] 1825 fn test_to_boolean() { 1826 let mut gc: Gc<HeapObject> = Gc::new(); 1827 assert!(!Value::Undefined.to_boolean()); 1828 assert!(!Value::Null.to_boolean()); 1829 assert!(!Value::Boolean(false).to_boolean()); 1830 assert!(Value::Boolean(true).to_boolean()); 1831 assert!(!Value::Number(0.0).to_boolean()); 1832 assert!(!Value::Number(f64::NAN).to_boolean()); 1833 assert!(Value::Number(1.0).to_boolean()); 1834 assert!(!Value::String(String::new()).to_boolean()); 1835 assert!(Value::String("hello".to_string()).to_boolean()); 1836 let obj_ref = gc.alloc(HeapObject::Object(ObjectData::new())); 1837 assert!(Value::Object(obj_ref).to_boolean()); 1838 } 1839 1840 #[test] 1841 fn test_to_number() { 1842 assert!(Value::Undefined.to_number().is_nan()); 1843 assert_eq!(Value::Null.to_number(), 0.0); 1844 assert_eq!(Value::Boolean(true).to_number(), 1.0); 1845 assert_eq!(Value::Boolean(false).to_number(), 0.0); 1846 assert_eq!(Value::Number(42.0).to_number(), 42.0); 1847 assert_eq!(Value::String("42".to_string()).to_number(), 42.0); 1848 assert_eq!(Value::String("".to_string()).to_number(), 0.0); 1849 assert!(Value::String("abc".to_string()).to_number().is_nan()); 1850 } 1851 1852 #[test] 1853 fn test_type_of() { 1854 let mut gc: Gc<HeapObject> = Gc::new(); 1855 assert_eq!(Value::Undefined.type_of(), "undefined"); 1856 assert_eq!(Value::Null.type_of(), "object"); 1857 assert_eq!(Value::Boolean(true).type_of(), "boolean"); 1858 assert_eq!(Value::Number(1.0).type_of(), "number"); 1859 assert_eq!(Value::String("hi".to_string()).type_of(), "string"); 1860 let obj_ref = gc.alloc(HeapObject::Object(ObjectData::new())); 1861 assert_eq!(Value::Object(obj_ref).type_of(), "object"); 1862 } 1863 1864 // ── VM bytecode-level tests ───────────────────────────── 1865 1866 #[test] 1867 fn test_load_const_number() { 1868 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1869 b.func.register_count = 1; 1870 let ci = b.add_constant(Constant::Number(42.0)); 1871 b.emit_reg_u16(Op::LoadConst, 0, ci); 1872 b.emit_reg(Op::Return, 0); 1873 let func = b.finish(); 1874 1875 let mut vm = Vm::new(); 1876 let result = vm.execute(&func).unwrap(); 1877 match result { 1878 Value::Number(n) => assert_eq!(n, 42.0), 1879 _ => panic!("expected Number, got {result:?}"), 1880 } 1881 } 1882 1883 #[test] 1884 fn test_arithmetic_ops() { 1885 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1886 b.func.register_count = 3; 1887 let c10 = b.add_constant(Constant::Number(10.0)); 1888 let c3 = b.add_constant(Constant::Number(3.0)); 1889 b.emit_reg_u16(Op::LoadConst, 0, c10); 1890 b.emit_reg_u16(Op::LoadConst, 1, c3); 1891 b.emit_reg3(Op::Add, 2, 0, 1); 1892 b.emit_reg(Op::Return, 2); 1893 let func = b.finish(); 1894 1895 let mut vm = Vm::new(); 1896 match vm.execute(&func).unwrap() { 1897 Value::Number(n) => assert_eq!(n, 13.0), 1898 v => panic!("expected 13, got {v:?}"), 1899 } 1900 } 1901 1902 #[test] 1903 fn test_string_concat() { 1904 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1905 b.func.register_count = 3; 1906 let c1 = b.add_constant(Constant::String("hello".into())); 1907 let c2 = b.add_constant(Constant::String(" world".into())); 1908 b.emit_reg_u16(Op::LoadConst, 0, c1); 1909 b.emit_reg_u16(Op::LoadConst, 1, c2); 1910 b.emit_reg3(Op::Add, 2, 0, 1); 1911 b.emit_reg(Op::Return, 2); 1912 let func = b.finish(); 1913 1914 let mut vm = Vm::new(); 1915 match vm.execute(&func).unwrap() { 1916 Value::String(s) => assert_eq!(s, "hello world"), 1917 v => panic!("expected string, got {v:?}"), 1918 } 1919 } 1920 1921 #[test] 1922 fn test_jump_if_false() { 1923 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1924 b.func.register_count = 2; 1925 b.emit_reg(Op::LoadFalse, 0); 1926 let patch = b.emit_cond_jump(Op::JumpIfFalse, 0); 1927 b.emit_load_int8(1, 1); 1928 let skip = b.emit_jump(Op::Jump); 1929 b.patch_jump(patch); 1930 b.emit_load_int8(1, 2); 1931 b.patch_jump(skip); 1932 b.emit_reg(Op::Return, 1); 1933 let func = b.finish(); 1934 1935 let mut vm = Vm::new(); 1936 match vm.execute(&func).unwrap() { 1937 Value::Number(n) => assert_eq!(n, 2.0), 1938 v => panic!("expected 2, got {v:?}"), 1939 } 1940 } 1941 1942 #[test] 1943 fn test_globals() { 1944 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1945 b.func.register_count = 2; 1946 let name = b.add_name("x"); 1947 b.emit_load_int8(0, 42); 1948 b.emit_store_global(name, 0); 1949 b.emit_load_global(1, name); 1950 b.emit_reg(Op::Return, 1); 1951 let func = b.finish(); 1952 1953 let mut vm = Vm::new(); 1954 match vm.execute(&func).unwrap() { 1955 Value::Number(n) => assert_eq!(n, 42.0), 1956 v => panic!("expected 42, got {v:?}"), 1957 } 1958 } 1959 1960 #[test] 1961 fn test_function_call() { 1962 let mut inner_b = BytecodeBuilder::new("add1".into(), 1); 1963 inner_b.func.register_count = 2; 1964 inner_b.emit_load_int8(1, 1); 1965 inner_b.emit_reg3(Op::Add, 0, 0, 1); 1966 inner_b.emit_reg(Op::Return, 0); 1967 let inner = inner_b.finish(); 1968 1969 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1970 b.func.register_count = 4; 1971 let fi = b.add_function(inner); 1972 b.emit_reg_u16(Op::CreateClosure, 0, fi); 1973 b.emit_load_int8(1, 10); 1974 b.emit_call(2, 0, 1, 1); 1975 b.emit_reg(Op::Return, 2); 1976 let func = b.finish(); 1977 1978 let mut vm = Vm::new(); 1979 match vm.execute(&func).unwrap() { 1980 Value::Number(n) => assert_eq!(n, 11.0), 1981 v => panic!("expected 11, got {v:?}"), 1982 } 1983 } 1984 1985 #[test] 1986 fn test_native_function() { 1987 let mut b = BytecodeBuilder::new("<test>".into(), 0); 1988 b.func.register_count = 3; 1989 let name = b.add_name("double"); 1990 b.emit_load_global(0, name); 1991 b.emit_load_int8(1, 21); 1992 b.emit_call(2, 0, 1, 1); 1993 b.emit_reg(Op::Return, 2); 1994 let func = b.finish(); 1995 1996 let mut vm = Vm::new(); 1997 vm.define_native("double", |args, _ctx| { 1998 let n = args.first().unwrap_or(&Value::Undefined).to_number(); 1999 Ok(Value::Number(n * 2.0)) 2000 }); 2001 match vm.execute(&func).unwrap() { 2002 Value::Number(n) => assert_eq!(n, 42.0), 2003 v => panic!("expected 42, got {v:?}"), 2004 } 2005 } 2006 2007 #[test] 2008 fn test_object_property() { 2009 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2010 b.func.register_count = 3; 2011 let name = b.add_name("x"); 2012 b.emit_reg(Op::CreateObject, 0); 2013 b.emit_load_int8(1, 42); 2014 b.emit_set_prop_name(0, name, 1); 2015 b.emit_get_prop_name(2, 0, name); 2016 b.emit_reg(Op::Return, 2); 2017 let func = b.finish(); 2018 2019 let mut vm = Vm::new(); 2020 match vm.execute(&func).unwrap() { 2021 Value::Number(n) => assert_eq!(n, 42.0), 2022 v => panic!("expected 42, got {v:?}"), 2023 } 2024 } 2025 2026 #[test] 2027 fn test_typeof_operator() { 2028 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2029 b.func.register_count = 2; 2030 b.emit_load_int8(0, 5); 2031 b.emit_reg_reg(Op::TypeOf, 1, 0); 2032 b.emit_reg(Op::Return, 1); 2033 let func = b.finish(); 2034 2035 let mut vm = Vm::new(); 2036 match vm.execute(&func).unwrap() { 2037 Value::String(s) => assert_eq!(s, "number"), 2038 v => panic!("expected 'number', got {v:?}"), 2039 } 2040 } 2041 2042 #[test] 2043 fn test_comparison() { 2044 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2045 b.func.register_count = 3; 2046 b.emit_load_int8(0, 5); 2047 b.emit_load_int8(1, 10); 2048 b.emit_reg3(Op::LessThan, 2, 0, 1); 2049 b.emit_reg(Op::Return, 2); 2050 let func = b.finish(); 2051 2052 let mut vm = Vm::new(); 2053 match vm.execute(&func).unwrap() { 2054 Value::Boolean(b) => assert!(b), 2055 v => panic!("expected true, got {v:?}"), 2056 } 2057 } 2058 2059 #[test] 2060 fn test_abstract_equality() { 2061 assert!(abstract_eq(&Value::Null, &Value::Undefined)); 2062 assert!(abstract_eq(&Value::Undefined, &Value::Null)); 2063 assert!(abstract_eq( 2064 &Value::Number(1.0), 2065 &Value::String("1".to_string()) 2066 )); 2067 assert!(abstract_eq(&Value::Boolean(true), &Value::Number(1.0))); 2068 assert!(!abstract_eq(&Value::Number(0.0), &Value::Null)); 2069 } 2070 2071 #[test] 2072 fn test_strict_equality() { 2073 assert!(strict_eq(&Value::Number(1.0), &Value::Number(1.0))); 2074 assert!(!strict_eq( 2075 &Value::Number(1.0), 2076 &Value::String("1".to_string()) 2077 )); 2078 assert!(strict_eq(&Value::Null, &Value::Null)); 2079 assert!(!strict_eq(&Value::Null, &Value::Undefined)); 2080 } 2081 2082 #[test] 2083 fn test_bitwise_ops() { 2084 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2085 b.func.register_count = 3; 2086 b.emit_load_int8(0, 0x0F); 2087 b.emit_load_int8(1, 0x03); 2088 b.emit_reg3(Op::BitAnd, 2, 0, 1); 2089 b.emit_reg(Op::Return, 2); 2090 let func = b.finish(); 2091 2092 let mut vm = Vm::new(); 2093 match vm.execute(&func).unwrap() { 2094 Value::Number(n) => assert_eq!(n, 3.0), 2095 v => panic!("expected 3, got {v:?}"), 2096 } 2097 } 2098 2099 #[test] 2100 fn test_throw_uncaught() { 2101 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2102 b.func.register_count = 1; 2103 let ci = b.add_constant(Constant::String("oops".into())); 2104 b.emit_reg_u16(Op::LoadConst, 0, ci); 2105 b.emit_reg(Op::Throw, 0); 2106 let func = b.finish(); 2107 2108 let mut vm = Vm::new(); 2109 let err = vm.execute(&func).unwrap_err(); 2110 assert_eq!(err.message, "oops"); 2111 } 2112 2113 #[test] 2114 fn test_loop_counting() { 2115 let mut b = BytecodeBuilder::new("<test>".into(), 0); 2116 b.func.register_count = 3; 2117 b.emit_load_int8(0, 0); 2118 b.emit_load_int8(1, 5); 2119 let loop_start = b.offset(); 2120 b.emit_reg3(Op::LessThan, 2, 0, 1); 2121 let exit_patch = b.emit_cond_jump(Op::JumpIfFalse, 2); 2122 b.emit_load_int8(2, 1); 2123 b.emit_reg3(Op::Add, 0, 0, 2); 2124 b.emit_jump_to(loop_start); 2125 b.patch_jump(exit_patch); 2126 b.emit_reg(Op::Return, 0); 2127 let func = b.finish(); 2128 2129 let mut vm = Vm::new(); 2130 match vm.execute(&func).unwrap() { 2131 Value::Number(n) => assert_eq!(n, 5.0), 2132 v => panic!("expected 5, got {v:?}"), 2133 } 2134 } 2135 2136 // ── End-to-end (compile + execute) tests ──────────────── 2137 2138 #[test] 2139 fn test_e2e_arithmetic() { 2140 match eval("2 + 3 * 4").unwrap() { 2141 Value::Number(n) => assert_eq!(n, 14.0), 2142 v => panic!("expected 14, got {v:?}"), 2143 } 2144 } 2145 2146 #[test] 2147 fn test_e2e_variables() { 2148 match eval("var x = 10; var y = 20; x + y").unwrap() { 2149 Value::Number(n) => assert_eq!(n, 30.0), 2150 v => panic!("expected 30, got {v:?}"), 2151 } 2152 } 2153 2154 #[test] 2155 fn test_e2e_if_else() { 2156 match eval("var x = 5; if (x > 3) { x = 100; } x").unwrap() { 2157 Value::Number(n) => assert_eq!(n, 100.0), 2158 v => panic!("expected 100, got {v:?}"), 2159 } 2160 } 2161 2162 #[test] 2163 fn test_e2e_while_loop() { 2164 match eval("var i = 0; var sum = 0; while (i < 10) { sum = sum + i; i = i + 1; } sum") 2165 .unwrap() 2166 { 2167 Value::Number(n) => assert_eq!(n, 45.0), 2168 v => panic!("expected 45, got {v:?}"), 2169 } 2170 } 2171 2172 #[test] 2173 fn test_e2e_function_call() { 2174 match eval("function add(a, b) { return a + b; } add(3, 4)").unwrap() { 2175 Value::Number(n) => assert_eq!(n, 7.0), 2176 v => panic!("expected 7, got {v:?}"), 2177 } 2178 } 2179 2180 #[test] 2181 fn test_e2e_recursive_factorial() { 2182 let src = r#" 2183 function fact(n) { 2184 if (n <= 1) return 1; 2185 return n * fact(n - 1); 2186 } 2187 fact(6) 2188 "#; 2189 match eval(src).unwrap() { 2190 Value::Number(n) => assert_eq!(n, 720.0), 2191 v => panic!("expected 720, got {v:?}"), 2192 } 2193 } 2194 2195 #[test] 2196 fn test_e2e_string_concat() { 2197 match eval("'hello' + ' ' + 'world'").unwrap() { 2198 Value::String(s) => assert_eq!(s, "hello world"), 2199 v => panic!("expected 'hello world', got {v:?}"), 2200 } 2201 } 2202 2203 #[test] 2204 fn test_e2e_comparison_coercion() { 2205 match eval("1 == '1'").unwrap() { 2206 Value::Boolean(b) => assert!(b), 2207 v => panic!("expected true, got {v:?}"), 2208 } 2209 match eval("1 === '1'").unwrap() { 2210 Value::Boolean(b) => assert!(!b), 2211 v => panic!("expected false, got {v:?}"), 2212 } 2213 } 2214 2215 #[test] 2216 fn test_e2e_typeof() { 2217 match eval("typeof 42").unwrap() { 2218 Value::String(s) => assert_eq!(s, "number"), 2219 v => panic!("expected 'number', got {v:?}"), 2220 } 2221 match eval("typeof 'hello'").unwrap() { 2222 Value::String(s) => assert_eq!(s, "string"), 2223 v => panic!("expected 'string', got {v:?}"), 2224 } 2225 } 2226 2227 #[test] 2228 fn test_e2e_object_literal() { 2229 match eval("var o = { x: 10, y: 20 }; o.x + o.y").unwrap() { 2230 Value::Number(n) => assert_eq!(n, 30.0), 2231 v => panic!("expected 30, got {v:?}"), 2232 } 2233 } 2234 2235 #[test] 2236 fn test_e2e_for_loop() { 2237 match eval("var s = 0; for (var i = 0; i < 5; i = i + 1) { s = s + i; } s").unwrap() { 2238 Value::Number(n) => assert_eq!(n, 10.0), 2239 v => panic!("expected 10, got {v:?}"), 2240 } 2241 } 2242 2243 #[test] 2244 fn test_e2e_nested_functions() { 2245 // Note: closures (capturing parent scope vars) not yet supported. 2246 // This test verifies nested function declarations and calls work. 2247 let src = r#" 2248 function outer(x) { 2249 function inner(y) { 2250 return y + 1; 2251 } 2252 return inner(x); 2253 } 2254 outer(5) 2255 "#; 2256 match eval(src).unwrap() { 2257 Value::Number(n) => assert_eq!(n, 6.0), 2258 v => panic!("expected 6, got {v:?}"), 2259 } 2260 } 2261 2262 #[test] 2263 fn test_e2e_logical_operators() { 2264 match eval("true && false").unwrap() { 2265 Value::Boolean(b) => assert!(!b), 2266 v => panic!("expected false, got {v:?}"), 2267 } 2268 match eval("false || true").unwrap() { 2269 Value::Boolean(b) => assert!(b), 2270 v => panic!("expected true, got {v:?}"), 2271 } 2272 } 2273 2274 #[test] 2275 fn test_e2e_unary_neg() { 2276 match eval("-(5 + 3)").unwrap() { 2277 Value::Number(n) => assert_eq!(n, -8.0), 2278 v => panic!("expected -8, got {v:?}"), 2279 } 2280 } 2281 2282 #[test] 2283 fn test_e2e_ternary() { 2284 match eval("true ? 1 : 2").unwrap() { 2285 Value::Number(n) => assert_eq!(n, 1.0), 2286 v => panic!("expected 1, got {v:?}"), 2287 } 2288 match eval("false ? 1 : 2").unwrap() { 2289 Value::Number(n) => assert_eq!(n, 2.0), 2290 v => panic!("expected 2, got {v:?}"), 2291 } 2292 } 2293 2294 #[test] 2295 fn test_e2e_fibonacci() { 2296 let src = r#" 2297 function fib(n) { 2298 if (n <= 1) return n; 2299 return fib(n - 1) + fib(n - 2); 2300 } 2301 fib(10) 2302 "#; 2303 match eval(src).unwrap() { 2304 Value::Number(n) => assert_eq!(n, 55.0), 2305 v => panic!("expected 55, got {v:?}"), 2306 } 2307 } 2308 2309 // ── GC integration tests ──────────────────────────────── 2310 2311 #[test] 2312 fn test_gc_object_survives_collection() { 2313 let src = r#" 2314 var o = { x: 42 }; 2315 o.x 2316 "#; 2317 match eval(src).unwrap() { 2318 Value::Number(n) => assert_eq!(n, 42.0), 2319 v => panic!("expected 42, got {v:?}"), 2320 } 2321 } 2322 2323 #[test] 2324 fn test_gc_many_objects() { 2325 // Allocate many objects to trigger GC threshold. 2326 let src = r#" 2327 var sum = 0; 2328 var i = 0; 2329 while (i < 100) { 2330 var o = { val: i }; 2331 sum = sum + o.val; 2332 i = i + 1; 2333 } 2334 sum 2335 "#; 2336 match eval(src).unwrap() { 2337 Value::Number(n) => assert_eq!(n, 4950.0), 2338 v => panic!("expected 4950, got {v:?}"), 2339 } 2340 } 2341 2342 #[test] 2343 fn test_gc_reference_identity() { 2344 // With GC, object assignment is by reference. 2345 let mut gc: Gc<HeapObject> = Gc::new(); 2346 let r = gc.alloc(HeapObject::Object(ObjectData::new())); 2347 let a = Value::Object(r); 2348 let b = a.clone(); 2349 assert!(strict_eq(&a, &b)); // Same GcRef → strict equal. 2350 } 2351 2352 // ── Object model tests ────────────────────────────────── 2353 2354 #[test] 2355 fn test_prototype_chain_lookup() { 2356 // Property lookup walks the prototype chain. 2357 let src = r#" 2358 function Animal() {} 2359 var a = {}; 2360 a.sound = "woof"; 2361 a.sound 2362 "#; 2363 match eval(src).unwrap() { 2364 Value::String(s) => assert_eq!(s, "woof"), 2365 v => panic!("expected 'woof', got {v:?}"), 2366 } 2367 } 2368 2369 #[test] 2370 fn test_typeof_all_types() { 2371 // typeof returns correct strings for all types. 2372 let cases = [ 2373 ("typeof undefined", "undefined"), 2374 ("typeof null", "object"), 2375 ("typeof true", "boolean"), 2376 ("typeof 42", "number"), 2377 ("typeof 'hello'", "string"), 2378 ("typeof {}", "object"), 2379 ("typeof function(){}", "function"), 2380 ]; 2381 for (src, expected) in cases { 2382 match eval(src).unwrap() { 2383 Value::String(s) => assert_eq!(s, expected, "typeof failed for: {src}"), 2384 v => panic!("expected string for {src}, got {v:?}"), 2385 } 2386 } 2387 } 2388 2389 #[test] 2390 fn test_instanceof_basic() { 2391 let src = r#" 2392 function Foo() {} 2393 var f = {}; 2394 f instanceof Foo 2395 "#; 2396 // Plain object without prototype link → false 2397 match eval(src).unwrap() { 2398 Value::Boolean(b) => assert!(!b), 2399 v => panic!("expected false, got {v:?}"), 2400 } 2401 } 2402 2403 #[test] 2404 fn test_in_operator() { 2405 let src = r#" 2406 var o = { x: 1, y: 2 }; 2407 var r1 = "x" in o; 2408 var r2 = "z" in o; 2409 r1 === true && r2 === false 2410 "#; 2411 match eval(src).unwrap() { 2412 Value::Boolean(b) => assert!(b), 2413 v => panic!("expected true, got {v:?}"), 2414 } 2415 } 2416 2417 #[test] 2418 fn test_delete_property() { 2419 let src = r#" 2420 var o = { x: 1, y: 2 }; 2421 delete o.x; 2422 typeof o.x === "undefined" && o.y === 2 2423 "#; 2424 match eval(src).unwrap() { 2425 Value::Boolean(b) => assert!(b), 2426 v => panic!("expected true, got {v:?}"), 2427 } 2428 } 2429 2430 #[test] 2431 fn test_delete_computed_property() { 2432 let src = r#" 2433 var o = { a: 10, b: 20 }; 2434 var key = "a"; 2435 delete o[key]; 2436 typeof o.a === "undefined" && o.b === 20 2437 "#; 2438 match eval(src).unwrap() { 2439 Value::Boolean(b) => assert!(b), 2440 v => panic!("expected true, got {v:?}"), 2441 } 2442 } 2443 2444 #[test] 2445 fn test_delete_non_configurable() { 2446 // Array length is non-configurable, delete should return false. 2447 let mut gc: Gc<HeapObject> = Gc::new(); 2448 let mut obj = ObjectData::new(); 2449 obj.properties.insert( 2450 "x".to_string(), 2451 Property { 2452 value: Value::Number(1.0), 2453 writable: true, 2454 enumerable: true, 2455 configurable: false, 2456 }, 2457 ); 2458 let obj_ref = gc.alloc(HeapObject::Object(obj)); 2459 2460 // Try to delete the non-configurable property. 2461 match gc.get_mut(obj_ref) { 2462 Some(HeapObject::Object(data)) => { 2463 let prop = data.properties.get("x").unwrap(); 2464 assert!(!prop.configurable); 2465 // The property should still be there. 2466 assert!(data.properties.contains_key("x")); 2467 } 2468 _ => panic!("expected object"), 2469 } 2470 } 2471 2472 #[test] 2473 fn test_property_writable_flag() { 2474 // Setting a non-writable property should silently fail. 2475 let mut gc: Gc<HeapObject> = Gc::new(); 2476 let mut obj = ObjectData::new(); 2477 obj.properties.insert( 2478 "frozen".to_string(), 2479 Property { 2480 value: Value::Number(42.0), 2481 writable: false, 2482 enumerable: true, 2483 configurable: false, 2484 }, 2485 ); 2486 let obj_ref = gc.alloc(HeapObject::Object(obj)); 2487 2488 // Verify the property value. 2489 match gc.get(obj_ref) { 2490 Some(HeapObject::Object(data)) => { 2491 assert_eq!( 2492 data.properties.get("frozen").unwrap().value.to_number(), 2493 42.0 2494 ); 2495 } 2496 _ => panic!("expected object"), 2497 } 2498 } 2499 2500 #[test] 2501 fn test_for_in_basic() { 2502 let src = r#" 2503 var o = { a: 1, b: 2, c: 3 }; 2504 var sum = 0; 2505 for (var key in o) { 2506 sum = sum + o[key]; 2507 } 2508 sum 2509 "#; 2510 match eval(src).unwrap() { 2511 Value::Number(n) => assert_eq!(n, 6.0), 2512 v => panic!("expected 6, got {v:?}"), 2513 } 2514 } 2515 2516 #[test] 2517 fn test_for_in_collects_keys() { 2518 let src = r#" 2519 var o = { x: 10, y: 20 }; 2520 var keys = ""; 2521 for (var k in o) { 2522 keys = keys + k + ","; 2523 } 2524 keys 2525 "#; 2526 match eval(src).unwrap() { 2527 Value::String(s) => { 2528 // Both keys should appear (order may vary with HashMap). 2529 assert!(s.contains("x,")); 2530 assert!(s.contains("y,")); 2531 } 2532 v => panic!("expected string, got {v:?}"), 2533 } 2534 } 2535 2536 #[test] 2537 fn test_for_in_empty_object() { 2538 let src = r#" 2539 var o = {}; 2540 var count = 0; 2541 for (var k in o) { 2542 count = count + 1; 2543 } 2544 count 2545 "#; 2546 match eval(src).unwrap() { 2547 Value::Number(n) => assert_eq!(n, 0.0), 2548 v => panic!("expected 0, got {v:?}"), 2549 } 2550 } 2551 2552 #[test] 2553 fn test_property_enumerable_flag() { 2554 // Array "length" is non-enumerable, should not appear in for-in. 2555 let src = r#" 2556 var arr = [10, 20, 30]; 2557 var keys = ""; 2558 for (var k in arr) { 2559 keys = keys + k + ","; 2560 } 2561 keys 2562 "#; 2563 match eval(src).unwrap() { 2564 Value::String(s) => { 2565 // "length" should NOT be in the keys (it's non-enumerable). 2566 assert!(!s.contains("length")); 2567 // Array indices should be present. 2568 assert!(s.contains("0,")); 2569 assert!(s.contains("1,")); 2570 assert!(s.contains("2,")); 2571 } 2572 v => panic!("expected string, got {v:?}"), 2573 } 2574 } 2575 2576 #[test] 2577 fn test_object_reference_semantics() { 2578 // Objects have reference semantics (shared via GcRef). 2579 let src = r#" 2580 var a = { x: 1 }; 2581 var b = a; 2582 b.x = 42; 2583 a.x 2584 "#; 2585 match eval(src).unwrap() { 2586 Value::Number(n) => assert_eq!(n, 42.0), 2587 v => panic!("expected 42, got {v:?}"), 2588 } 2589 } 2590 2591 #[test] 2592 fn test_gc_enumerate_keys_order() { 2593 // Integer keys should come first, sorted numerically. 2594 let mut gc: Gc<HeapObject> = Gc::new(); 2595 let mut obj = ObjectData::new(); 2596 obj.properties 2597 .insert("b".to_string(), Property::data(Value::Number(2.0))); 2598 obj.properties 2599 .insert("2".to_string(), Property::data(Value::Number(3.0))); 2600 obj.properties 2601 .insert("a".to_string(), Property::data(Value::Number(1.0))); 2602 obj.properties 2603 .insert("0".to_string(), Property::data(Value::Number(0.0))); 2604 let obj_ref = gc.alloc(HeapObject::Object(obj)); 2605 2606 let keys = gc_enumerate_keys(&gc, obj_ref); 2607 // Integer keys first (sorted), then string keys. 2608 let int_part: Vec<&str> = keys.iter().take(2).map(|s| s.as_str()).collect(); 2609 assert_eq!(int_part, vec!["0", "2"]); 2610 // Remaining keys are string keys (order depends on HashMap iteration). 2611 let str_part: Vec<&str> = keys.iter().skip(2).map(|s| s.as_str()).collect(); 2612 assert!(str_part.contains(&"a")); 2613 assert!(str_part.contains(&"b")); 2614 } 2615 2616 #[test] 2617 fn test_instanceof_with_gc() { 2618 // Direct test of gc_instanceof. 2619 let mut gc: Gc<HeapObject> = Gc::new(); 2620 2621 // Create a constructor function with a .prototype object. 2622 let proto = gc.alloc(HeapObject::Object(ObjectData::new())); 2623 let ctor = gc.alloc(HeapObject::Function(Box::new(FunctionData { 2624 name: "Foo".to_string(), 2625 kind: FunctionKind::Native(NativeFunc { 2626 callback: |_, _ctx| Ok(Value::Undefined), 2627 }), 2628 prototype_obj: Some(proto), 2629 properties: HashMap::new(), 2630 upvalues: Vec::new(), 2631 }))); 2632 2633 // Create an object whose [[Prototype]] is the constructor's .prototype. 2634 let mut obj_data = ObjectData::new(); 2635 obj_data.prototype = Some(proto); 2636 let obj = gc.alloc(HeapObject::Object(obj_data)); 2637 2638 assert!(gc_instanceof(&gc, obj, ctor)); 2639 2640 // An unrelated object should not match. 2641 let other = gc.alloc(HeapObject::Object(ObjectData::new())); 2642 assert!(!gc_instanceof(&gc, other, ctor)); 2643 } 2644 2645 #[test] 2646 fn test_try_catch_basic() { 2647 // Simple try/catch should catch a thrown value. 2648 let src = r#" 2649 var caught = false; 2650 try { throw "err"; } catch (e) { caught = true; } 2651 caught 2652 "#; 2653 match eval(src).unwrap() { 2654 Value::Boolean(true) => {} 2655 v => panic!("expected true, got {v:?}"), 2656 } 2657 } 2658 2659 #[test] 2660 fn test_try_catch_nested_call() { 2661 // try/catch should catch errors thrown from called functions. 2662 let src = r#" 2663 function thrower() { throw "err"; } 2664 var caught = false; 2665 try { thrower(); } catch (e) { caught = true; } 2666 caught 2667 "#; 2668 match eval(src).unwrap() { 2669 Value::Boolean(true) => {} 2670 v => panic!("expected true, got {v:?}"), 2671 } 2672 } 2673 2674 // ── Closure tests ──────────────────────────────────────── 2675 2676 #[test] 2677 fn test_closure_basic() { 2678 // Basic closure: inner function reads outer variable. 2679 let src = r#" 2680 function outer() { 2681 var x = 10; 2682 function inner() { 2683 return x; 2684 } 2685 return inner(); 2686 } 2687 outer() 2688 "#; 2689 match eval(src).unwrap() { 2690 Value::Number(n) => assert_eq!(n, 10.0), 2691 v => panic!("expected 10, got {v:?}"), 2692 } 2693 } 2694 2695 #[test] 2696 fn test_closure_return_function() { 2697 // Closure survives the outer function's return. 2698 let src = r#" 2699 function makeAdder(x) { 2700 return function(y) { return x + y; }; 2701 } 2702 var add5 = makeAdder(5); 2703 add5(3) 2704 "#; 2705 match eval(src).unwrap() { 2706 Value::Number(n) => assert_eq!(n, 8.0), 2707 v => panic!("expected 8, got {v:?}"), 2708 } 2709 } 2710 2711 #[test] 2712 fn test_closure_mutation() { 2713 // Closures share live references — mutation is visible. 2714 let src = r#" 2715 function counter() { 2716 var n = 0; 2717 return function() { n = n + 1; return n; }; 2718 } 2719 var c = counter(); 2720 c(); 2721 c(); 2722 c() 2723 "#; 2724 match eval(src).unwrap() { 2725 Value::Number(n) => assert_eq!(n, 3.0), 2726 v => panic!("expected 3, got {v:?}"), 2727 } 2728 } 2729 2730 #[test] 2731 fn test_closure_shared_variable() { 2732 // Two closures from the same scope share the same variable. 2733 let src = r#" 2734 function make() { 2735 var x = 0; 2736 function inc() { x = x + 1; } 2737 function get() { return x; } 2738 inc(); 2739 inc(); 2740 return get(); 2741 } 2742 make() 2743 "#; 2744 match eval(src).unwrap() { 2745 Value::Number(n) => assert_eq!(n, 2.0), 2746 v => panic!("expected 2, got {v:?}"), 2747 } 2748 } 2749 2750 #[test] 2751 fn test_closure_arrow() { 2752 // Arrow function captures outer variable. 2753 let src = r#" 2754 function outer() { 2755 var x = 42; 2756 var f = () => x; 2757 return f(); 2758 } 2759 outer() 2760 "#; 2761 match eval(src).unwrap() { 2762 Value::Number(n) => assert_eq!(n, 42.0), 2763 v => panic!("expected 42, got {v:?}"), 2764 } 2765 } 2766 2767 #[test] 2768 fn test_closure_nested() { 2769 // Transitive capture: grandchild function reads grandparent variable. 2770 let src = r#" 2771 function outer() { 2772 var x = 100; 2773 function middle() { 2774 function inner() { 2775 return x; 2776 } 2777 return inner(); 2778 } 2779 return middle(); 2780 } 2781 outer() 2782 "#; 2783 match eval(src).unwrap() { 2784 Value::Number(n) => assert_eq!(n, 100.0), 2785 v => panic!("expected 100, got {v:?}"), 2786 } 2787 } 2788 2789 #[test] 2790 fn test_closure_param_capture() { 2791 // Closure captures a function parameter. 2792 let src = r#" 2793 function multiply(factor) { 2794 return function(x) { return x * factor; }; 2795 } 2796 var double = multiply(2); 2797 double(7) 2798 "#; 2799 match eval(src).unwrap() { 2800 Value::Number(n) => assert_eq!(n, 14.0), 2801 v => panic!("expected 14, got {v:?}"), 2802 } 2803 } 2804 2805 // ── const tests ────────────────────────────────────────── 2806 2807 #[test] 2808 fn test_const_basic() { 2809 let src = "const x = 42; x"; 2810 match eval(src).unwrap() { 2811 Value::Number(n) => assert_eq!(n, 42.0), 2812 v => panic!("expected 42, got {v:?}"), 2813 } 2814 } 2815 2816 #[test] 2817 fn test_const_reassignment_error() { 2818 let src = "const x = 1; x = 2;"; 2819 let program = crate::parser::Parser::parse(src).expect("parse ok"); 2820 let result = crate::compiler::compile(&program); 2821 assert!( 2822 result.is_err(), 2823 "const reassignment should be a compile error" 2824 ); 2825 } 2826 2827 #[test] 2828 fn test_const_missing_init_error() { 2829 let src = "const x;"; 2830 let program = crate::parser::Parser::parse(src).expect("parse ok"); 2831 let result = crate::compiler::compile(&program); 2832 assert!( 2833 result.is_err(), 2834 "const without initializer should be a compile error" 2835 ); 2836 } 2837 2838 // ── this binding tests ─────────────────────────────────── 2839 2840 #[test] 2841 fn test_method_call_this() { 2842 let src = r#" 2843 var obj = {}; 2844 obj.x = 10; 2845 obj.getX = function() { return this.x; }; 2846 obj.getX() 2847 "#; 2848 match eval(src).unwrap() { 2849 Value::Number(n) => assert_eq!(n, 10.0), 2850 v => panic!("expected 10, got {v:?}"), 2851 } 2852 } 2853 2854 // ── Object built-in tests ──────────────────────────────── 2855 2856 #[test] 2857 fn test_object_keys() { 2858 let src = r#" 2859 var obj = {}; 2860 obj.a = 1; 2861 obj.b = 2; 2862 obj.c = 3; 2863 var k = Object.keys(obj); 2864 k.length 2865 "#; 2866 match eval(src).unwrap() { 2867 Value::Number(n) => assert_eq!(n, 3.0), 2868 v => panic!("expected 3, got {v:?}"), 2869 } 2870 } 2871 2872 #[test] 2873 fn test_object_values() { 2874 let src = r#" 2875 var obj = {}; 2876 obj.x = 10; 2877 var v = Object.values(obj); 2878 v[0] 2879 "#; 2880 match eval(src).unwrap() { 2881 Value::Number(n) => assert_eq!(n, 10.0), 2882 v => panic!("expected 10, got {v:?}"), 2883 } 2884 } 2885 2886 #[test] 2887 fn test_object_entries() { 2888 let src = r#" 2889 var obj = {}; 2890 obj.x = 42; 2891 var e = Object.entries(obj); 2892 e[0][1] 2893 "#; 2894 match eval(src).unwrap() { 2895 Value::Number(n) => assert_eq!(n, 42.0), 2896 v => panic!("expected 42, got {v:?}"), 2897 } 2898 } 2899 2900 #[test] 2901 fn test_object_assign() { 2902 let src = r#" 2903 var a = {}; 2904 a.x = 1; 2905 var b = {}; 2906 b.y = 2; 2907 var c = Object.assign(a, b); 2908 c.y 2909 "#; 2910 match eval(src).unwrap() { 2911 Value::Number(n) => assert_eq!(n, 2.0), 2912 v => panic!("expected 2, got {v:?}"), 2913 } 2914 } 2915 2916 #[test] 2917 fn test_object_create() { 2918 let src = r#" 2919 var proto = {}; 2920 proto.greet = "hello"; 2921 var child = Object.create(proto); 2922 child.greet 2923 "#; 2924 match eval(src).unwrap() { 2925 Value::String(s) => assert_eq!(s, "hello"), 2926 v => panic!("expected 'hello', got {v:?}"), 2927 } 2928 } 2929 2930 #[test] 2931 fn test_object_is() { 2932 let src = "Object.is(NaN, NaN)"; 2933 match eval(src).unwrap() { 2934 Value::Boolean(b) => assert!(b), 2935 v => panic!("expected true, got {v:?}"), 2936 } 2937 } 2938 2939 #[test] 2940 fn test_object_freeze() { 2941 let src = r#" 2942 var obj = {}; 2943 obj.x = 1; 2944 Object.freeze(obj); 2945 Object.isFrozen(obj) 2946 "#; 2947 match eval(src).unwrap() { 2948 Value::Boolean(b) => assert!(b), 2949 v => panic!("expected true, got {v:?}"), 2950 } 2951 } 2952 2953 #[test] 2954 fn test_object_has_own_property() { 2955 let src = r#" 2956 var obj = {}; 2957 obj.x = 1; 2958 obj.hasOwnProperty("x") 2959 "#; 2960 match eval(src).unwrap() { 2961 Value::Boolean(b) => assert!(b), 2962 v => panic!("expected true, got {v:?}"), 2963 } 2964 } 2965 2966 // ── Array built-in tests ───────────────────────────────── 2967 2968 #[test] 2969 fn test_array_push_pop() { 2970 let src = r#" 2971 var arr = [1, 2, 3]; 2972 arr.push(4); 2973 arr.pop() 2974 "#; 2975 match eval(src).unwrap() { 2976 Value::Number(n) => assert_eq!(n, 4.0), 2977 v => panic!("expected 4, got {v:?}"), 2978 } 2979 } 2980 2981 #[test] 2982 fn test_array_push_length() { 2983 let src = r#" 2984 var arr = []; 2985 arr.push(10); 2986 arr.push(20); 2987 arr.length 2988 "#; 2989 match eval(src).unwrap() { 2990 Value::Number(n) => assert_eq!(n, 2.0), 2991 v => panic!("expected 2, got {v:?}"), 2992 } 2993 } 2994 2995 #[test] 2996 fn test_array_shift_unshift() { 2997 let src = r#" 2998 var arr = [1, 2, 3]; 2999 arr.unshift(0); 3000 arr.shift() 3001 "#; 3002 match eval(src).unwrap() { 3003 Value::Number(n) => assert_eq!(n, 0.0), 3004 v => panic!("expected 0, got {v:?}"), 3005 } 3006 } 3007 3008 #[test] 3009 fn test_array_index_of() { 3010 let src = r#" 3011 var arr = [10, 20, 30]; 3012 arr.indexOf(20) 3013 "#; 3014 match eval(src).unwrap() { 3015 Value::Number(n) => assert_eq!(n, 1.0), 3016 v => panic!("expected 1, got {v:?}"), 3017 } 3018 } 3019 3020 #[test] 3021 fn test_array_includes() { 3022 let src = r#" 3023 var arr = [1, 2, 3]; 3024 arr.includes(2) 3025 "#; 3026 match eval(src).unwrap() { 3027 Value::Boolean(b) => assert!(b), 3028 v => panic!("expected true, got {v:?}"), 3029 } 3030 } 3031 3032 #[test] 3033 fn test_array_join() { 3034 let src = r#" 3035 var arr = [1, 2, 3]; 3036 arr.join("-") 3037 "#; 3038 match eval(src).unwrap() { 3039 Value::String(s) => assert_eq!(s, "1-2-3"), 3040 v => panic!("expected '1-2-3', got {v:?}"), 3041 } 3042 } 3043 3044 #[test] 3045 fn test_array_slice() { 3046 let src = r#" 3047 var arr = [1, 2, 3, 4, 5]; 3048 var s = arr.slice(1, 3); 3049 s.length 3050 "#; 3051 match eval(src).unwrap() { 3052 Value::Number(n) => assert_eq!(n, 2.0), 3053 v => panic!("expected 2, got {v:?}"), 3054 } 3055 } 3056 3057 #[test] 3058 fn test_array_concat() { 3059 let src = r#" 3060 var a = [1, 2]; 3061 var b = [3, 4]; 3062 var c = a.concat(b); 3063 c.length 3064 "#; 3065 match eval(src).unwrap() { 3066 Value::Number(n) => assert_eq!(n, 4.0), 3067 v => panic!("expected 4, got {v:?}"), 3068 } 3069 } 3070 3071 #[test] 3072 fn test_array_reverse() { 3073 let src = r#" 3074 var arr = [1, 2, 3]; 3075 arr.reverse(); 3076 arr[0] 3077 "#; 3078 match eval(src).unwrap() { 3079 Value::Number(n) => assert_eq!(n, 3.0), 3080 v => panic!("expected 3, got {v:?}"), 3081 } 3082 } 3083 3084 #[test] 3085 fn test_array_splice() { 3086 let src = r#" 3087 var arr = [1, 2, 3, 4, 5]; 3088 var removed = arr.splice(1, 2); 3089 removed.length 3090 "#; 3091 match eval(src).unwrap() { 3092 Value::Number(n) => assert_eq!(n, 2.0), 3093 v => panic!("expected 2, got {v:?}"), 3094 } 3095 } 3096 3097 #[test] 3098 fn test_array_is_array() { 3099 let src = "Array.isArray([1, 2, 3])"; 3100 match eval(src).unwrap() { 3101 Value::Boolean(b) => assert!(b), 3102 v => panic!("expected true, got {v:?}"), 3103 } 3104 } 3105 3106 #[test] 3107 fn test_array_map() { 3108 let src = r#" 3109 var arr = [1, 2, 3]; 3110 var doubled = arr.map(function(x) { return x * 2; }); 3111 doubled[1] 3112 "#; 3113 match eval(src).unwrap() { 3114 Value::Number(n) => assert_eq!(n, 4.0), 3115 v => panic!("expected 4, got {v:?}"), 3116 } 3117 } 3118 3119 #[test] 3120 fn test_array_filter() { 3121 let src = r#" 3122 var arr = [1, 2, 3, 4, 5]; 3123 var evens = arr.filter(function(x) { return x % 2 === 0; }); 3124 evens.length 3125 "#; 3126 match eval(src).unwrap() { 3127 Value::Number(n) => assert_eq!(n, 2.0), 3128 v => panic!("expected 2, got {v:?}"), 3129 } 3130 } 3131 3132 #[test] 3133 fn test_array_reduce() { 3134 let src = r#" 3135 var arr = [1, 2, 3, 4]; 3136 arr.reduce(function(acc, x) { return acc + x; }, 0) 3137 "#; 3138 match eval(src).unwrap() { 3139 Value::Number(n) => assert_eq!(n, 10.0), 3140 v => panic!("expected 10, got {v:?}"), 3141 } 3142 } 3143 3144 #[test] 3145 fn test_array_foreach() { 3146 let src = r#" 3147 var arr = [1, 2, 3]; 3148 var sum = 0; 3149 arr.forEach(function(x) { sum = sum + x; }); 3150 sum 3151 "#; 3152 match eval(src).unwrap() { 3153 Value::Number(n) => assert_eq!(n, 6.0), 3154 v => panic!("expected 6, got {v:?}"), 3155 } 3156 } 3157 3158 #[test] 3159 fn test_array_find() { 3160 let src = r#" 3161 var arr = [1, 2, 3, 4]; 3162 arr.find(function(x) { return x > 2; }) 3163 "#; 3164 match eval(src).unwrap() { 3165 Value::Number(n) => assert_eq!(n, 3.0), 3166 v => panic!("expected 3, got {v:?}"), 3167 } 3168 } 3169 3170 #[test] 3171 fn test_array_find_index() { 3172 let src = r#" 3173 var arr = [1, 2, 3, 4]; 3174 arr.findIndex(function(x) { return x > 2; }) 3175 "#; 3176 match eval(src).unwrap() { 3177 Value::Number(n) => assert_eq!(n, 2.0), 3178 v => panic!("expected 2, got {v:?}"), 3179 } 3180 } 3181 3182 #[test] 3183 fn test_array_some_every() { 3184 let src = r#" 3185 var arr = [2, 4, 6]; 3186 var all_even = arr.every(function(x) { return x % 2 === 0; }); 3187 var has_big = arr.some(function(x) { return x > 10; }); 3188 all_even && !has_big 3189 "#; 3190 match eval(src).unwrap() { 3191 Value::Boolean(b) => assert!(b), 3192 v => panic!("expected true, got {v:?}"), 3193 } 3194 } 3195 3196 #[test] 3197 fn test_array_sort() { 3198 let src = r#" 3199 var arr = [3, 1, 2]; 3200 arr.sort(); 3201 arr[0] 3202 "#; 3203 match eval(src).unwrap() { 3204 Value::Number(n) => assert_eq!(n, 1.0), 3205 v => panic!("expected 1, got {v:?}"), 3206 } 3207 } 3208 3209 #[test] 3210 fn test_array_sort_custom() { 3211 let src = r#" 3212 var arr = [3, 1, 2]; 3213 arr.sort(function(a, b) { return a - b; }); 3214 arr[2] 3215 "#; 3216 match eval(src).unwrap() { 3217 Value::Number(n) => assert_eq!(n, 3.0), 3218 v => panic!("expected 3, got {v:?}"), 3219 } 3220 } 3221 3222 #[test] 3223 fn test_array_from() { 3224 let src = r#" 3225 var arr = Array.from("abc"); 3226 arr.length 3227 "#; 3228 match eval(src).unwrap() { 3229 Value::Number(n) => assert_eq!(n, 3.0), 3230 v => panic!("expected 3, got {v:?}"), 3231 } 3232 } 3233 3234 #[test] 3235 fn test_array_from_array() { 3236 let src = r#" 3237 var orig = [10, 20, 30]; 3238 var copy = Array.from(orig); 3239 copy[2] 3240 "#; 3241 match eval(src).unwrap() { 3242 Value::Number(n) => assert_eq!(n, 30.0), 3243 v => panic!("expected 30, got {v:?}"), 3244 } 3245 } 3246 3247 #[test] 3248 fn test_array_flat() { 3249 let src = r#" 3250 var arr = [[1, 2], [3, 4]]; 3251 var flat = arr.flat(); 3252 flat.length 3253 "#; 3254 match eval(src).unwrap() { 3255 Value::Number(n) => assert_eq!(n, 4.0), 3256 v => panic!("expected 4, got {v:?}"), 3257 } 3258 } 3259 3260 // ── Error built-in tests ───────────────────────────────── 3261 3262 #[test] 3263 fn test_error_constructor() { 3264 let src = r#" 3265 var e = new Error("oops"); 3266 e.message 3267 "#; 3268 match eval(src).unwrap() { 3269 Value::String(s) => assert_eq!(s, "oops"), 3270 v => panic!("expected 'oops', got {v:?}"), 3271 } 3272 } 3273 3274 #[test] 3275 fn test_type_error_constructor() { 3276 let src = r#" 3277 var e = new TypeError("bad type"); 3278 e.message 3279 "#; 3280 match eval(src).unwrap() { 3281 Value::String(s) => assert_eq!(s, "bad type"), 3282 v => panic!("expected 'bad type', got {v:?}"), 3283 } 3284 } 3285 3286 // ── Global function tests ──────────────────────────────── 3287 3288 #[test] 3289 fn test_parse_int() { 3290 let src = "parseInt('42')"; 3291 match eval(src).unwrap() { 3292 Value::Number(n) => assert_eq!(n, 42.0), 3293 v => panic!("expected 42, got {v:?}"), 3294 } 3295 } 3296 3297 #[test] 3298 fn test_parse_int_hex() { 3299 let src = "parseInt('0xFF', 16)"; 3300 match eval(src).unwrap() { 3301 Value::Number(n) => assert_eq!(n, 255.0), 3302 v => panic!("expected 255, got {v:?}"), 3303 } 3304 } 3305 3306 #[test] 3307 fn test_is_nan() { 3308 let src = "isNaN(NaN)"; 3309 match eval(src).unwrap() { 3310 Value::Boolean(b) => assert!(b), 3311 v => panic!("expected true, got {v:?}"), 3312 } 3313 } 3314 3315 #[test] 3316 fn test_is_finite() { 3317 let src = "isFinite(42)"; 3318 match eval(src).unwrap() { 3319 Value::Boolean(b) => assert!(b), 3320 v => panic!("expected true, got {v:?}"), 3321 } 3322 } 3323 3324 // ── String built-in tests ───────────────────────────────── 3325 3326 #[test] 3327 fn test_string_constructor() { 3328 match eval("String(42)").unwrap() { 3329 Value::String(s) => assert_eq!(s, "42"), 3330 v => panic!("expected '42', got {v:?}"), 3331 } 3332 match eval("String(true)").unwrap() { 3333 Value::String(s) => assert_eq!(s, "true"), 3334 v => panic!("expected 'true', got {v:?}"), 3335 } 3336 match eval("String()").unwrap() { 3337 Value::String(s) => assert_eq!(s, ""), 3338 v => panic!("expected '', got {v:?}"), 3339 } 3340 } 3341 3342 #[test] 3343 fn test_string_length() { 3344 match eval("'hello'.length").unwrap() { 3345 Value::Number(n) => assert_eq!(n, 5.0), 3346 v => panic!("expected 5, got {v:?}"), 3347 } 3348 } 3349 3350 #[test] 3351 fn test_string_char_at() { 3352 match eval("'hello'.charAt(1)").unwrap() { 3353 Value::String(s) => assert_eq!(s, "e"), 3354 v => panic!("expected 'e', got {v:?}"), 3355 } 3356 match eval("'hello'.charAt(10)").unwrap() { 3357 Value::String(s) => assert_eq!(s, ""), 3358 v => panic!("expected '', got {v:?}"), 3359 } 3360 } 3361 3362 #[test] 3363 fn test_string_char_code_at() { 3364 match eval("'A'.charCodeAt(0)").unwrap() { 3365 Value::Number(n) => assert_eq!(n, 65.0), 3366 v => panic!("expected 65, got {v:?}"), 3367 } 3368 } 3369 3370 #[test] 3371 fn test_string_proto_concat() { 3372 match eval("'hello'.concat(' ', 'world')").unwrap() { 3373 Value::String(s) => assert_eq!(s, "hello world"), 3374 v => panic!("expected 'hello world', got {v:?}"), 3375 } 3376 } 3377 3378 #[test] 3379 fn test_string_slice() { 3380 match eval("'hello world'.slice(6)").unwrap() { 3381 Value::String(s) => assert_eq!(s, "world"), 3382 v => panic!("expected 'world', got {v:?}"), 3383 } 3384 match eval("'hello'.slice(1, 3)").unwrap() { 3385 Value::String(s) => assert_eq!(s, "el"), 3386 v => panic!("expected 'el', got {v:?}"), 3387 } 3388 match eval("'hello'.slice(-3)").unwrap() { 3389 Value::String(s) => assert_eq!(s, "llo"), 3390 v => panic!("expected 'llo', got {v:?}"), 3391 } 3392 } 3393 3394 #[test] 3395 fn test_string_substring() { 3396 match eval("'hello'.substring(1, 3)").unwrap() { 3397 Value::String(s) => assert_eq!(s, "el"), 3398 v => panic!("expected 'el', got {v:?}"), 3399 } 3400 // substring swaps args if start > end 3401 match eval("'hello'.substring(3, 1)").unwrap() { 3402 Value::String(s) => assert_eq!(s, "el"), 3403 v => panic!("expected 'el', got {v:?}"), 3404 } 3405 } 3406 3407 #[test] 3408 fn test_string_index_of() { 3409 match eval("'hello world'.indexOf('world')").unwrap() { 3410 Value::Number(n) => assert_eq!(n, 6.0), 3411 v => panic!("expected 6, got {v:?}"), 3412 } 3413 match eval("'hello'.indexOf('xyz')").unwrap() { 3414 Value::Number(n) => assert_eq!(n, -1.0), 3415 v => panic!("expected -1, got {v:?}"), 3416 } 3417 } 3418 3419 #[test] 3420 fn test_string_last_index_of() { 3421 match eval("'abcabc'.lastIndexOf('abc')").unwrap() { 3422 Value::Number(n) => assert_eq!(n, 3.0), 3423 v => panic!("expected 3, got {v:?}"), 3424 } 3425 } 3426 3427 #[test] 3428 fn test_string_includes() { 3429 match eval("'hello world'.includes('world')").unwrap() { 3430 Value::Boolean(b) => assert!(b), 3431 v => panic!("expected true, got {v:?}"), 3432 } 3433 match eval("'hello'.includes('xyz')").unwrap() { 3434 Value::Boolean(b) => assert!(!b), 3435 v => panic!("expected false, got {v:?}"), 3436 } 3437 } 3438 3439 #[test] 3440 fn test_string_starts_ends_with() { 3441 match eval("'hello'.startsWith('hel')").unwrap() { 3442 Value::Boolean(b) => assert!(b), 3443 v => panic!("expected true, got {v:?}"), 3444 } 3445 match eval("'hello'.endsWith('llo')").unwrap() { 3446 Value::Boolean(b) => assert!(b), 3447 v => panic!("expected true, got {v:?}"), 3448 } 3449 } 3450 3451 #[test] 3452 fn test_string_trim() { 3453 match eval("' hello '.trim()").unwrap() { 3454 Value::String(s) => assert_eq!(s, "hello"), 3455 v => panic!("expected 'hello', got {v:?}"), 3456 } 3457 match eval("' hello '.trimStart()").unwrap() { 3458 Value::String(s) => assert_eq!(s, "hello "), 3459 v => panic!("expected 'hello ', got {v:?}"), 3460 } 3461 match eval("' hello '.trimEnd()").unwrap() { 3462 Value::String(s) => assert_eq!(s, " hello"), 3463 v => panic!("expected ' hello', got {v:?}"), 3464 } 3465 } 3466 3467 #[test] 3468 fn test_string_pad() { 3469 match eval("'5'.padStart(3, '0')").unwrap() { 3470 Value::String(s) => assert_eq!(s, "005"), 3471 v => panic!("expected '005', got {v:?}"), 3472 } 3473 match eval("'5'.padEnd(3, '0')").unwrap() { 3474 Value::String(s) => assert_eq!(s, "500"), 3475 v => panic!("expected '500', got {v:?}"), 3476 } 3477 } 3478 3479 #[test] 3480 fn test_string_repeat() { 3481 match eval("'ab'.repeat(3)").unwrap() { 3482 Value::String(s) => assert_eq!(s, "ababab"), 3483 v => panic!("expected 'ababab', got {v:?}"), 3484 } 3485 } 3486 3487 #[test] 3488 fn test_string_split() { 3489 // split returns an array; verify length and elements. 3490 match eval("'a,b,c'.split(',').length").unwrap() { 3491 Value::Number(n) => assert_eq!(n, 3.0), 3492 v => panic!("expected 3, got {v:?}"), 3493 } 3494 match eval("'a,b,c'.split(',')[0]").unwrap() { 3495 Value::String(s) => assert_eq!(s, "a"), 3496 v => panic!("expected 'a', got {v:?}"), 3497 } 3498 match eval("'a,b,c'.split(',')[2]").unwrap() { 3499 Value::String(s) => assert_eq!(s, "c"), 3500 v => panic!("expected 'c', got {v:?}"), 3501 } 3502 } 3503 3504 #[test] 3505 fn test_string_replace() { 3506 match eval("'hello world'.replace('world', 'there')").unwrap() { 3507 Value::String(s) => assert_eq!(s, "hello there"), 3508 v => panic!("expected 'hello there', got {v:?}"), 3509 } 3510 } 3511 3512 #[test] 3513 fn test_string_replace_all() { 3514 match eval("'aabbcc'.replaceAll('b', 'x')").unwrap() { 3515 Value::String(s) => assert_eq!(s, "aaxxcc"), 3516 v => panic!("expected 'aaxxcc', got {v:?}"), 3517 } 3518 } 3519 3520 #[test] 3521 fn test_string_case() { 3522 match eval("'Hello'.toLowerCase()").unwrap() { 3523 Value::String(s) => assert_eq!(s, "hello"), 3524 v => panic!("expected 'hello', got {v:?}"), 3525 } 3526 match eval("'Hello'.toUpperCase()").unwrap() { 3527 Value::String(s) => assert_eq!(s, "HELLO"), 3528 v => panic!("expected 'HELLO', got {v:?}"), 3529 } 3530 } 3531 3532 #[test] 3533 fn test_string_at() { 3534 match eval("'hello'.at(0)").unwrap() { 3535 Value::String(s) => assert_eq!(s, "h"), 3536 v => panic!("expected 'h', got {v:?}"), 3537 } 3538 match eval("'hello'.at(-1)").unwrap() { 3539 Value::String(s) => assert_eq!(s, "o"), 3540 v => panic!("expected 'o', got {v:?}"), 3541 } 3542 } 3543 3544 #[test] 3545 fn test_string_from_char_code() { 3546 match eval("String.fromCharCode(72, 101, 108)").unwrap() { 3547 Value::String(s) => assert_eq!(s, "Hel"), 3548 v => panic!("expected 'Hel', got {v:?}"), 3549 } 3550 } 3551 3552 #[test] 3553 fn test_string_from_code_point() { 3554 match eval("String.fromCodePoint(65, 66, 67)").unwrap() { 3555 Value::String(s) => assert_eq!(s, "ABC"), 3556 v => panic!("expected 'ABC', got {v:?}"), 3557 } 3558 } 3559 3560 // ── Number built-in tests ───────────────────────────────── 3561 3562 #[test] 3563 fn test_number_constructor() { 3564 match eval("Number('42')").unwrap() { 3565 Value::Number(n) => assert_eq!(n, 42.0), 3566 v => panic!("expected 42, got {v:?}"), 3567 } 3568 match eval("Number(true)").unwrap() { 3569 Value::Number(n) => assert_eq!(n, 1.0), 3570 v => panic!("expected 1, got {v:?}"), 3571 } 3572 match eval("Number()").unwrap() { 3573 Value::Number(n) => assert_eq!(n, 0.0), 3574 v => panic!("expected 0, got {v:?}"), 3575 } 3576 } 3577 3578 #[test] 3579 fn test_number_is_nan() { 3580 match eval("Number.isNaN(NaN)").unwrap() { 3581 Value::Boolean(b) => assert!(b), 3582 v => panic!("expected true, got {v:?}"), 3583 } 3584 match eval("Number.isNaN(42)").unwrap() { 3585 Value::Boolean(b) => assert!(!b), 3586 v => panic!("expected false, got {v:?}"), 3587 } 3588 // Number.isNaN doesn't coerce — string "NaN" is not NaN. 3589 match eval("Number.isNaN('NaN')").unwrap() { 3590 Value::Boolean(b) => assert!(!b), 3591 v => panic!("expected false, got {v:?}"), 3592 } 3593 } 3594 3595 #[test] 3596 fn test_number_is_finite() { 3597 match eval("Number.isFinite(42)").unwrap() { 3598 Value::Boolean(b) => assert!(b), 3599 v => panic!("expected true, got {v:?}"), 3600 } 3601 match eval("Number.isFinite(Infinity)").unwrap() { 3602 Value::Boolean(b) => assert!(!b), 3603 v => panic!("expected false, got {v:?}"), 3604 } 3605 } 3606 3607 #[test] 3608 fn test_number_is_integer() { 3609 match eval("Number.isInteger(42)").unwrap() { 3610 Value::Boolean(b) => assert!(b), 3611 v => panic!("expected true, got {v:?}"), 3612 } 3613 match eval("Number.isInteger(42.5)").unwrap() { 3614 Value::Boolean(b) => assert!(!b), 3615 v => panic!("expected false, got {v:?}"), 3616 } 3617 } 3618 3619 #[test] 3620 fn test_number_is_safe_integer() { 3621 match eval("Number.isSafeInteger(42)").unwrap() { 3622 Value::Boolean(b) => assert!(b), 3623 v => panic!("expected true, got {v:?}"), 3624 } 3625 match eval("Number.isSafeInteger(9007199254740992)").unwrap() { 3626 Value::Boolean(b) => assert!(!b), 3627 v => panic!("expected false, got {v:?}"), 3628 } 3629 } 3630 3631 #[test] 3632 fn test_number_constants() { 3633 match eval("Number.MAX_SAFE_INTEGER").unwrap() { 3634 Value::Number(n) => assert_eq!(n, 9007199254740991.0), 3635 v => panic!("expected MAX_SAFE_INTEGER, got {v:?}"), 3636 } 3637 match eval("Number.EPSILON").unwrap() { 3638 Value::Number(n) => assert_eq!(n, f64::EPSILON), 3639 v => panic!("expected EPSILON, got {v:?}"), 3640 } 3641 } 3642 3643 #[test] 3644 fn test_number_to_fixed() { 3645 match eval("var n = 3.14159; n.toFixed(2)").unwrap() { 3646 Value::String(s) => assert_eq!(s, "3.14"), 3647 v => panic!("expected '3.14', got {v:?}"), 3648 } 3649 } 3650 3651 #[test] 3652 fn test_number_to_string_radix() { 3653 match eval("var n = 255; n.toString(16)").unwrap() { 3654 Value::String(s) => assert_eq!(s, "ff"), 3655 v => panic!("expected 'ff', got {v:?}"), 3656 } 3657 match eval("var n = 10; n.toString(2)").unwrap() { 3658 Value::String(s) => assert_eq!(s, "1010"), 3659 v => panic!("expected '1010', got {v:?}"), 3660 } 3661 } 3662 3663 #[test] 3664 fn test_number_parse_int() { 3665 match eval("Number.parseInt('42')").unwrap() { 3666 Value::Number(n) => assert_eq!(n, 42.0), 3667 v => panic!("expected 42, got {v:?}"), 3668 } 3669 } 3670 3671 // ── Boolean built-in tests ──────────────────────────────── 3672 3673 #[test] 3674 fn test_boolean_constructor() { 3675 match eval("Boolean(1)").unwrap() { 3676 Value::Boolean(b) => assert!(b), 3677 v => panic!("expected true, got {v:?}"), 3678 } 3679 match eval("Boolean(0)").unwrap() { 3680 Value::Boolean(b) => assert!(!b), 3681 v => panic!("expected false, got {v:?}"), 3682 } 3683 match eval("Boolean('')").unwrap() { 3684 Value::Boolean(b) => assert!(!b), 3685 v => panic!("expected false, got {v:?}"), 3686 } 3687 match eval("Boolean('hello')").unwrap() { 3688 Value::Boolean(b) => assert!(b), 3689 v => panic!("expected true, got {v:?}"), 3690 } 3691 } 3692 3693 #[test] 3694 fn test_boolean_to_string() { 3695 match eval("true.toString()").unwrap() { 3696 Value::String(s) => assert_eq!(s, "true"), 3697 v => panic!("expected 'true', got {v:?}"), 3698 } 3699 match eval("false.toString()").unwrap() { 3700 Value::String(s) => assert_eq!(s, "false"), 3701 v => panic!("expected 'false', got {v:?}"), 3702 } 3703 } 3704 3705 // ── Symbol built-in tests ───────────────────────────────── 3706 3707 #[test] 3708 fn test_symbol_uniqueness() { 3709 // Each Symbol() call should produce a unique value. 3710 match eval("var a = Symbol('x'); var b = Symbol('x'); a === b").unwrap() { 3711 Value::Boolean(b) => assert!(!b), 3712 v => panic!("expected false, got {v:?}"), 3713 } 3714 } 3715 3716 #[test] 3717 fn test_symbol_well_known() { 3718 match eval("typeof Symbol.iterator").unwrap() { 3719 Value::String(s) => assert_eq!(s, "string"), 3720 v => panic!("expected 'string', got {v:?}"), 3721 } 3722 match eval("Symbol.iterator").unwrap() { 3723 Value::String(s) => assert_eq!(s, "@@iterator"), 3724 v => panic!("expected '@@iterator', got {v:?}"), 3725 } 3726 } 3727 3728 #[test] 3729 fn test_symbol_for_and_key_for() { 3730 // "for" is a keyword, so use bracket notation: Symbol["for"](...). 3731 match eval("Symbol['for']('test') === Symbol['for']('test')").unwrap() { 3732 Value::Boolean(b) => assert!(b), 3733 v => panic!("expected true, got {v:?}"), 3734 } 3735 match eval("Symbol.keyFor(Symbol['for']('mykey'))").unwrap() { 3736 Value::String(s) => assert_eq!(s, "mykey"), 3737 v => panic!("expected 'mykey', got {v:?}"), 3738 } 3739 } 3740 3741 // ── Primitive auto-boxing tests ─────────────────────────── 3742 3743 #[test] 3744 fn test_string_method_chaining() { 3745 match eval("' Hello World '.trim().toLowerCase()").unwrap() { 3746 Value::String(s) => assert_eq!(s, "hello world"), 3747 v => panic!("expected 'hello world', got {v:?}"), 3748 } 3749 } 3750 3751 #[test] 3752 fn test_string_substr() { 3753 match eval("'hello world'.substr(6, 5)").unwrap() { 3754 Value::String(s) => assert_eq!(s, "world"), 3755 v => panic!("expected 'world', got {v:?}"), 3756 } 3757 } 3758 3759 // ── Math built-in ───────────────────────────────────────── 3760 3761 #[test] 3762 fn test_math_constants() { 3763 let r = eval("Math.PI").unwrap(); 3764 match r { 3765 Value::Number(n) => assert!((n - std::f64::consts::PI).abs() < 1e-10), 3766 _ => panic!("Expected number"), 3767 } 3768 let r = eval("Math.E").unwrap(); 3769 match r { 3770 Value::Number(n) => assert!((n - std::f64::consts::E).abs() < 1e-10), 3771 _ => panic!("Expected number"), 3772 } 3773 let r = eval("Math.SQRT2").unwrap(); 3774 match r { 3775 Value::Number(n) => assert!((n - std::f64::consts::SQRT_2).abs() < 1e-10), 3776 _ => panic!("Expected number"), 3777 } 3778 } 3779 3780 #[test] 3781 fn test_math_abs() { 3782 assert_eq!(eval("Math.abs(-5)").unwrap().to_number(), 5.0); 3783 assert_eq!(eval("Math.abs(3)").unwrap().to_number(), 3.0); 3784 assert_eq!(eval("Math.abs(0)").unwrap().to_number(), 0.0); 3785 } 3786 3787 #[test] 3788 fn test_math_floor_ceil_round_trunc() { 3789 assert_eq!(eval("Math.floor(4.7)").unwrap().to_number(), 4.0); 3790 assert_eq!(eval("Math.ceil(4.2)").unwrap().to_number(), 5.0); 3791 assert_eq!(eval("Math.round(4.5)").unwrap().to_number(), 5.0); 3792 assert_eq!(eval("Math.round(4.4)").unwrap().to_number(), 4.0); 3793 assert_eq!(eval("Math.trunc(4.7)").unwrap().to_number(), 4.0); 3794 assert_eq!(eval("Math.trunc(-4.7)").unwrap().to_number(), -4.0); 3795 } 3796 3797 #[test] 3798 fn test_math_max_min() { 3799 assert_eq!(eval("Math.max(1, 3, 2)").unwrap().to_number(), 3.0); 3800 assert_eq!(eval("Math.min(1, 3, 2)").unwrap().to_number(), 1.0); 3801 assert_eq!(eval("Math.max()").unwrap().to_number(), f64::NEG_INFINITY); 3802 assert_eq!(eval("Math.min()").unwrap().to_number(), f64::INFINITY); 3803 } 3804 3805 #[test] 3806 fn test_math_pow_sqrt() { 3807 assert_eq!(eval("Math.pow(2, 10)").unwrap().to_number(), 1024.0); 3808 assert_eq!(eval("Math.sqrt(9)").unwrap().to_number(), 3.0); 3809 let cbrt = eval("Math.cbrt(27)").unwrap().to_number(); 3810 assert!((cbrt - 3.0).abs() < 1e-10); 3811 } 3812 3813 #[test] 3814 fn test_math_trig() { 3815 let sin = eval("Math.sin(0)").unwrap().to_number(); 3816 assert!(sin.abs() < 1e-10); 3817 let cos = eval("Math.cos(0)").unwrap().to_number(); 3818 assert!((cos - 1.0).abs() < 1e-10); 3819 let atan2 = eval("Math.atan2(1, 1)").unwrap().to_number(); 3820 assert!((atan2 - std::f64::consts::FRAC_PI_4).abs() < 1e-10); 3821 } 3822 3823 #[test] 3824 fn test_math_log_exp() { 3825 let exp = eval("Math.exp(1)").unwrap().to_number(); 3826 assert!((exp - std::f64::consts::E).abs() < 1e-10); 3827 let log = eval("Math.log(Math.E)").unwrap().to_number(); 3828 assert!((log - 1.0).abs() < 1e-10); 3829 let log2 = eval("Math.log2(8)").unwrap().to_number(); 3830 assert!((log2 - 3.0).abs() < 1e-10); 3831 let log10 = eval("Math.log10(1000)").unwrap().to_number(); 3832 assert!((log10 - 3.0).abs() < 1e-10); 3833 } 3834 3835 #[test] 3836 fn test_math_sign() { 3837 assert_eq!(eval("Math.sign(5)").unwrap().to_number(), 1.0); 3838 assert_eq!(eval("Math.sign(-5)").unwrap().to_number(), -1.0); 3839 assert_eq!(eval("Math.sign(0)").unwrap().to_number(), 0.0); 3840 } 3841 3842 #[test] 3843 fn test_math_clz32() { 3844 assert_eq!(eval("Math.clz32(1)").unwrap().to_number(), 31.0); 3845 assert_eq!(eval("Math.clz32(0)").unwrap().to_number(), 32.0); 3846 } 3847 3848 #[test] 3849 fn test_math_imul() { 3850 assert_eq!(eval("Math.imul(3, 4)").unwrap().to_number(), 12.0); 3851 assert_eq!(eval("Math.imul(0xffffffff, 5)").unwrap().to_number(), -5.0); 3852 } 3853 3854 #[test] 3855 fn test_math_hypot() { 3856 assert_eq!(eval("Math.hypot(3, 4)").unwrap().to_number(), 5.0); 3857 assert_eq!(eval("Math.hypot()").unwrap().to_number(), 0.0); 3858 } 3859 3860 #[test] 3861 fn test_math_random() { 3862 match eval("var r = Math.random(); r >= 0 && r < 1").unwrap() { 3863 Value::Boolean(b) => assert!(b), 3864 v => panic!("expected true, got {v:?}"), 3865 } 3866 } 3867 3868 #[test] 3869 fn test_math_fround() { 3870 let r = eval("Math.fround(5.5)").unwrap().to_number(); 3871 assert_eq!(r, 5.5f32 as f64); 3872 } 3873 3874 // ── Date built-in ───────────────────────────────────────── 3875 3876 #[test] 3877 fn test_date_now() { 3878 let r = eval("Date.now()").unwrap().to_number(); 3879 assert!(r > 1_577_836_800_000.0); 3880 } 3881 3882 #[test] 3883 fn test_date_utc() { 3884 let r = eval("Date.UTC(2020, 0, 1)").unwrap().to_number(); 3885 assert_eq!(r, 1_577_836_800_000.0); 3886 } 3887 3888 #[test] 3889 fn test_date_parse() { 3890 let r = eval("Date.parse('2020-01-01T00:00:00.000Z')") 3891 .unwrap() 3892 .to_number(); 3893 assert_eq!(r, 1_577_836_800_000.0); 3894 } 3895 3896 #[test] 3897 fn test_date_constructor_ms() { 3898 let r = eval("var d = new Date(1577836800000); d.getFullYear()") 3899 .unwrap() 3900 .to_number(); 3901 assert_eq!(r, 2020.0); 3902 } 3903 3904 #[test] 3905 fn test_date_constructor_components() { 3906 let r = eval("var d = new Date(2020, 0, 1, 0, 0, 0, 0); d.getTime()") 3907 .unwrap() 3908 .to_number(); 3909 assert_eq!(r, 1_577_836_800_000.0); 3910 } 3911 3912 #[test] 3913 fn test_date_getters() { 3914 let src = r#" 3915 var d = new Date(1577836800000); 3916 var results = [ 3917 d.getFullYear(), 3918 d.getMonth(), 3919 d.getDate(), 3920 d.getHours(), 3921 d.getMinutes(), 3922 d.getSeconds(), 3923 d.getMilliseconds(), 3924 d.getDay() 3925 ]; 3926 results[0] === 2020 && results[1] === 0 && results[2] === 1 && 3927 results[3] === 0 && results[4] === 0 && results[5] === 0 && 3928 results[6] === 0 && results[7] === 3 3929 "#; 3930 match eval(src).unwrap() { 3931 Value::Boolean(b) => assert!(b), 3932 v => panic!("expected true, got {v:?}"), 3933 } 3934 } 3935 3936 #[test] 3937 fn test_date_setters() { 3938 let src = r#" 3939 var d = new Date(1577836800000); 3940 d.setFullYear(2025); 3941 d.getFullYear() 3942 "#; 3943 assert_eq!(eval(src).unwrap().to_number(), 2025.0); 3944 } 3945 3946 #[test] 3947 fn test_date_to_iso_string() { 3948 match eval("var d = new Date(1577836800000); d.toISOString()").unwrap() { 3949 Value::String(s) => assert_eq!(s, "2020-01-01T00:00:00.000Z"), 3950 v => panic!("expected ISO string, got {v:?}"), 3951 } 3952 } 3953 3954 #[test] 3955 fn test_date_value_of() { 3956 let r = eval("var d = new Date(1577836800000); d.valueOf()") 3957 .unwrap() 3958 .to_number(); 3959 assert_eq!(r, 1_577_836_800_000.0); 3960 } 3961 3962 #[test] 3963 fn test_date_to_string() { 3964 let r = eval("var d = new Date(1577836800000); d.toString()").unwrap(); 3965 match r { 3966 Value::String(s) => assert!(s.contains("2020") && s.contains("GMT")), 3967 _ => panic!("Expected string"), 3968 } 3969 } 3970 3971 #[test] 3972 fn test_date_to_json() { 3973 match eval("var d = new Date(1577836800000); d.toJSON()").unwrap() { 3974 Value::String(s) => assert_eq!(s, "2020-01-01T00:00:00.000Z"), 3975 v => panic!("expected ISO string, got {v:?}"), 3976 } 3977 } 3978 3979 #[test] 3980 fn test_date_constructor_string() { 3981 let r = eval("var d = new Date('2020-06-15T12:30:00Z'); d.getMonth()") 3982 .unwrap() 3983 .to_number(); 3984 assert_eq!(r, 5.0); 3985 } 3986 3987 // ── JSON built-in ───────────────────────────────────────── 3988 3989 #[test] 3990 fn test_json_parse_primitives() { 3991 assert!(matches!(eval("JSON.parse('null')").unwrap(), Value::Null)); 3992 match eval("JSON.parse('true')").unwrap() { 3993 Value::Boolean(b) => assert!(b), 3994 v => panic!("expected true, got {v:?}"), 3995 } 3996 match eval("JSON.parse('false')").unwrap() { 3997 Value::Boolean(b) => assert!(!b), 3998 v => panic!("expected false, got {v:?}"), 3999 } 4000 assert_eq!(eval("JSON.parse('42')").unwrap().to_number(), 42.0); 4001 match eval(r#"JSON.parse('"hello"')"#).unwrap() { 4002 Value::String(s) => assert_eq!(s, "hello"), 4003 v => panic!("expected 'hello', got {v:?}"), 4004 } 4005 } 4006 4007 #[test] 4008 fn test_json_parse_array() { 4009 let src = r#" 4010 var a = JSON.parse('[1, 2, 3]'); 4011 a.length === 3 && a[0] === 1 && a[1] === 2 && a[2] === 3 4012 "#; 4013 match eval(src).unwrap() { 4014 Value::Boolean(b) => assert!(b), 4015 v => panic!("expected true, got {v:?}"), 4016 } 4017 } 4018 4019 #[test] 4020 fn test_json_parse_object() { 4021 let src = r#" 4022 var o = JSON.parse('{"name":"test","value":42}'); 4023 o.name === "test" && o.value === 42 4024 "#; 4025 match eval(src).unwrap() { 4026 Value::Boolean(b) => assert!(b), 4027 v => panic!("expected true, got {v:?}"), 4028 } 4029 } 4030 4031 #[test] 4032 fn test_json_parse_nested() { 4033 let src = r#" 4034 var o = JSON.parse('{"a":[1,{"b":2}]}'); 4035 o.a[1].b === 2 4036 "#; 4037 match eval(src).unwrap() { 4038 Value::Boolean(b) => assert!(b), 4039 v => panic!("expected true, got {v:?}"), 4040 } 4041 } 4042 4043 #[test] 4044 fn test_json_parse_invalid() { 4045 assert!(eval("JSON.parse('{invalid}')").is_err()); 4046 assert!(eval("JSON.parse('')").is_err()); 4047 } 4048 4049 #[test] 4050 fn test_json_stringify_primitives() { 4051 match eval("JSON.stringify(null)").unwrap() { 4052 Value::String(s) => assert_eq!(s, "null"), 4053 v => panic!("expected 'null', got {v:?}"), 4054 } 4055 match eval("JSON.stringify(true)").unwrap() { 4056 Value::String(s) => assert_eq!(s, "true"), 4057 v => panic!("expected 'true', got {v:?}"), 4058 } 4059 match eval("JSON.stringify(42)").unwrap() { 4060 Value::String(s) => assert_eq!(s, "42"), 4061 v => panic!("expected '42', got {v:?}"), 4062 } 4063 match eval(r#"JSON.stringify("hello")"#).unwrap() { 4064 Value::String(s) => assert_eq!(s, "\"hello\""), 4065 v => panic!("expected quoted hello, got {v:?}"), 4066 } 4067 } 4068 4069 #[test] 4070 fn test_json_stringify_array() { 4071 match eval("JSON.stringify([1, 2, 3])").unwrap() { 4072 Value::String(s) => assert_eq!(s, "[1,2,3]"), 4073 v => panic!("expected '[1,2,3]', got {v:?}"), 4074 } 4075 } 4076 4077 #[test] 4078 fn test_json_stringify_object() { 4079 let src = r#" 4080 var o = {a: 1, b: "hello"}; 4081 JSON.stringify(o) 4082 "#; 4083 let r = eval(src).unwrap(); 4084 match r { 4085 Value::String(s) => { 4086 assert!(s.contains("\"a\"") && s.contains("\"b\"")); 4087 assert!(s.contains('1') && s.contains("\"hello\"")); 4088 } 4089 _ => panic!("Expected string"), 4090 } 4091 } 4092 4093 #[test] 4094 fn test_json_stringify_nested() { 4095 let src = r#" 4096 JSON.stringify({a: [1, 2], b: {c: 3}}) 4097 "#; 4098 let r = eval(src).unwrap(); 4099 match r { 4100 Value::String(s) => { 4101 assert!(s.contains("[1,2]")); 4102 assert!(s.contains("\"c\":3") || s.contains("\"c\": 3")); 4103 } 4104 _ => panic!("Expected string"), 4105 } 4106 } 4107 4108 #[test] 4109 fn test_json_stringify_special_values() { 4110 match eval("JSON.stringify(NaN)").unwrap() { 4111 Value::String(s) => assert_eq!(s, "null"), 4112 v => panic!("expected 'null', got {v:?}"), 4113 } 4114 match eval("JSON.stringify(Infinity)").unwrap() { 4115 Value::String(s) => assert_eq!(s, "null"), 4116 v => panic!("expected 'null', got {v:?}"), 4117 } 4118 assert!(matches!( 4119 eval("JSON.stringify(undefined)").unwrap(), 4120 Value::Undefined 4121 )); 4122 } 4123 4124 #[test] 4125 fn test_json_stringify_with_indent() { 4126 let src = r#"JSON.stringify([1, 2], null, 2)"#; 4127 let r = eval(src).unwrap(); 4128 match r { 4129 Value::String(s) => { 4130 assert!(s.contains('\n')); 4131 assert!(s.contains(" 1")); 4132 } 4133 _ => panic!("Expected string"), 4134 } 4135 } 4136 4137 #[test] 4138 fn test_json_parse_escape_sequences() { 4139 let src = r#"JSON.parse('"hello\\nworld"')"#; 4140 match eval(src).unwrap() { 4141 Value::String(s) => assert_eq!(s, "hello\nworld"), 4142 v => panic!("expected escaped string, got {v:?}"), 4143 } 4144 } 4145 4146 #[test] 4147 fn test_json_roundtrip() { 4148 let src = r#" 4149 var original = {name: "test", values: [1, 2, 3], nested: {ok: true}}; 4150 var json = JSON.stringify(original); 4151 var parsed = JSON.parse(json); 4152 parsed.name === "test" && parsed.values[1] === 2 && parsed.nested.ok === true 4153 "#; 4154 match eval(src).unwrap() { 4155 Value::Boolean(b) => assert!(b), 4156 v => panic!("expected true, got {v:?}"), 4157 } 4158 } 4159 4160 #[test] 4161 fn test_json_stringify_circular_detection() { 4162 let src = r#" 4163 var obj = {}; 4164 obj.self = obj; 4165 try { 4166 JSON.stringify(obj); 4167 false; 4168 } catch (e) { 4169 e.message.indexOf("circular") !== -1; 4170 } 4171 "#; 4172 match eval(src).unwrap() { 4173 Value::Boolean(b) => assert!(b), 4174 v => panic!("expected true, got {v:?}"), 4175 } 4176 } 4177 4178 #[test] 4179 fn test_json_parse_unicode_escape() { 4180 let src = r#"JSON.parse('"\\u0041"')"#; 4181 match eval(src).unwrap() { 4182 Value::String(s) => assert_eq!(s, "A"), 4183 v => panic!("expected 'A', got {v:?}"), 4184 } 4185 } 4186 4187 #[test] 4188 fn test_json_stringify_empty() { 4189 match eval("JSON.stringify([])").unwrap() { 4190 Value::String(s) => assert_eq!(s, "[]"), 4191 v => panic!("expected '[]', got {v:?}"), 4192 } 4193 match eval("JSON.stringify({})").unwrap() { 4194 Value::String(s) => assert_eq!(s, "{}"), 4195 v => panic!("expected '{{}}', got {v:?}"), 4196 } 4197 } 4198 4199 // ── RegExp tests ──────────────────────────────────────── 4200 4201 #[test] 4202 fn test_regexp_constructor() { 4203 match eval("var r = new RegExp('abc', 'g'); r.source").unwrap() { 4204 Value::String(s) => assert_eq!(s, "abc"), 4205 v => panic!("expected 'abc', got {v:?}"), 4206 } 4207 match eval("var r = new RegExp('abc', 'gi'); r.flags").unwrap() { 4208 Value::String(s) => assert_eq!(s, "gi"), 4209 v => panic!("expected 'gi', got {v:?}"), 4210 } 4211 match eval("var r = new RegExp('abc'); r.global").unwrap() { 4212 Value::Boolean(b) => assert!(!b), 4213 v => panic!("expected false, got {v:?}"), 4214 } 4215 match eval("var r = new RegExp('abc', 'g'); r.global").unwrap() { 4216 Value::Boolean(b) => assert!(b), 4217 v => panic!("expected true, got {v:?}"), 4218 } 4219 } 4220 4221 #[test] 4222 fn test_regexp_test() { 4223 match eval("var r = new RegExp('abc'); r.test('xabcx')").unwrap() { 4224 Value::Boolean(b) => assert!(b), 4225 v => panic!("expected true, got {v:?}"), 4226 } 4227 match eval("var r = new RegExp('abc'); r.test('xyz')").unwrap() { 4228 Value::Boolean(b) => assert!(!b), 4229 v => panic!("expected false, got {v:?}"), 4230 } 4231 match eval("var r = new RegExp('\\\\d+'); r.test('abc123')").unwrap() { 4232 Value::Boolean(b) => assert!(b), 4233 v => panic!("expected true, got {v:?}"), 4234 } 4235 } 4236 4237 #[test] 4238 fn test_regexp_exec() { 4239 match eval("var r = new RegExp('(a)(b)(c)'); var m = r.exec('abc'); m[0]").unwrap() { 4240 Value::String(s) => assert_eq!(s, "abc"), 4241 v => panic!("expected 'abc', got {v:?}"), 4242 } 4243 match eval("var r = new RegExp('(a)(b)(c)'); var m = r.exec('abc'); m[1]").unwrap() { 4244 Value::String(s) => assert_eq!(s, "a"), 4245 v => panic!("expected 'a', got {v:?}"), 4246 } 4247 match eval("var r = new RegExp('b+'); var m = r.exec('aabbc'); m[0]").unwrap() { 4248 Value::String(s) => assert_eq!(s, "bb"), 4249 v => panic!("expected 'bb', got {v:?}"), 4250 } 4251 match eval("var r = new RegExp('xyz'); r.exec('abc')").unwrap() { 4252 Value::Null => {} 4253 v => panic!("expected null, got {v:?}"), 4254 } 4255 } 4256 4257 #[test] 4258 fn test_regexp_exec_global() { 4259 let src = "var r = new RegExp('a', 'g'); r.exec('aba')[0]"; 4260 match eval(src).unwrap() { 4261 Value::String(s) => assert_eq!(s, "a"), 4262 v => panic!("expected 'a', got {v:?}"), 4263 } 4264 let src = r#" 4265 var r = new RegExp('a', 'g'); 4266 r.exec('aba'); 4267 var m = r.exec('aba'); 4268 m[0] + ',' + m.index 4269 "#; 4270 match eval(src).unwrap() { 4271 Value::String(s) => assert_eq!(s, "a,2"), 4272 v => panic!("expected 'a,2', got {v:?}"), 4273 } 4274 } 4275 4276 #[test] 4277 fn test_regexp_to_string() { 4278 match eval("var r = new RegExp('abc', 'gi'); r.toString()").unwrap() { 4279 Value::String(s) => assert_eq!(s, "/abc/gi"), 4280 v => panic!("expected '/abc/gi', got {v:?}"), 4281 } 4282 match eval("/hello\\d+/.toString()").unwrap() { 4283 Value::String(s) => assert_eq!(s, "/hello\\d+/"), 4284 v => panic!("expected '/hello\\d+/', got {v:?}"), 4285 } 4286 } 4287 4288 #[test] 4289 fn test_regexp_literal() { 4290 match eval("/abc/.test('abc')").unwrap() { 4291 Value::Boolean(b) => assert!(b), 4292 v => panic!("expected true, got {v:?}"), 4293 } 4294 match eval("/abc/.test('xyz')").unwrap() { 4295 Value::Boolean(b) => assert!(!b), 4296 v => panic!("expected false, got {v:?}"), 4297 } 4298 match eval("/\\d+/.test('123')").unwrap() { 4299 Value::Boolean(b) => assert!(b), 4300 v => panic!("expected true, got {v:?}"), 4301 } 4302 match eval("/abc/i.test('ABC')").unwrap() { 4303 Value::Boolean(b) => assert!(b), 4304 v => panic!("expected true, got {v:?}"), 4305 } 4306 } 4307 4308 #[test] 4309 fn test_regexp_literal_exec() { 4310 match eval("var m = /([a-z]+)(\\d+)/.exec('abc123'); m[0]").unwrap() { 4311 Value::String(s) => assert_eq!(s, "abc123"), 4312 v => panic!("expected 'abc123', got {v:?}"), 4313 } 4314 match eval("var m = /([a-z]+)(\\d+)/.exec('abc123'); m[1]").unwrap() { 4315 Value::String(s) => assert_eq!(s, "abc"), 4316 v => panic!("expected 'abc', got {v:?}"), 4317 } 4318 match eval("var m = /([a-z]+)(\\d+)/.exec('abc123'); m[2]").unwrap() { 4319 Value::String(s) => assert_eq!(s, "123"), 4320 v => panic!("expected '123', got {v:?}"), 4321 } 4322 } 4323 4324 #[test] 4325 fn test_string_match_regexp() { 4326 match eval("'hello world'.match(/world/)[0]").unwrap() { 4327 Value::String(s) => assert_eq!(s, "world"), 4328 v => panic!("expected 'world', got {v:?}"), 4329 } 4330 match eval("'aaa'.match(/a/g).length").unwrap() { 4331 Value::Number(n) => assert_eq!(n, 3.0), 4332 v => panic!("expected 3, got {v:?}"), 4333 } 4334 match eval("'abc'.match(/xyz/)").unwrap() { 4335 Value::Null => {} 4336 v => panic!("expected null, got {v:?}"), 4337 } 4338 } 4339 4340 #[test] 4341 fn test_string_search_regexp() { 4342 match eval("'hello world'.search(/world/)").unwrap() { 4343 Value::Number(n) => assert_eq!(n, 6.0), 4344 v => panic!("expected 6, got {v:?}"), 4345 } 4346 match eval("'abc'.search(/xyz/)").unwrap() { 4347 Value::Number(n) => assert_eq!(n, -1.0), 4348 v => panic!("expected -1, got {v:?}"), 4349 } 4350 match eval("'abc123'.search(/\\d/)").unwrap() { 4351 Value::Number(n) => assert_eq!(n, 3.0), 4352 v => panic!("expected 3, got {v:?}"), 4353 } 4354 } 4355 4356 #[test] 4357 fn test_string_replace_regexp() { 4358 match eval("'hello world'.replace(/world/, 'rust')").unwrap() { 4359 Value::String(s) => assert_eq!(s, "hello rust"), 4360 v => panic!("expected 'hello rust', got {v:?}"), 4361 } 4362 match eval("'aaa'.replace(/a/, 'b')").unwrap() { 4363 Value::String(s) => assert_eq!(s, "baa"), 4364 v => panic!("expected 'baa', got {v:?}"), 4365 } 4366 match eval("'aaa'.replace(/a/g, 'b')").unwrap() { 4367 Value::String(s) => assert_eq!(s, "bbb"), 4368 v => panic!("expected 'bbb', got {v:?}"), 4369 } 4370 } 4371 4372 #[test] 4373 fn test_string_replace_capture_groups() { 4374 let src = r#"'John Smith'.replace(/(\w+) (\w+)/, '$2, $1')"#; 4375 match eval(src).unwrap() { 4376 Value::String(s) => assert_eq!(s, "Smith, John"), 4377 v => panic!("expected 'Smith, John', got {v:?}"), 4378 } 4379 match eval("'abc'.replace(/(b)/, '[$1]')").unwrap() { 4380 Value::String(s) => assert_eq!(s, "a[b]c"), 4381 v => panic!("expected 'a[b]c', got {v:?}"), 4382 } 4383 } 4384 4385 #[test] 4386 fn test_string_split_regexp() { 4387 match eval("'a1b2c3'.split(/\\d/).length").unwrap() { 4388 Value::Number(n) => assert_eq!(n, 4.0), 4389 v => panic!("expected 4, got {v:?}"), 4390 } 4391 match eval("'a1b2c3'.split(/\\d/)[0]").unwrap() { 4392 Value::String(s) => assert_eq!(s, "a"), 4393 v => panic!("expected 'a', got {v:?}"), 4394 } 4395 } 4396 4397 #[test] 4398 fn test_regexp_ignore_case() { 4399 match eval("/abc/i.exec('XAbCx')[0]").unwrap() { 4400 Value::String(s) => assert_eq!(s, "AbC"), 4401 v => panic!("expected 'AbC', got {v:?}"), 4402 } 4403 } 4404 4405 #[test] 4406 fn test_regexp_multiline() { 4407 match eval("/^b/m.test('a\\nb')").unwrap() { 4408 Value::Boolean(b) => assert!(b), 4409 v => panic!("expected true, got {v:?}"), 4410 } 4411 match eval("/^b/.test('a\\nb')").unwrap() { 4412 Value::Boolean(b) => assert!(!b), 4413 v => panic!("expected false, got {v:?}"), 4414 } 4415 } 4416 4417 #[test] 4418 fn test_regexp_dot_all() { 4419 match eval("/a.b/s.test('a\\nb')").unwrap() { 4420 Value::Boolean(b) => assert!(b), 4421 v => panic!("expected true, got {v:?}"), 4422 } 4423 match eval("/a.b/.test('a\\nb')").unwrap() { 4424 Value::Boolean(b) => assert!(!b), 4425 v => panic!("expected false, got {v:?}"), 4426 } 4427 } 4428 4429 #[test] 4430 fn test_regexp_word_boundary() { 4431 match eval("/\\bfoo\\b/.test('a foo b')").unwrap() { 4432 Value::Boolean(b) => assert!(b), 4433 v => panic!("expected true, got {v:?}"), 4434 } 4435 match eval("/\\bfoo\\b/.test('foobar')").unwrap() { 4436 Value::Boolean(b) => assert!(!b), 4437 v => panic!("expected false, got {v:?}"), 4438 } 4439 } 4440 4441 #[test] 4442 fn test_regexp_quantifiers_vm() { 4443 match eval("/a{3}/.test('aaa')").unwrap() { 4444 Value::Boolean(b) => assert!(b), 4445 v => panic!("expected true, got {v:?}"), 4446 } 4447 match eval("/a{3}/.test('aa')").unwrap() { 4448 Value::Boolean(b) => assert!(!b), 4449 v => panic!("expected false, got {v:?}"), 4450 } 4451 match eval("/a+?/.exec('aaa')[0]").unwrap() { 4452 Value::String(s) => assert_eq!(s, "a"), 4453 v => panic!("expected 'a', got {v:?}"), 4454 } 4455 } 4456 4457 #[test] 4458 fn test_regexp_alternation_vm() { 4459 match eval("/cat|dog/.exec('I have a dog')[0]").unwrap() { 4460 Value::String(s) => assert_eq!(s, "dog"), 4461 v => panic!("expected 'dog', got {v:?}"), 4462 } 4463 } 4464 4465 #[test] 4466 fn test_regexp_lookahead_vm() { 4467 match eval("/a(?=b)/.test('ab')").unwrap() { 4468 Value::Boolean(b) => assert!(b), 4469 v => panic!("expected true, got {v:?}"), 4470 } 4471 match eval("/a(?=b)/.test('ac')").unwrap() { 4472 Value::Boolean(b) => assert!(!b), 4473 v => panic!("expected false, got {v:?}"), 4474 } 4475 match eval("/a(?!b)/.test('ac')").unwrap() { 4476 Value::Boolean(b) => assert!(b), 4477 v => panic!("expected true, got {v:?}"), 4478 } 4479 } 4480 4481 #[test] 4482 fn test_regexp_char_class_vm() { 4483 match eval("/[abc]/.test('b')").unwrap() { 4484 Value::Boolean(b) => assert!(b), 4485 v => panic!("expected true, got {v:?}"), 4486 } 4487 match eval("/[a-z]+/.exec('Hello')[0]").unwrap() { 4488 Value::String(s) => assert_eq!(s, "ello"), 4489 v => panic!("expected 'ello', got {v:?}"), 4490 } 4491 } 4492 4493 #[test] 4494 fn test_regexp_backreference_vm() { 4495 match eval("/(a)\\1/.test('aa')").unwrap() { 4496 Value::Boolean(b) => assert!(b), 4497 v => panic!("expected true, got {v:?}"), 4498 } 4499 match eval("/(a)\\1/.test('ab')").unwrap() { 4500 Value::Boolean(b) => assert!(!b), 4501 v => panic!("expected false, got {v:?}"), 4502 } 4503 } 4504 4505 #[test] 4506 fn test_regexp_properties() { 4507 match eval("var r = /abc/gim; r.global").unwrap() { 4508 Value::Boolean(b) => assert!(b), 4509 v => panic!("expected true, got {v:?}"), 4510 } 4511 match eval("/abc/.lastIndex").unwrap() { 4512 Value::Number(n) => assert_eq!(n, 0.0), 4513 v => panic!("expected 0, got {v:?}"), 4514 } 4515 } 4516 4517 #[test] 4518 fn test_string_replace_all_regexp() { 4519 match eval("'aba'.replaceAll(/a/g, 'x')").unwrap() { 4520 Value::String(s) => assert_eq!(s, "xbx"), 4521 v => panic!("expected 'xbx', got {v:?}"), 4522 } 4523 } 4524 4525 // ── Map tests ───────────────────────────────────────────── 4526 4527 #[test] 4528 fn test_map_basic() { 4529 match eval("var m = new Map(); m.set('a', 1); m.get('a')").unwrap() { 4530 Value::Number(n) => assert_eq!(n, 1.0), 4531 v => panic!("expected 1, got {v:?}"), 4532 } 4533 } 4534 4535 #[test] 4536 fn test_map_size() { 4537 match eval("var m = new Map(); m.set('a', 1); m.set('b', 2); m.size").unwrap() { 4538 Value::Number(n) => assert_eq!(n, 2.0), 4539 v => panic!("expected 2, got {v:?}"), 4540 } 4541 } 4542 4543 #[test] 4544 fn test_map_has() { 4545 match eval("var m = new Map(); m.set('x', 10); m.has('x')").unwrap() { 4546 Value::Boolean(b) => assert!(b), 4547 v => panic!("expected true, got {v:?}"), 4548 } 4549 match eval("var m = new Map(); m.has('x')").unwrap() { 4550 Value::Boolean(b) => assert!(!b), 4551 v => panic!("expected false, got {v:?}"), 4552 } 4553 } 4554 4555 #[test] 4556 fn test_map_delete() { 4557 match eval("var m = new Map(); m.set('a', 1); m['delete']('a'); m.has('a')").unwrap() { 4558 Value::Boolean(b) => assert!(!b), 4559 v => panic!("expected false, got {v:?}"), 4560 } 4561 match eval("var m = new Map(); m.set('a', 1); m['delete']('a'); m.size").unwrap() { 4562 Value::Number(n) => assert_eq!(n, 0.0), 4563 v => panic!("expected 0, got {v:?}"), 4564 } 4565 } 4566 4567 #[test] 4568 fn test_map_clear() { 4569 match eval("var m = new Map(); m.set('a', 1); m.set('b', 2); m.clear(); m.size").unwrap() { 4570 Value::Number(n) => assert_eq!(n, 0.0), 4571 v => panic!("expected 0, got {v:?}"), 4572 } 4573 } 4574 4575 #[test] 4576 fn test_map_overwrite() { 4577 match eval("var m = new Map(); m.set('a', 1); m.set('a', 2); m.get('a')").unwrap() { 4578 Value::Number(n) => assert_eq!(n, 2.0), 4579 v => panic!("expected 2, got {v:?}"), 4580 } 4581 // Size should still be 1 after overwriting. 4582 match eval("var m = new Map(); m.set('a', 1); m.set('a', 2); m.size").unwrap() { 4583 Value::Number(n) => assert_eq!(n, 1.0), 4584 v => panic!("expected 1, got {v:?}"), 4585 } 4586 } 4587 4588 #[test] 4589 fn test_map_get_missing() { 4590 match eval("var m = new Map(); m.get('missing')").unwrap() { 4591 Value::Undefined => {} 4592 v => panic!("expected undefined, got {v:?}"), 4593 } 4594 } 4595 4596 #[test] 4597 fn test_map_chaining() { 4598 // set() returns the Map for chaining. 4599 match eval("var m = new Map(); m.set('a', 1).set('b', 2); m.size").unwrap() { 4600 Value::Number(n) => assert_eq!(n, 2.0), 4601 v => panic!("expected 2, got {v:?}"), 4602 } 4603 } 4604 4605 #[test] 4606 fn test_map_nan_key() { 4607 // NaN === NaN for Map keys (SameValueZero). 4608 match eval("var m = new Map(); m.set(NaN, 'nan'); m.get(NaN)").unwrap() { 4609 Value::String(s) => assert_eq!(s, "nan"), 4610 v => panic!("expected 'nan', got {v:?}"), 4611 } 4612 } 4613 4614 #[test] 4615 fn test_map_object_key() { 4616 match eval("var m = new Map(); var o = {}; m.set(o, 'val'); m.get(o)").unwrap() { 4617 Value::String(s) => assert_eq!(s, "val"), 4618 v => panic!("expected 'val', got {v:?}"), 4619 } 4620 } 4621 4622 #[test] 4623 fn test_map_constructor_with_pairs() { 4624 match eval("var m = new Map([['a', 1], ['b', 2]]); m.get('b')").unwrap() { 4625 Value::Number(n) => assert_eq!(n, 2.0), 4626 v => panic!("expected 2, got {v:?}"), 4627 } 4628 } 4629 4630 #[test] 4631 fn test_map_keys_values_entries() { 4632 match eval("var m = new Map(); m.set('a', 1); m.set('b', 2); m.keys().length").unwrap() { 4633 Value::Number(n) => assert_eq!(n, 2.0), 4634 v => panic!("expected 2, got {v:?}"), 4635 } 4636 match eval("var m = new Map(); m.set('a', 1); m.set('b', 2); m.values()[1]").unwrap() { 4637 Value::Number(n) => assert_eq!(n, 2.0), 4638 v => panic!("expected 2, got {v:?}"), 4639 } 4640 match eval("var m = new Map(); m.set('a', 1); m.entries()[0][0]").unwrap() { 4641 Value::String(s) => assert_eq!(s, "a"), 4642 v => panic!("expected 'a', got {v:?}"), 4643 } 4644 } 4645 4646 #[test] 4647 fn test_map_insertion_order() { 4648 match eval("var m = new Map(); m.set('c', 3); m.set('a', 1); m.set('b', 2); m.keys()[0]") 4649 .unwrap() 4650 { 4651 Value::String(s) => assert_eq!(s, "c"), 4652 v => panic!("expected 'c', got {v:?}"), 4653 } 4654 } 4655 4656 // ── Set tests ───────────────────────────────────────────── 4657 4658 #[test] 4659 fn test_set_basic() { 4660 match eval("var s = new Set(); s.add(1); s.add(2); s.size").unwrap() { 4661 Value::Number(n) => assert_eq!(n, 2.0), 4662 v => panic!("expected 2, got {v:?}"), 4663 } 4664 } 4665 4666 #[test] 4667 fn test_set_has() { 4668 match eval("var s = new Set(); s.add(42); s.has(42)").unwrap() { 4669 Value::Boolean(b) => assert!(b), 4670 v => panic!("expected true, got {v:?}"), 4671 } 4672 match eval("var s = new Set(); s.has(42)").unwrap() { 4673 Value::Boolean(b) => assert!(!b), 4674 v => panic!("expected false, got {v:?}"), 4675 } 4676 } 4677 4678 #[test] 4679 fn test_set_delete() { 4680 match eval("var s = new Set(); s.add(1); s['delete'](1); s.has(1)").unwrap() { 4681 Value::Boolean(b) => assert!(!b), 4682 v => panic!("expected false, got {v:?}"), 4683 } 4684 match eval("var s = new Set(); s.add(1); s['delete'](1); s.size").unwrap() { 4685 Value::Number(n) => assert_eq!(n, 0.0), 4686 v => panic!("expected 0, got {v:?}"), 4687 } 4688 } 4689 4690 #[test] 4691 fn test_set_clear() { 4692 match eval("var s = new Set(); s.add(1); s.add(2); s.clear(); s.size").unwrap() { 4693 Value::Number(n) => assert_eq!(n, 0.0), 4694 v => panic!("expected 0, got {v:?}"), 4695 } 4696 } 4697 4698 #[test] 4699 fn test_set_uniqueness() { 4700 match eval("var s = new Set(); s.add(1); s.add(1); s.add(1); s.size").unwrap() { 4701 Value::Number(n) => assert_eq!(n, 1.0), 4702 v => panic!("expected 1, got {v:?}"), 4703 } 4704 } 4705 4706 #[test] 4707 fn test_set_chaining() { 4708 match eval("var s = new Set(); s.add(1).add(2).add(3); s.size").unwrap() { 4709 Value::Number(n) => assert_eq!(n, 3.0), 4710 v => panic!("expected 3, got {v:?}"), 4711 } 4712 } 4713 4714 #[test] 4715 fn test_set_nan() { 4716 match eval("var s = new Set(); s.add(NaN); s.add(NaN); s.size").unwrap() { 4717 Value::Number(n) => assert_eq!(n, 1.0), 4718 v => panic!("expected 1, got {v:?}"), 4719 } 4720 match eval("var s = new Set(); s.add(NaN); s.has(NaN)").unwrap() { 4721 Value::Boolean(b) => assert!(b), 4722 v => panic!("expected true, got {v:?}"), 4723 } 4724 } 4725 4726 #[test] 4727 fn test_set_constructor_from_array() { 4728 match eval("var s = new Set([1, 2, 3, 2, 1]); s.size").unwrap() { 4729 Value::Number(n) => assert_eq!(n, 3.0), 4730 v => panic!("expected 3, got {v:?}"), 4731 } 4732 } 4733 4734 #[test] 4735 fn test_set_values() { 4736 match eval("var s = new Set(); s.add('a'); s.add('b'); s.values().length").unwrap() { 4737 Value::Number(n) => assert_eq!(n, 2.0), 4738 v => panic!("expected 2, got {v:?}"), 4739 } 4740 } 4741 4742 #[test] 4743 fn test_set_entries() { 4744 // Set.entries() returns [value, value] pairs. 4745 match eval("var s = new Set(); s.add('x'); s.entries()[0][0]").unwrap() { 4746 Value::String(s) => assert_eq!(s, "x"), 4747 v => panic!("expected 'x', got {v:?}"), 4748 } 4749 match eval("var s = new Set(); s.add('x'); s.entries()[0][1]").unwrap() { 4750 Value::String(s) => assert_eq!(s, "x"), 4751 v => panic!("expected 'x', got {v:?}"), 4752 } 4753 } 4754 4755 #[test] 4756 fn test_set_insertion_order() { 4757 match eval("var s = new Set(); s.add('c'); s.add('a'); s.add('b'); s.values()[0]").unwrap() 4758 { 4759 Value::String(s) => assert_eq!(s, "c"), 4760 v => panic!("expected 'c', got {v:?}"), 4761 } 4762 } 4763 4764 // ── WeakMap tests ───────────────────────────────────────── 4765 4766 #[test] 4767 fn test_weakmap_basic() { 4768 match eval("var wm = new WeakMap(); var o = {}; wm.set(o, 'val'); wm.get(o)").unwrap() { 4769 Value::String(s) => assert_eq!(s, "val"), 4770 v => panic!("expected 'val', got {v:?}"), 4771 } 4772 } 4773 4774 #[test] 4775 fn test_weakmap_has_delete() { 4776 match eval("var wm = new WeakMap(); var o = {}; wm.set(o, 1); wm.has(o)").unwrap() { 4777 Value::Boolean(b) => assert!(b), 4778 v => panic!("expected true, got {v:?}"), 4779 } 4780 match eval("var wm = new WeakMap(); var o = {}; wm.set(o, 1); wm['delete'](o); wm.has(o)") 4781 .unwrap() 4782 { 4783 Value::Boolean(b) => assert!(!b), 4784 v => panic!("expected false, got {v:?}"), 4785 } 4786 } 4787 4788 #[test] 4789 fn test_weakmap_rejects_primitive_key() { 4790 assert!(eval("var wm = new WeakMap(); wm.set('str', 1)").is_err()); 4791 assert!(eval("var wm = new WeakMap(); wm.set(42, 1)").is_err()); 4792 } 4793 4794 // ── WeakSet tests ───────────────────────────────────────── 4795 4796 #[test] 4797 fn test_weakset_basic() { 4798 match eval("var ws = new WeakSet(); var o = {}; ws.add(o); ws.has(o)").unwrap() { 4799 Value::Boolean(b) => assert!(b), 4800 v => panic!("expected true, got {v:?}"), 4801 } 4802 } 4803 4804 #[test] 4805 fn test_weakset_delete() { 4806 match eval("var ws = new WeakSet(); var o = {}; ws.add(o); ws['delete'](o); ws.has(o)") 4807 .unwrap() 4808 { 4809 Value::Boolean(b) => assert!(!b), 4810 v => panic!("expected false, got {v:?}"), 4811 } 4812 } 4813 4814 #[test] 4815 fn test_weakset_rejects_primitive() { 4816 assert!(eval("var ws = new WeakSet(); ws.add('str')").is_err()); 4817 assert!(eval("var ws = new WeakSet(); ws.add(42)").is_err()); 4818 } 4819}