1#![allow(clippy::collapsible_if, clippy::collapsible_match)]
2
3use num_bigint::BigInt;
4use std::sync::{Arc, Mutex};
5use std::{cell::RefCell, rc::Rc};
6
7use crate::js_date::is_date_object;
8use crate::{
9 JSError,
10 core::{BinaryOp, Expr, PropertyKey, Statement, evaluate_statements, get_well_known_symbol_rc, utf8_to_utf16},
11 js_array::is_array,
12 js_class::ClassDefinition,
13 js_promise::JSPromise,
14 raise_eval_error, raise_type_error,
15};
16
17#[derive(Clone, Debug)]
18pub struct JSMap {
19 pub entries: Vec<(Value, Value)>, }
21
22#[derive(Clone, Debug)]
23pub struct JSSet {
24 pub values: Vec<Value>,
25}
26
27#[derive(Clone, Debug)]
28pub struct JSWeakMap {
29 pub entries: Vec<(std::rc::Weak<RefCell<JSObjectData>>, Value)>, }
31
32#[derive(Clone, Debug)]
33pub struct JSWeakSet {
34 pub values: Vec<std::rc::Weak<RefCell<JSObjectData>>>, }
36
37#[derive(Clone, Debug)]
38pub struct JSGenerator {
39 pub params: Vec<(String, Option<Box<Expr>>)>,
40 pub body: Vec<Statement>,
41 pub env: JSObjectDataPtr, pub state: GeneratorState,
43}
44
45#[derive(Clone, Debug)]
46pub struct JSProxy {
47 pub target: Value, pub handler: Value, pub revoked: bool, }
51
52#[derive(Clone, Debug)]
53pub struct JSArrayBuffer {
54 pub data: Arc<Mutex<Vec<u8>>>, pub detached: bool, pub shared: bool, }
62
63#[derive(Clone, Debug)]
64pub struct JSDataView {
65 pub buffer: Rc<RefCell<JSArrayBuffer>>, pub byte_offset: usize, pub byte_length: usize, }
69
70#[derive(Clone, Debug, PartialEq)]
71pub enum TypedArrayKind {
72 Int8,
73 Uint8,
74 Uint8Clamped,
75 Int16,
76 Uint16,
77 Int32,
78 Uint32,
79 Float32,
80 Float64,
81 BigInt64,
82 BigUint64,
83}
84
85#[derive(Clone, Debug)]
86pub struct JSTypedArray {
87 pub kind: TypedArrayKind,
88 pub buffer: Rc<RefCell<JSArrayBuffer>>, pub byte_offset: usize, pub length: usize, }
92
93pub fn parse_bigint_string(raw: &str) -> Result<BigInt, JSError> {
94 let s = if let Some(st) = raw.strip_suffix('n') { st } else { raw };
95 let (radix, num_str) = if s.starts_with("0x") || s.starts_with("0X") {
96 (16, &s[2..])
97 } else if s.starts_with("0b") || s.starts_with("0B") {
98 (2, &s[2..])
99 } else if s.starts_with("0o") || s.starts_with("0O") {
100 (8, &s[2..])
101 } else {
102 (10, s)
103 };
104 BigInt::parse_bytes(num_str.as_bytes(), radix).ok_or(raise_eval_error!("invalid bigint"))
105}
106
107impl JSTypedArray {
108 pub fn element_size(&self) -> usize {
110 match self.kind {
111 TypedArrayKind::Int8 | TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => 1,
112 TypedArrayKind::Int16 | TypedArrayKind::Uint16 => 2,
113 TypedArrayKind::Int32 | TypedArrayKind::Uint32 | TypedArrayKind::Float32 => 4,
114 TypedArrayKind::Float64 | TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => 8,
115 }
116 }
117
118 pub fn get(&self, index: usize) -> Result<i64, JSError> {
120 if index >= self.length {
121 return Err(raise_type_error!("Index out of bounds"));
122 }
123
124 let buffer = self.buffer.borrow();
125 if buffer.detached {
126 return Err(raise_type_error!("ArrayBuffer is detached"));
127 }
128
129 let byte_index = self.byte_offset + index * self.element_size();
130 let data_lock = buffer.data.lock().unwrap();
131 if byte_index + self.element_size() > data_lock.len() {
132 return Err(raise_type_error!("Index out of bounds"));
133 }
134
135 match self.kind {
136 TypedArrayKind::Int8 => Ok(data_lock[byte_index] as i8 as i64),
137 TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => Ok(data_lock[byte_index] as i64),
138 TypedArrayKind::Int16 => {
139 let bytes = &data_lock[byte_index..byte_index + 2];
140 Ok(i16::from_le_bytes([bytes[0], bytes[1]]) as i64)
141 }
142 TypedArrayKind::Uint16 => {
143 let bytes = &data_lock[byte_index..byte_index + 2];
144 Ok(u16::from_le_bytes([bytes[0], bytes[1]]) as i64)
145 }
146 TypedArrayKind::Int32 => {
147 let bytes = &data_lock[byte_index..byte_index + 4];
148 Ok(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64)
149 }
150 TypedArrayKind::Uint32 => {
151 let bytes = &data_lock[byte_index..byte_index + 4];
152 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64)
153 }
154 TypedArrayKind::Float32 => {
155 let bytes = &data_lock[byte_index..byte_index + 4];
156 let float_val = f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
157 Ok(float_val as i64) }
159 TypedArrayKind::Float64 => {
160 let bytes = &data_lock[byte_index..byte_index + 8];
161 let float_val = f64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]);
162 Ok(float_val as i64) }
164 TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => {
165 Ok(0)
167 }
168 }
169 }
170
171 pub fn set(&mut self, index: usize, value: i64) -> Result<(), JSError> {
173 if index >= self.length {
174 return Err(raise_type_error!("Index out of bounds"));
175 }
176 let buffer = self.buffer.borrow_mut();
177 if buffer.detached {
178 return Err(raise_type_error!("ArrayBuffer is detached"));
179 }
180
181 let byte_index = self.byte_offset + index * self.element_size();
182 let mut data_lock = buffer.data.lock().unwrap();
183 if byte_index + self.element_size() > data_lock.len() {
184 return Err(raise_type_error!("Index out of bounds"));
185 }
186
187 match self.kind {
188 TypedArrayKind::Int8 => {
189 data_lock[byte_index] = value as i8 as u8;
190 }
191 TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => {
192 data_lock[byte_index] = value as u8;
193 }
194 TypedArrayKind::Int16 => {
195 let bytes = (value as i16).to_le_bytes();
196 data_lock[byte_index..byte_index + 2].copy_from_slice(&bytes);
197 }
198 TypedArrayKind::Uint16 => {
199 let bytes = (value as u16).to_le_bytes();
200 data_lock[byte_index..byte_index + 2].copy_from_slice(&bytes);
201 }
202 TypedArrayKind::Int32 => {
203 let bytes = (value as i32).to_le_bytes();
204 data_lock[byte_index..byte_index + 4].copy_from_slice(&bytes);
205 }
206 TypedArrayKind::Uint32 => {
207 let bytes = (value as u32).to_le_bytes();
208 data_lock[byte_index..byte_index + 4].copy_from_slice(&bytes);
209 }
210 TypedArrayKind::Float32 => {
211 let bytes = (value as f32).to_le_bytes();
212 data_lock[byte_index..byte_index + 4].copy_from_slice(&bytes);
213 }
214 TypedArrayKind::Float64 => {
215 let bytes = (value as f64).to_le_bytes();
216 data_lock[byte_index..byte_index + 8].copy_from_slice(&bytes);
217 }
218 TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => {
219 }
221 }
222
223 Ok(())
224 }
225}
226
227impl JSDataView {
228 pub fn get_int8(&self, offset: usize) -> Result<i8, JSError> {
230 self.check_bounds(offset, 1)?;
231 let buffer = self.buffer.borrow();
232 let data_lock = buffer.data.lock().unwrap();
233 Ok(data_lock[self.byte_offset + offset] as i8)
234 }
235
236 pub fn get_uint8(&self, offset: usize) -> Result<u8, JSError> {
238 self.check_bounds(offset, 1)?;
239 let buffer = self.buffer.borrow();
240 let data_lock = buffer.data.lock().unwrap();
241 Ok(data_lock[self.byte_offset + offset])
242 }
243
244 pub fn get_int16(&self, offset: usize, little_endian: bool) -> Result<i16, JSError> {
246 self.check_bounds(offset, 2)?;
247 let buffer = self.buffer.borrow();
248 let data_lock = buffer.data.lock().unwrap();
249 let bytes = &data_lock[self.byte_offset + offset..self.byte_offset + offset + 2];
250 if little_endian {
251 Ok(i16::from_le_bytes([bytes[0], bytes[1]]))
252 } else {
253 Ok(i16::from_be_bytes([bytes[0], bytes[1]]))
254 }
255 }
256
257 pub fn get_uint16(&self, offset: usize, little_endian: bool) -> Result<u16, JSError> {
259 self.check_bounds(offset, 2)?;
260 let buffer = self.buffer.borrow();
261 let data_lock = buffer.data.lock().unwrap();
262 let bytes = &data_lock[self.byte_offset + offset..self.byte_offset + offset + 2];
263 if little_endian {
264 Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
265 } else {
266 Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
267 }
268 }
269
270 pub fn get_int32(&self, offset: usize, little_endian: bool) -> Result<i32, JSError> {
272 self.check_bounds(offset, 4)?;
273 let buffer = self.buffer.borrow();
274 let data_lock = buffer.data.lock().unwrap();
275 let bytes = &data_lock[self.byte_offset + offset..self.byte_offset + offset + 4];
276 if little_endian {
277 Ok(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
278 } else {
279 Ok(i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
280 }
281 }
282
283 pub fn get_uint32(&self, offset: usize, little_endian: bool) -> Result<u32, JSError> {
285 self.check_bounds(offset, 4)?;
286 let buffer = self.buffer.borrow();
287 let data_lock = buffer.data.lock().unwrap();
288 let bytes = &data_lock[self.byte_offset + offset..self.byte_offset + offset + 4];
289 if little_endian {
290 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
291 } else {
292 Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
293 }
294 }
295
296 pub fn get_float32(&self, offset: usize, little_endian: bool) -> Result<f32, JSError> {
298 self.check_bounds(offset, 4)?;
299 let buffer = self.buffer.borrow();
300 let data_lock = buffer.data.lock().unwrap();
301 let bytes = &data_lock[self.byte_offset + offset..self.byte_offset + offset + 4];
302 if little_endian {
303 Ok(f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
304 } else {
305 Ok(f32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
306 }
307 }
308
309 pub fn get_float64(&self, offset: usize, little_endian: bool) -> Result<f64, JSError> {
311 self.check_bounds(offset, 8)?;
312 let buffer = self.buffer.borrow();
313 let data_lock = buffer.data.lock().unwrap();
314 let bytes = &data_lock[self.byte_offset + offset..self.byte_offset + offset + 8];
315 if little_endian {
316 Ok(f64::from_le_bytes([
317 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
318 ]))
319 } else {
320 Ok(f64::from_be_bytes([
321 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
322 ]))
323 }
324 }
325
326 pub fn get_big_int64(&self, offset: usize, little_endian: bool) -> Result<i64, JSError> {
328 self.check_bounds(offset, 8)?;
329 let buffer = self.buffer.borrow();
330 let data_lock = buffer.data.lock().unwrap();
331 let bytes = &data_lock[self.byte_offset + offset..self.byte_offset + offset + 8];
332 if little_endian {
333 Ok(i64::from_le_bytes([
334 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
335 ]))
336 } else {
337 Ok(i64::from_be_bytes([
338 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
339 ]))
340 }
341 }
342
343 pub fn get_big_uint64(&self, offset: usize, little_endian: bool) -> Result<u64, JSError> {
345 self.check_bounds(offset, 8)?;
346 let buffer = self.buffer.borrow();
347 let data_lock = buffer.data.lock().unwrap();
348 let bytes = &data_lock[self.byte_offset + offset..self.byte_offset + offset + 8];
349 if little_endian {
350 Ok(u64::from_le_bytes([
351 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
352 ]))
353 } else {
354 Ok(u64::from_be_bytes([
355 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
356 ]))
357 }
358 }
359
360 pub fn set_int8(&mut self, offset: usize, value: i8) -> Result<(), JSError> {
362 self.check_bounds(offset, 1)?;
363 let buffer = self.buffer.borrow_mut();
364 let mut data_lock = buffer.data.lock().unwrap();
365 data_lock[self.byte_offset + offset] = value as u8;
366 Ok(())
367 }
368
369 pub fn set_uint8(&mut self, offset: usize, value: u8) -> Result<(), JSError> {
371 self.check_bounds(offset, 1)?;
372 let buffer = self.buffer.borrow_mut();
373 let mut data_lock = buffer.data.lock().unwrap();
374 data_lock[self.byte_offset + offset] = value;
375 Ok(())
376 }
377
378 pub fn set_int16(&mut self, offset: usize, value: i16, little_endian: bool) -> Result<(), JSError> {
380 self.check_bounds(offset, 2)?;
381 let buffer = self.buffer.borrow_mut();
382 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
383 let mut data_lock = buffer.data.lock().unwrap();
384 data_lock[self.byte_offset + offset..self.byte_offset + offset + 2].copy_from_slice(&bytes);
385 Ok(())
386 }
387
388 pub fn set_uint16(&mut self, offset: usize, value: u16, little_endian: bool) -> Result<(), JSError> {
390 self.check_bounds(offset, 2)?;
391 let buffer = self.buffer.borrow_mut();
392 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
393 let mut data_lock = buffer.data.lock().unwrap();
394 data_lock[self.byte_offset + offset..self.byte_offset + offset + 2].copy_from_slice(&bytes);
395 Ok(())
396 }
397
398 pub fn set_int32(&mut self, offset: usize, value: i32, little_endian: bool) -> Result<(), JSError> {
400 self.check_bounds(offset, 4)?;
401 let buffer = self.buffer.borrow_mut();
402 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
403 let mut data_lock = buffer.data.lock().unwrap();
404 data_lock[self.byte_offset + offset..self.byte_offset + offset + 4].copy_from_slice(&bytes);
405 Ok(())
406 }
407
408 pub fn set_uint32(&mut self, offset: usize, value: u32, little_endian: bool) -> Result<(), JSError> {
410 self.check_bounds(offset, 4)?;
411 let buffer = self.buffer.borrow_mut();
412 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
413 let mut data_lock = buffer.data.lock().unwrap();
414 data_lock[self.byte_offset + offset..self.byte_offset + offset + 4].copy_from_slice(&bytes);
415 Ok(())
416 }
417
418 pub fn set_float32(&mut self, offset: usize, value: f32, little_endian: bool) -> Result<(), JSError> {
420 self.check_bounds(offset, 4)?;
421 let buffer = self.buffer.borrow_mut();
422 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
423 let mut data_lock = buffer.data.lock().unwrap();
424 data_lock[self.byte_offset + offset..self.byte_offset + offset + 4].copy_from_slice(&bytes);
425 Ok(())
426 }
427
428 pub fn set_float64(&mut self, offset: usize, value: f64, little_endian: bool) -> Result<(), JSError> {
430 self.check_bounds(offset, 8)?;
431 let buffer = self.buffer.borrow_mut();
432 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
433 let mut data_lock = buffer.data.lock().unwrap();
434 data_lock[self.byte_offset + offset..self.byte_offset + offset + 8].copy_from_slice(&bytes);
435 Ok(())
436 }
437
438 pub fn set_big_int64(&mut self, offset: usize, value: i64, little_endian: bool) -> Result<(), JSError> {
440 self.check_bounds(offset, 8)?;
441 let buffer = self.buffer.borrow_mut();
442 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
443 let mut data_lock = buffer.data.lock().unwrap();
444 data_lock[self.byte_offset + offset..self.byte_offset + offset + 8].copy_from_slice(&bytes);
445 Ok(())
446 }
447
448 pub fn set_big_uint64(&mut self, offset: usize, value: u64, little_endian: bool) -> Result<(), JSError> {
450 self.check_bounds(offset, 8)?;
451 let buffer = self.buffer.borrow_mut();
452 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
453 let mut data_lock = buffer.data.lock().unwrap();
454 data_lock[self.byte_offset + offset..self.byte_offset + offset + 8].copy_from_slice(&bytes);
455 Ok(())
456 }
457
458 fn check_bounds(&self, offset: usize, size: usize) -> Result<(), JSError> {
460 let buffer = self.buffer.borrow();
461 if buffer.detached {
462 return Err(raise_type_error!("ArrayBuffer is detached"));
463 }
464 if offset + size > self.byte_length {
465 return Err(raise_type_error!("Offset out of bounds"));
466 }
467 Ok(())
468 }
469}
470
471#[derive(Clone, Debug)]
472pub enum GeneratorState {
473 NotStarted,
474 Running { pc: usize, stack: Vec<Value> }, Suspended { pc: usize, stack: Vec<Value> }, Completed,
477}
478
479pub type JSObjectDataPtr = Rc<RefCell<JSObjectData>>;
480
481#[inline]
482pub fn new_js_object_data() -> JSObjectDataPtr {
483 Rc::new(RefCell::new(JSObjectData::new()))
484}
485
486#[derive(Clone, Default)]
487pub struct JSObjectData {
488 pub properties: std::collections::HashMap<PropertyKey, Rc<RefCell<Value>>>,
489 pub constants: std::collections::HashSet<String>,
490 pub non_enumerable: std::collections::HashSet<PropertyKey>,
492 pub non_writable: std::collections::HashSet<PropertyKey>,
494 pub non_configurable: std::collections::HashSet<PropertyKey>,
496 pub prototype: Option<Rc<RefCell<JSObjectData>>>,
497 pub is_function_scope: bool,
498}
499
500impl std::fmt::Debug for JSObjectData {
501 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
502 write!(
503 f,
504 "JSObjectData {{ properties: {}, constants: {}, prototype: {}, is_function_scope: {} }}",
505 self.properties.len(),
506 self.constants.len(),
507 self.prototype.is_some(),
508 self.is_function_scope
509 )
510 }
511}
512
513impl JSObjectData {
514 pub fn new() -> Self {
515 JSObjectData::default()
516 }
517
518 pub fn insert(&mut self, key: PropertyKey, val: Rc<RefCell<Value>>) {
519 self.properties.insert(key, val);
520 }
521
522 pub fn set_non_enumerable(&mut self, key: PropertyKey) {
524 self.non_enumerable.insert(key);
525 }
526
527 pub fn set_non_writable(&mut self, key: PropertyKey) {
529 self.non_writable.insert(key);
530 }
531
532 pub fn set_non_configurable(&mut self, key: PropertyKey) {
534 self.non_configurable.insert(key);
535 }
536
537 pub fn is_writable(&self, key: &PropertyKey) -> bool {
539 !self.non_writable.contains(key)
540 }
541
542 pub fn is_configurable(&self, key: &PropertyKey) -> bool {
544 !self.non_configurable.contains(key)
545 }
546
547 pub fn is_enumerable(&self, key: &PropertyKey) -> bool {
549 !self.non_enumerable.contains(key)
550 }
551
552 pub fn get(&self, key: &PropertyKey) -> Option<Rc<RefCell<Value>>> {
553 self.properties.get(key).cloned()
554 }
555
556 pub fn contains_key(&self, key: &PropertyKey) -> bool {
557 self.properties.contains_key(key)
558 }
559
560 pub fn remove(&mut self, key: &PropertyKey) -> Option<Rc<RefCell<Value>>> {
561 self.properties.remove(key)
562 }
563
564 pub fn keys(&self) -> std::collections::hash_map::Keys<'_, PropertyKey, Rc<RefCell<Value>>> {
565 self.properties.keys()
566 }
567
568 pub fn is_const(&self, key: &str) -> bool {
569 self.constants.contains(key)
570 }
571
572 pub fn set_const(&mut self, key: String) {
573 self.constants.insert(key);
574 }
575}
576
577#[derive(Clone, Debug)]
578pub struct SymbolData {
579 pub description: Option<String>,
580}
581
582pub type ValuePtr = Rc<RefCell<Value>>;
583
584#[derive(Debug, Clone)]
585pub enum Value {
586 Number(f64),
587 BigInt(BigInt),
588 String(Vec<u16>), Boolean(bool),
590 Undefined,
591 Null,
592 Object(JSObjectDataPtr), Function(String), Closure(Vec<(String, Option<Box<Expr>>)>, Vec<Statement>, JSObjectDataPtr), AsyncClosure(Vec<(String, Option<Box<Expr>>)>, Vec<Statement>, JSObjectDataPtr), GeneratorFunction(Option<String>, Vec<(String, Option<Box<Expr>>)>, Vec<Statement>, JSObjectDataPtr), ClassDefinition(Rc<ClassDefinition>), Getter(Vec<Statement>, JSObjectDataPtr), Setter(Vec<(String, Option<Box<Expr>>)>, Vec<Statement>, JSObjectDataPtr), Property {
601 value: Option<Rc<RefCell<Value>>>,
603 getter: Option<(Vec<Statement>, JSObjectDataPtr)>,
604 #[allow(clippy::type_complexity)]
605 setter: Option<(Vec<(String, Option<Box<Expr>>)>, Vec<Statement>, JSObjectDataPtr)>,
606 },
607 Promise(Rc<RefCell<JSPromise>>), Symbol(Rc<SymbolData>), Map(Rc<RefCell<JSMap>>), Set(Rc<RefCell<JSSet>>), WeakMap(Rc<RefCell<JSWeakMap>>), WeakSet(Rc<RefCell<JSWeakSet>>), Generator(Rc<RefCell<JSGenerator>>), Proxy(Rc<RefCell<JSProxy>>), ArrayBuffer(Rc<RefCell<JSArrayBuffer>>), DataView(Rc<RefCell<JSDataView>>), TypedArray(Rc<RefCell<JSTypedArray>>), }
619
620impl std::fmt::Display for Value {
621 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
622 write!(f, "{}", value_to_string(self))
623 }
624}
625
626pub fn is_truthy(val: &Value) -> bool {
627 match val {
628 Value::BigInt(b) => b != &BigInt::from(0),
629 Value::Number(n) => *n != 0.0 && !n.is_nan(),
630 Value::String(s) => !s.is_empty(),
631 Value::Boolean(b) => *b,
632 Value::Undefined => false,
633 Value::Null => false,
634 Value::Object(_) => true,
635 Value::Function(_) => true,
636 Value::Closure(_, _, _) => true,
637 Value::AsyncClosure(_, _, _) => true,
638 Value::GeneratorFunction(..) => true,
639 Value::ClassDefinition(_) => true,
640 Value::Getter(_, _) => true,
641 Value::Setter(_, _, _) => true,
642 Value::Property { .. } => true,
643 Value::Promise(_) => true,
644 Value::Symbol(_) => true,
645 Value::Map(_) => true,
646 Value::Set(_) => true,
647 Value::WeakMap(_) => true,
648 Value::WeakSet(_) => true,
649 Value::Generator(_) => true,
650 Value::Proxy(_) => true,
651 Value::ArrayBuffer(_) => true,
652 Value::DataView(_) => true,
653 Value::TypedArray(_) => true,
654 }
655}
656
657pub fn values_equal(a: &Value, b: &Value) -> bool {
659 match (a, b) {
660 (Value::BigInt(sa), Value::BigInt(sb)) => sa == sb,
661 (Value::Number(na), Value::Number(nb)) => na == nb,
662 (Value::String(sa), Value::String(sb)) => sa == sb,
663 (Value::Boolean(ba), Value::Boolean(bb)) => ba == bb,
664 (Value::Undefined, Value::Undefined) => true,
665 (Value::Null, Value::Null) => true,
666 (Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b), (Value::Symbol(sa), Value::Symbol(sb)) => Rc::ptr_eq(sa, sb), _ => false, }
670}
671
672pub fn value_to_string(val: &Value) -> String {
674 match val {
675 Value::Number(n) => n.to_string(),
676 Value::BigInt(b) => format!("{b}n"),
677 Value::String(s) => format!("\"{}\"", String::from_utf16_lossy(s)),
678 Value::Boolean(b) => b.to_string(),
679 Value::Undefined => "undefined".to_string(),
680 Value::Null => "null".to_string(),
681 Value::Object(obj) => {
682 if crate::js_regexp::is_regex_object(obj) {
684 if let Ok(pat) = crate::js_regexp::get_regex_literal_pattern(obj) {
685 return pat;
686 } else {
687 return "[object RegExp]".to_string();
688 }
689 }
690 let has_closure = get_own_property(obj, &"__closure__".into()).is_some();
692 log::trace!("DEBUG: has_closure = {has_closure}");
693 if has_closure {
694 "function".to_string()
695 } else if let Some(length_rc) = get_own_property(obj, &"length".into()) {
696 if let Value::Number(len) = &*length_rc.borrow() {
697 if len.is_finite() && *len >= 0.0 && len.fract() == 0.0 {
698 let len_usize = *len as usize;
699 let mut parts = Vec::new();
700 for i in 0..len_usize {
701 if let Some(val_rc) = get_own_property(obj, &i.to_string().into()) {
702 let val = val_rc.borrow();
703 let display = match *val {
704 Value::Null => "null".to_string(),
705 Value::Undefined => "undefined".to_string(),
706 _ => value_to_string(&val),
707 };
708 parts.push(display);
709 } else {
710 parts.push("undefined".to_string());
711 }
712 }
713 format!("[{}]", parts.join(", "))
714 } else {
715 "[object Object]".to_string()
716 }
717 } else {
718 "[object Object]".to_string()
719 }
720 } else {
721 "[object Object]".to_string()
722 }
723 }
724 Value::Function(name) => format!("function {}", name),
725 Value::Closure(_, _, _) => "function".to_string(),
726 Value::AsyncClosure(_, _, _) => "function".to_string(),
727 Value::GeneratorFunction(..) => "function".to_string(),
728 Value::ClassDefinition(_) => "class".to_string(),
729 Value::Getter(_, _) => "getter".to_string(),
730 Value::Setter(_, _, _) => "setter".to_string(),
731 Value::Property { .. } => "[property]".to_string(),
732 Value::Promise(_) => "[object Promise]".to_string(),
733 Value::Symbol(desc) => match desc.description.as_ref() {
734 Some(d) => format!("Symbol({})", d),
735 None => "Symbol()".to_string(),
736 },
737 Value::Map(_) => "[object Map]".to_string(),
738 Value::Set(_) => "[object Set]".to_string(),
739 Value::WeakMap(_) => "[object WeakMap]".to_string(),
740 Value::WeakSet(_) => "[object WeakSet]".to_string(),
741 Value::Generator(_) => "[object Generator]".to_string(),
742 Value::Proxy(_) => "[object Proxy]".to_string(),
743 Value::ArrayBuffer(_) => "[object ArrayBuffer]".to_string(),
744 Value::DataView(_) => "[object DataView]".to_string(),
745 Value::TypedArray(_) => "[object TypedArray]".to_string(),
746 }
747}
748
749#[allow(clippy::type_complexity)]
753pub fn extract_closure_from_value(val: &Value) -> Option<(Vec<(String, Option<Box<Expr>>)>, Vec<Statement>, JSObjectDataPtr)> {
754 match val {
755 Value::Closure(params, body, env) => Some((params.clone(), body.clone(), env.clone())),
756 Value::AsyncClosure(params, body, env) => Some((params.clone(), body.clone(), env.clone())),
757 Value::GeneratorFunction(_, params, body, env) => Some((params.clone(), body.clone(), env.clone())),
758 Value::Object(obj_map) => {
759 if let Ok(Some(cl_rc)) = obj_get_key_value(obj_map, &"__closure__".into()) {
760 match &*cl_rc.borrow() {
761 Value::Closure(params, body, env) => Some((params.clone(), body.clone(), env.clone())),
762 Value::AsyncClosure(params, body, env) => Some((params.clone(), body.clone(), env.clone())),
763 Value::GeneratorFunction(_, params, body, env) => Some((params.clone(), body.clone(), env.clone())),
764 _ => None,
765 }
766 } else {
767 None
768 }
769 }
770 _ => None,
771 }
772}
773
774pub fn to_primitive(val: &Value, hint: &str, env: &JSObjectDataPtr) -> Result<Value, JSError> {
776 match val {
777 Value::Number(_) | Value::String(_) | Value::Boolean(_) | Value::Undefined | Value::Null | Value::Symbol(_) => Ok(val.clone()),
778 Value::Object(obj_map) => {
779 if let Some(tp_sym) = get_well_known_symbol_rc("toPrimitive") {
781 let key = PropertyKey::Symbol(tp_sym.clone());
782 if let Some(method_rc) = obj_get_key_value(obj_map, &key)? {
783 let method_val = method_rc.borrow().clone();
784 if let Some((params, body, captured_env)) = extract_closure_from_value(&method_val) {
786 let func_env = new_js_object_data();
788 func_env.borrow_mut().prototype = Some(captured_env.clone());
789 env_set(&func_env, "this", Value::Object(obj_map.clone()))?;
790 if !params.is_empty() {
792 let name = ¶ms[0].0;
793 env_set(&func_env, name.as_str(), Value::String(utf8_to_utf16(hint)))?;
794 }
795 let result = evaluate_statements(&func_env, &body)?;
796 match result {
797 Value::Number(_) | Value::String(_) | Value::Boolean(_) | Value::BigInt(_) | Value::Symbol(_) => {
798 return Ok(result);
799 }
800 _ => {
801 return Err(raise_type_error!("[Symbol.toPrimitive] must return a primitive"));
802 }
803 }
804 } else {
805 }
807 }
808 }
809
810 if hint == "string" {
812 let to_s = crate::js_object::handle_to_string_method(&Value::Object(obj_map.clone()), &[], env)?;
814 if matches!(to_s, Value::String(_) | Value::Number(_) | Value::Boolean(_) | Value::BigInt(_)) {
815 return Ok(to_s);
816 }
817 let val_of = crate::js_object::handle_value_of_method(&Value::Object(obj_map.clone()), &[], env)?;
818 if matches!(val_of, Value::String(_) | Value::Number(_) | Value::Boolean(_) | Value::BigInt(_)) {
819 return Ok(val_of);
820 }
821 } else {
822 let val_of = crate::js_object::handle_value_of_method(&Value::Object(obj_map.clone()), &[], env)?;
824 if matches!(val_of, Value::Number(_) | Value::String(_) | Value::Boolean(_) | Value::BigInt(_)) {
825 return Ok(val_of);
826 }
827 let to_s = crate::js_object::handle_to_string_method(&Value::Object(obj_map.clone()), &[], env)?;
828 if matches!(to_s, Value::String(_) | Value::Number(_) | Value::Boolean(_) | Value::BigInt(_)) {
829 return Ok(to_s);
830 }
831 }
832
833 Err(raise_type_error!("Cannot convert object to primitive"))
834 }
835 _ => Ok(val.clone()),
836 }
837}
838
839pub fn value_to_sort_string(val: &Value) -> String {
841 match val {
842 Value::Number(n) => {
843 if n.is_nan() {
844 "NaN".to_string()
845 } else if *n == f64::INFINITY {
846 "Infinity".to_string()
847 } else if *n == f64::NEG_INFINITY {
848 "-Infinity".to_string()
849 } else {
850 n.to_string()
851 }
852 }
853 Value::BigInt(b) => b.to_string(),
854 Value::String(s) => String::from_utf16_lossy(s),
855 Value::Boolean(b) => b.to_string(),
856 Value::Undefined => "undefined".to_string(),
857 Value::Null => "null".to_string(),
858 Value::Object(_) => "[object Object]".to_string(),
859 Value::Function(name) => format!("[function {}]", name),
860 Value::Closure(..) | Value::AsyncClosure(..) | Value::GeneratorFunction(..) => "[function]".to_string(),
861 Value::ClassDefinition(_) => "[class]".to_string(),
862 Value::Getter(_, _) => "[getter]".to_string(),
863 Value::Setter(_, _, _) => "[setter]".to_string(),
864 Value::Property { .. } => "[property]".to_string(),
865 Value::Promise(_) => "[object Promise]".to_string(),
866 Value::Symbol(_) => "[object Symbol]".to_string(),
867 Value::Map(_) => "[object Map]".to_string(),
868 Value::Set(_) => "[object Set]".to_string(),
869 Value::WeakMap(_) => "[object WeakMap]".to_string(),
870 Value::WeakSet(_) => "[object WeakSet]".to_string(),
871 Value::Generator(_) => "[object Generator]".to_string(),
872 Value::Proxy(_) => "[object Proxy]".to_string(),
873 Value::ArrayBuffer(_) => "[object ArrayBuffer]".to_string(),
874 Value::DataView(_) => "[object DataView]".to_string(),
875 Value::TypedArray(_) => "[object TypedArray]".to_string(),
876 }
877}
878
879pub fn obj_get_key_value(js_obj: &JSObjectDataPtr, key: &PropertyKey) -> Result<Option<Rc<RefCell<Value>>>, JSError> {
881 let proxy_opt = get_own_property(js_obj, &"__proxy__".into());
884 if let Some(proxy_val_rc) = proxy_opt {
885 if let Value::Proxy(proxy) = &*proxy_val_rc.borrow() {
886 return crate::js_proxy::proxy_get_property(proxy, key);
887 }
888 }
889
890 let mut current: Option<JSObjectDataPtr> = Some(js_obj.clone());
893 while let Some(cur) = current {
894 let val_opt = get_own_property(&cur, key);
895 if let Some(val) = val_opt {
896 let val_clone = val.borrow().clone();
899 match val_clone {
900 Value::Property { value, getter, .. } => {
901 log::trace!("obj_get_key_value - property descriptor found for key {}", key);
902 if let Some((body, env)) = getter {
903 let getter_env = new_js_object_data();
905 getter_env.borrow_mut().prototype = Some(env);
906 env_set(&getter_env, "this", Value::Object(js_obj.clone()))?;
907 let result = evaluate_statements(&getter_env, &body)?;
908 if let Value::Object(ref obj_ptr) = result {
909 let ptr = Rc::as_ptr(obj_ptr);
910 log::trace!("obj_get_key_value - getter returned object ptr={:p} for key {}", ptr, key);
911 }
912 return Ok(Some(Rc::new(RefCell::new(result))));
913 } else if let Some(val_rc) = value {
914 if let Value::Object(ref obj_ptr) = *val_rc.borrow() {
916 log::trace!("obj_get_key_value - returning object ptr={:p} for key {}", Rc::as_ptr(obj_ptr), key);
917 }
918 return Ok(Some(val_rc));
919 } else {
920 return Ok(Some(Rc::new(RefCell::new(Value::Undefined))));
921 }
922 }
923 Value::Getter(body, env) => {
924 log::trace!("obj_get_key_value - getter found for key {}", key);
925 let getter_env = new_js_object_data();
926 getter_env.borrow_mut().prototype = Some(env);
927 env_set(&getter_env, "this", Value::Object(js_obj.clone()))?;
928 let result = evaluate_statements(&getter_env, &body)?;
929 if let Value::Object(ref obj_ptr) = result {
930 let ptr = Rc::as_ptr(obj_ptr);
931 log::trace!("obj_get_key_value - getter returned object ptr={:p} for key {}", ptr, key);
932 }
933 return Ok(Some(Rc::new(RefCell::new(result))));
934 }
935 _ => {
936 log::trace!("obj_get_key_value - raw value found for key {}", key);
937 if let Value::Object(ref obj_ptr) = *val.borrow() {
938 log::trace!("obj_get_key_value - returning object ptr={:p} for key {}", Rc::as_ptr(obj_ptr), key);
939 }
940 return Ok(Some(val.clone()));
941 }
942 }
943 }
944 current = cur.borrow().prototype.clone();
946 }
947
948 fn make_iterator_closure(next_body: Vec<Statement>, captured_env: JSObjectDataPtr) -> Value {
955 let iter_body = vec![
956 Statement::Let("__i".to_string(), Some(Expr::Value(Value::Number(0.0)))),
957 Statement::Return(Some(Expr::Object(vec![(
958 "next".to_string(),
959 Expr::Function(None, Vec::new(), next_body),
960 )]))),
961 ];
962 Value::Closure(Vec::new(), iter_body, captured_env)
963 }
964
965 if let PropertyKey::Symbol(sym_rc) = key {
967 if let Some(iter_sym_rc) = get_well_known_symbol_rc("iterator")
969 && let (Value::Symbol(iter_sd), Value::Symbol(req_sd)) = (&*iter_sym_rc.borrow(), &*sym_rc.borrow())
970 && Rc::ptr_eq(iter_sd, req_sd)
971 {
972 if is_array(js_obj) {
974 let next_body = vec![
976 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
977 Statement::If(
978 Expr::Binary(
979 Box::new(Expr::Var("idx".to_string())),
980 BinaryOp::LessThan,
981 Box::new(Expr::Property(Box::new(Expr::Var("__array".to_string())), "length".to_string())),
982 ),
983 vec![
984 Statement::Let(
985 "v".to_string(),
986 Some(Expr::Index(
987 Box::new(Expr::Var("__array".to_string())),
988 Box::new(Expr::Var("idx".to_string())),
989 )),
990 ),
991 Statement::Expr(Expr::Assign(
992 Box::new(Expr::Var("__i".to_string())),
993 Box::new(Expr::Binary(
994 Box::new(Expr::Var("idx".to_string())),
995 BinaryOp::Add,
996 Box::new(Expr::Value(Value::Number(1.0))),
997 )),
998 )),
999 Statement::Return(Some(Expr::Object(vec![
1000 ("value".to_string(), Expr::Var("v".to_string())),
1001 ("done".to_string(), Expr::Value(Value::Boolean(false))),
1002 ]))),
1003 ],
1004 Some(vec![Statement::Return(Some(Expr::Object(vec![(
1005 "done".to_string(),
1006 Expr::Value(Value::Boolean(true)),
1007 )])))]),
1008 ),
1009 ];
1010
1011 let captured_env = new_js_object_data();
1012 captured_env.borrow_mut().insert(
1013 PropertyKey::String("__array".to_string()),
1014 Rc::new(RefCell::new(Value::Object(js_obj.clone()))),
1015 );
1016 let closure = make_iterator_closure(next_body, captured_env.clone());
1017 return Ok(Some(Rc::new(RefCell::new(closure))));
1018 }
1019
1020 let map_opt = get_own_property(js_obj, &"__map__".into());
1022 if let Some(map_val) = map_opt {
1023 if let Value::Map(map_rc) = &*map_val.borrow() {
1024 let map_entries = map_rc.borrow().entries.clone();
1025
1026 let next_body = vec![
1028 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
1029 Statement::If(
1030 Expr::Binary(
1031 Box::new(Expr::Var("idx".to_string())),
1032 BinaryOp::LessThan,
1033 Box::new(Expr::Value(Value::Number(map_entries.len() as f64))),
1034 ),
1035 vec![
1036 Statement::Let(
1037 "entry".to_string(),
1038 Some(Expr::Array(vec![
1039 Expr::Property(
1040 Box::new(Expr::Index(
1041 Box::new(Expr::Var("__entries".to_string())),
1042 Box::new(Expr::Var("idx".to_string())),
1043 )),
1044 "0".to_string(),
1045 ),
1046 Expr::Property(
1047 Box::new(Expr::Index(
1048 Box::new(Expr::Var("__entries".to_string())),
1049 Box::new(Expr::Var("idx".to_string())),
1050 )),
1051 "1".to_string(),
1052 ),
1053 ])),
1054 ),
1055 Statement::Expr(Expr::Assign(
1056 Box::new(Expr::Var("__i".to_string())),
1057 Box::new(Expr::Binary(
1058 Box::new(Expr::Var("__i".to_string())),
1059 BinaryOp::Add,
1060 Box::new(Expr::Value(Value::Number(1.0))),
1061 )),
1062 )),
1063 Statement::Return(Some(Expr::Object(vec![
1064 ("value".to_string(), Expr::Var("entry".to_string())),
1065 ("done".to_string(), Expr::Value(Value::Boolean(false))),
1066 ]))),
1067 ],
1068 Some(vec![Statement::Return(Some(Expr::Object(vec![(
1069 "done".to_string(),
1070 Expr::Value(Value::Boolean(true)),
1071 )])))]),
1072 ),
1073 ];
1074
1075 let captured_env = new_js_object_data();
1076 let mut entries_obj = JSObjectData::new();
1078 for (i, (key, value)) in map_entries.iter().enumerate() {
1079 let mut entry_obj = JSObjectData::new();
1080 entry_obj.insert("0".into(), Rc::new(RefCell::new(key.clone())));
1081 entry_obj.insert("1".into(), Rc::new(RefCell::new(value.clone())));
1082 entries_obj.insert(
1083 i.to_string().into(),
1084 Rc::new(RefCell::new(Value::Object(Rc::new(RefCell::new(entry_obj))))),
1085 );
1086 }
1087 captured_env.borrow_mut().insert(
1088 "__entries".into(),
1089 Rc::new(RefCell::new(Value::Object(Rc::new(RefCell::new(entries_obj))))),
1090 );
1091
1092 let closure = make_iterator_closure(next_body, captured_env.clone());
1093 return Ok(Some(Rc::new(RefCell::new(closure))));
1094 }
1095 }
1096
1097 if let Some(val_rc) = get_own_property(js_obj, &"__value__".into()) {
1099 if let Value::String(s) = &*val_rc.borrow() {
1100 let next_body = vec![
1102 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
1103 Statement::If(
1105 Expr::Binary(
1106 Box::new(Expr::Var("idx".to_string())),
1107 BinaryOp::LessThan,
1108 Box::new(Expr::Property(Box::new(Expr::Var("__str".to_string())), "length".to_string())),
1109 ),
1110 vec![
1111 Statement::Let(
1113 "first".to_string(),
1114 Some(Expr::Call(
1115 Box::new(Expr::Property(Box::new(Expr::Var("__str".to_string())), "charCodeAt".to_string())),
1116 vec![Expr::Var("idx".to_string())],
1117 )),
1118 ),
1119 Statement::If(
1121 Expr::LogicalAnd(
1122 Box::new(Expr::Binary(
1123 Box::new(Expr::Var("first".to_string())),
1124 BinaryOp::GreaterEqual,
1125 Box::new(Expr::Value(Value::Number(0xD800 as f64))),
1126 )),
1127 Box::new(Expr::Binary(
1128 Box::new(Expr::Var("first".to_string())),
1129 BinaryOp::LessEqual,
1130 Box::new(Expr::Value(Value::Number(0xDBFF as f64))),
1131 )),
1132 ),
1133 vec![
1134 Statement::Let(
1136 "second".to_string(),
1137 Some(Expr::Call(
1138 Box::new(Expr::Property(
1139 Box::new(Expr::Var("__str".to_string())),
1140 "charCodeAt".to_string(),
1141 )),
1142 vec![Expr::Binary(
1143 Box::new(Expr::Var("idx".to_string())),
1144 BinaryOp::Add,
1145 Box::new(Expr::Value(Value::Number(1.0))),
1146 )],
1147 )),
1148 ),
1149 Statement::If(
1151 Expr::LogicalAnd(
1152 Box::new(Expr::Binary(
1153 Box::new(Expr::Var("second".to_string())),
1154 BinaryOp::GreaterEqual,
1155 Box::new(Expr::Value(Value::Number(0xDC00 as f64))),
1156 )),
1157 Box::new(Expr::Binary(
1158 Box::new(Expr::Var("second".to_string())),
1159 BinaryOp::LessEqual,
1160 Box::new(Expr::Value(Value::Number(0xDFFF as f64))),
1161 )),
1162 ),
1163 vec![
1164 Statement::Let(
1166 "ch".to_string(),
1167 Some(Expr::Call(
1168 Box::new(Expr::Property(
1169 Box::new(Expr::Var("__str".to_string())),
1170 "substring".to_string(),
1171 )),
1172 vec![
1173 Expr::Var("idx".to_string()),
1174 Expr::Binary(
1175 Box::new(Expr::Var("idx".to_string())),
1176 BinaryOp::Add,
1177 Box::new(Expr::Value(Value::Number(2.0))),
1178 ),
1179 ],
1180 )),
1181 ),
1182 Statement::Expr(Expr::Assign(
1184 Box::new(Expr::Var("__i".to_string())),
1185 Box::new(Expr::Binary(
1186 Box::new(Expr::Var("idx".to_string())),
1187 BinaryOp::Add,
1188 Box::new(Expr::Value(Value::Number(2.0))),
1189 )),
1190 )),
1191 Statement::Return(Some(Expr::Object(vec![
1192 ("value".to_string(), Expr::Var("ch".to_string())),
1193 ("done".to_string(), Expr::Value(Value::Boolean(false))),
1194 ]))),
1195 ],
1196 None,
1198 ),
1199 ],
1200 None,
1202 ),
1203 Statement::Let(
1205 "ch".to_string(),
1206 Some(Expr::Call(
1207 Box::new(Expr::Property(Box::new(Expr::Var("__str".to_string())), "charAt".to_string())),
1208 vec![Expr::Var("idx".to_string())],
1209 )),
1210 ),
1211 Statement::Expr(Expr::Assign(
1212 Box::new(Expr::Var("__i".to_string())),
1213 Box::new(Expr::Binary(
1214 Box::new(Expr::Var("idx".to_string())),
1215 BinaryOp::Add,
1216 Box::new(Expr::Value(Value::Number(1.0))),
1217 )),
1218 )),
1219 Statement::Return(Some(Expr::Object(vec![
1220 ("value".to_string(), Expr::Var("ch".to_string())),
1221 ("done".to_string(), Expr::Value(Value::Boolean(false))),
1222 ]))),
1223 ],
1224 Some(vec![Statement::Return(Some(Expr::Object(vec![(
1225 "done".to_string(),
1226 Expr::Value(Value::Boolean(true)),
1227 )])))]),
1228 ),
1229 ];
1230
1231 let captured_env = new_js_object_data();
1232 captured_env.borrow_mut().insert(
1233 PropertyKey::String("__str".to_string()),
1234 Rc::new(RefCell::new(Value::String(s.clone()))),
1235 );
1236 let closure = make_iterator_closure(next_body, captured_env.clone());
1237 return Ok(Some(Rc::new(RefCell::new(closure))));
1238 }
1239 }
1240
1241 let set_opt = get_own_property(js_obj, &"__set__".into());
1243 if let Some(set_val) = set_opt {
1244 if let Value::Set(set_rc) = &*set_val.borrow() {
1245 let set_values = set_rc.borrow().values.clone();
1246 let next_body = vec![
1248 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
1249 Statement::If(
1250 Expr::Binary(
1251 Box::new(Expr::Var("idx".to_string())),
1252 BinaryOp::LessThan,
1253 Box::new(Expr::Value(Value::Number(set_values.len() as f64))),
1254 ),
1255 vec![
1256 Statement::Let(
1257 "value".to_string(),
1258 Some(Expr::Index(
1259 Box::new(Expr::Var("__values".to_string())),
1260 Box::new(Expr::Var("idx".to_string())),
1261 )),
1262 ),
1263 Statement::Expr(Expr::Assign(
1264 Box::new(Expr::Var("__i".to_string())),
1265 Box::new(Expr::Binary(
1266 Box::new(Expr::Var("idx".to_string())),
1267 BinaryOp::Add,
1268 Box::new(Expr::Value(Value::Number(1.0))),
1269 )),
1270 )),
1271 Statement::Return(Some(Expr::Object(vec![
1272 ("value".to_string(), Expr::Var("value".to_string())),
1273 ("done".to_string(), Expr::Value(Value::Boolean(false))),
1274 ]))),
1275 ],
1276 Some(vec![Statement::Return(Some(Expr::Object(vec![(
1277 "done".to_string(),
1278 Expr::Value(Value::Boolean(true)),
1279 )])))]),
1280 ),
1281 ];
1282
1283 let set_iter_body = vec![
1284 Statement::Let("__i".to_string(), Some(Expr::Value(Value::Number(0.0)))),
1285 Statement::Return(Some(Expr::Object(vec![(
1286 "next".to_string(),
1287 Expr::Function(None, Vec::new(), next_body),
1288 )]))),
1289 ];
1290
1291 let captured_env = new_js_object_data();
1292 let mut values_obj = JSObjectData::new();
1294 for (i, value) in set_values.iter().enumerate() {
1295 values_obj.insert(i.to_string().into(), Rc::new(RefCell::new(value.clone())));
1296 }
1297 captured_env.borrow_mut().insert(
1298 "__values".into(),
1299 Rc::new(RefCell::new(Value::Object(Rc::new(RefCell::new(values_obj))))),
1300 );
1301
1302 let closure = Value::Closure(Vec::new(), set_iter_body, captured_env.clone());
1303 return Ok(Some(Rc::new(RefCell::new(closure))));
1304 }
1305 }
1306
1307 let wrapped_opt = get_own_property(js_obj, &"__value__".into());
1309 if let Some(wrapped) = wrapped_opt {
1310 if let Value::String(_) = &*wrapped.borrow() {
1311 let next_body = vec![
1312 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
1313 Statement::If(
1314 Expr::Binary(
1315 Box::new(Expr::Var("idx".to_string())),
1316 BinaryOp::LessThan,
1317 Box::new(Expr::Property(Box::new(Expr::Var("__s".to_string())), "length".to_string())),
1318 ),
1319 vec![
1320 Statement::Let(
1321 "v".to_string(),
1322 Some(Expr::Index(
1323 Box::new(Expr::Var("__s".to_string())),
1324 Box::new(Expr::Var("idx".to_string())),
1325 )),
1326 ),
1327 Statement::Expr(Expr::Assign(
1328 Box::new(Expr::Var("__i".to_string())),
1329 Box::new(Expr::Binary(
1330 Box::new(Expr::Var("idx".to_string())),
1331 BinaryOp::Add,
1332 Box::new(Expr::Value(Value::Number(1.0))),
1333 )),
1334 )),
1335 Statement::Return(Some(Expr::Object(vec![
1336 ("value".to_string(), Expr::Var("v".to_string())),
1337 ("done".to_string(), Expr::Value(Value::Boolean(false))),
1338 ]))),
1339 ],
1340 Some(vec![Statement::Return(Some(Expr::Object(vec![(
1341 "done".to_string(),
1342 Expr::Value(Value::Boolean(true)),
1343 )])))]),
1344 ),
1345 ];
1346
1347 let str_iter_body = vec![
1348 Statement::Let("__i".to_string(), Some(Expr::Value(Value::Number(0.0)))),
1349 Statement::Return(Some(Expr::Object(vec![(
1350 "next".to_string(),
1351 Expr::Function(None, Vec::new(), next_body),
1352 )]))),
1353 ];
1354
1355 let captured_env = new_js_object_data();
1356 captured_env.borrow_mut().insert(
1357 PropertyKey::String("__s".to_string()),
1358 Rc::new(RefCell::new(wrapped.borrow().clone())),
1359 );
1360 let closure = Value::Closure(Vec::new(), str_iter_body, captured_env.clone());
1361 return Ok(Some(Rc::new(RefCell::new(closure))));
1362 }
1363 }
1364 }
1365 if let Some(tag_sym_rc) = get_well_known_symbol_rc("toStringTag")
1366 && let (Value::Symbol(tag_sd), Value::Symbol(req_sd)) = (&*tag_sym_rc.borrow(), &*sym_rc.borrow())
1367 && Rc::ptr_eq(tag_sd, req_sd)
1368 {
1369 if is_array(js_obj) {
1370 return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("Array"))))));
1371 }
1372 let wrapped_opt2 = get_own_property(js_obj, &"__value__".into());
1373 if let Some(wrapped) = wrapped_opt2 {
1374 match &*wrapped.borrow() {
1375 Value::String(_) => return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("String")))))),
1376 Value::Number(_) => return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("Number")))))),
1377 Value::Boolean(_) => return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("Boolean")))))),
1378 _ => {}
1379 }
1380 }
1381 if is_date_object(js_obj) {
1382 return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("Date"))))));
1383 }
1384 }
1385 }
1386
1387 Ok(None)
1388}
1389
1390pub fn obj_set_key_value(js_obj: &JSObjectDataPtr, key: &PropertyKey, val: Value) -> Result<(), JSError> {
1391 let proxy_opt = get_own_property(js_obj, &"__proxy__".into());
1393 if let Some(proxy_val) = proxy_opt {
1394 if let Value::Proxy(proxy) = &*proxy_val.borrow() {
1395 let success = crate::js_proxy::proxy_set_property(proxy, key, val)?;
1396 if !success {
1397 return Err(raise_eval_error!("Proxy set trap returned false"));
1398 }
1399 return Ok(());
1400 }
1401 }
1402
1403 let existing_opt = get_own_property(js_obj, key);
1405 if let Some(existing) = existing_opt {
1406 if !js_obj.borrow().is_writable(key) {
1408 return Err(raise_type_error!(format!("Cannot assign to read-only property '{}'", key)));
1409 }
1410
1411 match existing.borrow().clone() {
1412 Value::Property { value: _, getter, setter } => {
1413 if let Some((param, body, env)) = setter {
1414 let setter_env = new_js_object_data();
1416 setter_env.borrow_mut().prototype = Some(env);
1417 env_set(&setter_env, "this", Value::Object(js_obj.clone()))?;
1418 let name = ¶m[0].0;
1419 env_set(&setter_env, name.as_str(), val)?;
1420 let _v = evaluate_statements(&setter_env, &body)?;
1421 } else {
1422 let value = Some(Rc::new(RefCell::new(val)));
1424 let new_prop = Value::Property { value, getter, setter };
1425 if let PropertyKey::String(s) = key {
1426 if s == "message" {
1428 let dbg_id = get_own_property(js_obj, &"__dbg_ptr__".into())
1430 .map(|r| format!("{:?}", r.borrow()))
1431 .unwrap_or_else(|| "<none>".to_string());
1432 log::debug!(
1433 "DBG obj_set_key_value - inserting 'message' on obj ptr={js_obj:p} dbg_id={dbg_id} value={new_prop:?}"
1434 );
1435 }
1436 }
1437 let val_ptr_info = match &new_prop {
1439 Value::Object(o) => format!("object_ptr={:p}", Rc::as_ptr(o)),
1440 other => format!("value={:?}", other),
1441 };
1442 log::debug!(
1443 "DBG obj_set_key_value - setting existing prop '{}' on obj ptr={:p} -> {}",
1444 key,
1445 Rc::as_ptr(js_obj),
1446 val_ptr_info
1447 );
1448 js_obj.borrow_mut().insert(key.clone(), Rc::new(RefCell::new(new_prop)));
1449 }
1450 return Ok(());
1451 }
1452 Value::Setter(param, body, env) => {
1453 let setter_env = new_js_object_data();
1455 setter_env.borrow_mut().prototype = Some(env);
1456 env_set(&setter_env, "this", Value::Object(js_obj.clone()))?;
1457 let name = ¶m[0].0;
1458 env_set(&setter_env, name.as_str(), val)?;
1459 evaluate_statements(&setter_env, &body)?;
1460 return Ok(());
1461 }
1462 _ => {}
1463 }
1464 }
1465 if let PropertyKey::String(s) = key {
1468 if let Ok(index) = s.parse::<usize>() {
1469 if let Some(length_rc) = get_own_property(js_obj, &"length".into()) {
1470 if let Value::Number(current_len) = &*length_rc.borrow() {
1471 if index >= *current_len as usize {
1472 let new_len = (index + 1) as f64;
1473 obj_set_key_value(js_obj, &"length".into(), Value::Number(new_len))?;
1474 }
1475 }
1476 }
1477 }
1478 }
1479 if let PropertyKey::String(s) = key {
1480 if s == "message" {
1481 let dbg_id = get_own_property(js_obj, &"__dbg_ptr__".into())
1482 .map(|r| format!("{:?}", r.borrow()))
1483 .unwrap_or_else(|| "<none>".to_string());
1484 log::debug!("DBG obj_set_key_value - direct insert 'message' on obj ptr={js_obj:p} dbg_id={dbg_id} value={val:?}");
1485 }
1486 }
1487 let val_ptr_info = match &val {
1489 Value::Object(o) => format!("object_ptr={:p}", Rc::as_ptr(o)),
1490 other => format!("value={:?}", other),
1491 };
1492 log::debug!(
1493 "DBG obj_set_key_value - direct insert prop '{}' on obj ptr={:p} -> {}",
1494 key,
1495 Rc::as_ptr(js_obj),
1496 val_ptr_info
1497 );
1498 js_obj.borrow_mut().insert(key.clone(), Rc::new(RefCell::new(val)));
1499 Ok(())
1500}
1501
1502pub fn obj_set_rc(map: &JSObjectDataPtr, key: &PropertyKey, val_rc: Rc<RefCell<Value>>) {
1503 map.borrow_mut().insert(key.clone(), val_rc);
1504}
1505
1506pub fn obj_delete(map: &JSObjectDataPtr, key: &PropertyKey) -> Result<bool, JSError> {
1507 let proxy_opt = get_own_property(map, &"__proxy__".into());
1509 if let Some(proxy_val) = proxy_opt {
1510 if let Value::Proxy(proxy) = &*proxy_val.borrow() {
1511 return crate::js_proxy::proxy_delete_property(proxy, key);
1512 }
1513 }
1514
1515 if !map.borrow().is_configurable(key) {
1517 return Ok(false);
1518 }
1519
1520 map.borrow_mut().remove(key);
1521 Ok(true)
1522}
1523
1524pub fn env_get<T: AsRef<str>>(env: &JSObjectDataPtr, key: T) -> Option<Rc<RefCell<Value>>> {
1525 get_own_property(env, &key.as_ref().into())
1526}
1527
1528pub fn get_own_property(obj: &JSObjectDataPtr, key: &PropertyKey) -> Option<Rc<RefCell<Value>>> {
1532 obj.borrow().get(key)
1533}
1534
1535pub fn env_set<T: AsRef<str>>(env: &JSObjectDataPtr, key: T, val: Value) -> Result<(), JSError> {
1536 let key = key.as_ref();
1537 if env.borrow().is_const(key) {
1538 return Err(raise_type_error!(format!("Assignment to constant variable '{key}'")));
1539 }
1540 env.borrow_mut()
1541 .insert(PropertyKey::String(key.to_string()), Rc::new(RefCell::new(val)));
1542 Ok(())
1543}
1544
1545pub fn env_set_recursive<T: AsRef<str>>(env: &JSObjectDataPtr, key: T, val: Value) -> Result<(), JSError> {
1546 let key_str = key.as_ref();
1547 let mut current = env.clone();
1548 loop {
1549 if get_own_property(¤t, &key_str.into()).is_some() {
1550 return env_set(¤t, key_str, val);
1551 }
1552 let parent_opt = current.borrow().prototype.clone();
1553 if let Some(parent) = parent_opt {
1554 current = parent;
1555 } else {
1556 return env_set(env, key_str, val);
1558 }
1559 }
1560}
1561
1562pub fn env_set_var(env: &JSObjectDataPtr, key: &str, val: Value) -> Result<(), JSError> {
1563 let mut current = env.clone();
1564 loop {
1565 if current.borrow().is_function_scope {
1566 return env_set(¤t, key, val);
1567 }
1568 let parent_opt = current.borrow().prototype.clone();
1569 if let Some(parent) = parent_opt {
1570 current = parent;
1571 } else {
1572 return env_set(env, key, val);
1574 }
1575 }
1576}
1577
1578pub fn env_set_const(env: &JSObjectDataPtr, key: &str, val: Value) {
1579 let mut env_mut = env.borrow_mut();
1580 env_mut.insert(PropertyKey::String(key.to_string()), Rc::new(RefCell::new(val)));
1581 env_mut.set_const(key.to_string());
1582}