1use std::{cell::RefCell, rc::Rc};
2
3use crate::{
4 JSError,
5 core::{BinaryOp, Expr, PropertyKey, Statement, evaluate_statements, get_well_known_symbol_rc, utf8_to_utf16},
6 js_array::is_array,
7 js_class::ClassDefinition,
8 js_promise::JSPromise,
9 raise_eval_error, raise_type_error,
10};
11
12#[derive(Clone, Debug)]
13pub struct JSMap {
14 pub entries: Vec<(Value, Value)>, }
16
17#[derive(Clone, Debug)]
18pub struct JSSet {
19 pub values: Vec<Value>,
20}
21
22#[derive(Clone, Debug)]
23pub struct JSWeakMap {
24 pub entries: Vec<(std::rc::Weak<RefCell<JSObjectData>>, Value)>, }
26
27#[derive(Clone, Debug)]
28pub struct JSWeakSet {
29 pub values: Vec<std::rc::Weak<RefCell<JSObjectData>>>, }
31
32#[derive(Clone, Debug)]
33pub struct JSGenerator {
34 pub params: Vec<String>,
35 pub body: Vec<Statement>,
36 pub env: JSObjectDataPtr, pub state: GeneratorState,
38}
39
40#[derive(Clone, Debug)]
41pub struct JSProxy {
42 pub target: Value, pub handler: Value, pub revoked: bool, }
46
47#[derive(Clone, Debug)]
48pub struct JSArrayBuffer {
49 pub data: Vec<u8>, pub detached: bool, }
52
53#[derive(Clone, Debug)]
54pub struct JSDataView {
55 pub buffer: Rc<RefCell<JSArrayBuffer>>, pub byte_offset: usize, pub byte_length: usize, }
59
60#[derive(Clone, Debug, PartialEq)]
61pub enum TypedArrayKind {
62 Int8,
63 Uint8,
64 Uint8Clamped,
65 Int16,
66 Uint16,
67 Int32,
68 Uint32,
69 Float32,
70 Float64,
71 BigInt64,
72 BigUint64,
73}
74
75#[derive(Clone, Debug)]
76pub struct JSTypedArray {
77 pub kind: TypedArrayKind,
78 pub buffer: Rc<RefCell<JSArrayBuffer>>, pub byte_offset: usize, pub length: usize, }
82
83impl JSTypedArray {
84 pub fn element_size(&self) -> usize {
86 match self.kind {
87 TypedArrayKind::Int8 | TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => 1,
88 TypedArrayKind::Int16 | TypedArrayKind::Uint16 => 2,
89 TypedArrayKind::Int32 | TypedArrayKind::Uint32 | TypedArrayKind::Float32 => 4,
90 TypedArrayKind::Float64 | TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => 8,
91 }
92 }
93
94 pub fn get(&self, index: usize) -> Result<i64, JSError> {
96 if index >= self.length {
97 return Err(raise_type_error!("Index out of bounds"));
98 }
99
100 let buffer = self.buffer.borrow();
101 if buffer.detached {
102 return Err(raise_type_error!("ArrayBuffer is detached"));
103 }
104
105 let byte_index = self.byte_offset + index * self.element_size();
106 if byte_index + self.element_size() > buffer.data.len() {
107 return Err(raise_type_error!("Index out of bounds"));
108 }
109
110 match self.kind {
111 TypedArrayKind::Int8 => Ok(buffer.data[byte_index] as i8 as i64),
112 TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => Ok(buffer.data[byte_index] as i64),
113 TypedArrayKind::Int16 => {
114 let bytes = &buffer.data[byte_index..byte_index + 2];
115 Ok(i16::from_le_bytes([bytes[0], bytes[1]]) as i64)
116 }
117 TypedArrayKind::Uint16 => {
118 let bytes = &buffer.data[byte_index..byte_index + 2];
119 Ok(u16::from_le_bytes([bytes[0], bytes[1]]) as i64)
120 }
121 TypedArrayKind::Int32 => {
122 let bytes = &buffer.data[byte_index..byte_index + 4];
123 Ok(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64)
124 }
125 TypedArrayKind::Uint32 => {
126 let bytes = &buffer.data[byte_index..byte_index + 4];
127 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64)
128 }
129 TypedArrayKind::Float32 => {
130 let bytes = &buffer.data[byte_index..byte_index + 4];
131 let float_val = f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
132 Ok(float_val as i64) }
134 TypedArrayKind::Float64 => {
135 let bytes = &buffer.data[byte_index..byte_index + 8];
136 let float_val = f64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]);
137 Ok(float_val as i64) }
139 TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => {
140 Ok(0)
142 }
143 }
144 }
145
146 pub fn set(&mut self, index: usize, value: i64) -> Result<(), JSError> {
148 if index >= self.length {
149 return Err(raise_type_error!("Index out of bounds"));
150 }
151
152 let mut buffer = self.buffer.borrow_mut();
153 if buffer.detached {
154 return Err(raise_type_error!("ArrayBuffer is detached"));
155 }
156
157 let byte_index = self.byte_offset + index * self.element_size();
158 if byte_index + self.element_size() > buffer.data.len() {
159 return Err(raise_type_error!("Index out of bounds"));
160 }
161
162 match self.kind {
163 TypedArrayKind::Int8 => {
164 buffer.data[byte_index] = value as i8 as u8;
165 }
166 TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => {
167 buffer.data[byte_index] = value as u8;
168 }
169 TypedArrayKind::Int16 => {
170 let bytes = (value as i16).to_le_bytes();
171 buffer.data[byte_index..byte_index + 2].copy_from_slice(&bytes);
172 }
173 TypedArrayKind::Uint16 => {
174 let bytes = (value as u16).to_le_bytes();
175 buffer.data[byte_index..byte_index + 2].copy_from_slice(&bytes);
176 }
177 TypedArrayKind::Int32 => {
178 let bytes = (value as i32).to_le_bytes();
179 buffer.data[byte_index..byte_index + 4].copy_from_slice(&bytes);
180 }
181 TypedArrayKind::Uint32 => {
182 let bytes = (value as u32).to_le_bytes();
183 buffer.data[byte_index..byte_index + 4].copy_from_slice(&bytes);
184 }
185 TypedArrayKind::Float32 => {
186 let bytes = (value as f32).to_le_bytes();
187 buffer.data[byte_index..byte_index + 4].copy_from_slice(&bytes);
188 }
189 TypedArrayKind::Float64 => {
190 let bytes = (value as f64).to_le_bytes();
191 buffer.data[byte_index..byte_index + 8].copy_from_slice(&bytes);
192 }
193 TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => {
194 }
196 }
197
198 Ok(())
199 }
200}
201
202impl JSDataView {
203 pub fn get_int8(&self, offset: usize) -> Result<i8, JSError> {
205 self.check_bounds(offset, 1)?;
206 let buffer = self.buffer.borrow();
207 Ok(buffer.data[self.byte_offset + offset] as i8)
208 }
209
210 pub fn get_uint8(&self, offset: usize) -> Result<u8, JSError> {
212 self.check_bounds(offset, 1)?;
213 let buffer = self.buffer.borrow();
214 Ok(buffer.data[self.byte_offset + offset])
215 }
216
217 pub fn get_int16(&self, offset: usize, little_endian: bool) -> Result<i16, JSError> {
219 self.check_bounds(offset, 2)?;
220 let buffer = self.buffer.borrow();
221 let bytes = &buffer.data[self.byte_offset + offset..self.byte_offset + offset + 2];
222 if little_endian {
223 Ok(i16::from_le_bytes([bytes[0], bytes[1]]))
224 } else {
225 Ok(i16::from_be_bytes([bytes[0], bytes[1]]))
226 }
227 }
228
229 pub fn get_uint16(&self, offset: usize, little_endian: bool) -> Result<u16, JSError> {
231 self.check_bounds(offset, 2)?;
232 let buffer = self.buffer.borrow();
233 let bytes = &buffer.data[self.byte_offset + offset..self.byte_offset + offset + 2];
234 if little_endian {
235 Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
236 } else {
237 Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
238 }
239 }
240
241 pub fn get_int32(&self, offset: usize, little_endian: bool) -> Result<i32, JSError> {
243 self.check_bounds(offset, 4)?;
244 let buffer = self.buffer.borrow();
245 let bytes = &buffer.data[self.byte_offset + offset..self.byte_offset + offset + 4];
246 if little_endian {
247 Ok(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
248 } else {
249 Ok(i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
250 }
251 }
252
253 pub fn get_uint32(&self, offset: usize, little_endian: bool) -> Result<u32, JSError> {
255 self.check_bounds(offset, 4)?;
256 let buffer = self.buffer.borrow();
257 let bytes = &buffer.data[self.byte_offset + offset..self.byte_offset + offset + 4];
258 if little_endian {
259 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
260 } else {
261 Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
262 }
263 }
264
265 pub fn get_float32(&self, offset: usize, little_endian: bool) -> Result<f32, JSError> {
267 self.check_bounds(offset, 4)?;
268 let buffer = self.buffer.borrow();
269 let bytes = &buffer.data[self.byte_offset + offset..self.byte_offset + offset + 4];
270 if little_endian {
271 Ok(f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
272 } else {
273 Ok(f32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
274 }
275 }
276
277 pub fn get_float64(&self, offset: usize, little_endian: bool) -> Result<f64, JSError> {
279 self.check_bounds(offset, 8)?;
280 let buffer = self.buffer.borrow();
281 let bytes = &buffer.data[self.byte_offset + offset..self.byte_offset + offset + 8];
282 if little_endian {
283 Ok(f64::from_le_bytes([
284 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
285 ]))
286 } else {
287 Ok(f64::from_be_bytes([
288 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
289 ]))
290 }
291 }
292
293 pub fn get_big_int64(&self, offset: usize, little_endian: bool) -> Result<i64, JSError> {
295 self.check_bounds(offset, 8)?;
296 let buffer = self.buffer.borrow();
297 let bytes = &buffer.data[self.byte_offset + offset..self.byte_offset + offset + 8];
298 if little_endian {
299 Ok(i64::from_le_bytes([
300 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
301 ]))
302 } else {
303 Ok(i64::from_be_bytes([
304 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
305 ]))
306 }
307 }
308
309 pub fn get_big_uint64(&self, offset: usize, little_endian: bool) -> Result<u64, JSError> {
311 self.check_bounds(offset, 8)?;
312 let buffer = self.buffer.borrow();
313 let bytes = &buffer.data[self.byte_offset + offset..self.byte_offset + offset + 8];
314 if little_endian {
315 Ok(u64::from_le_bytes([
316 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
317 ]))
318 } else {
319 Ok(u64::from_be_bytes([
320 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
321 ]))
322 }
323 }
324
325 pub fn set_int8(&mut self, offset: usize, value: i8) -> Result<(), JSError> {
327 self.check_bounds(offset, 1)?;
328 let mut buffer = self.buffer.borrow_mut();
329 buffer.data[self.byte_offset + offset] = value as u8;
330 Ok(())
331 }
332
333 pub fn set_uint8(&mut self, offset: usize, value: u8) -> Result<(), JSError> {
335 self.check_bounds(offset, 1)?;
336 let mut buffer = self.buffer.borrow_mut();
337 buffer.data[self.byte_offset + offset] = value;
338 Ok(())
339 }
340
341 pub fn set_int16(&mut self, offset: usize, value: i16, little_endian: bool) -> Result<(), JSError> {
343 self.check_bounds(offset, 2)?;
344 let mut buffer = self.buffer.borrow_mut();
345 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
346 buffer.data[self.byte_offset + offset..self.byte_offset + offset + 2].copy_from_slice(&bytes);
347 Ok(())
348 }
349
350 pub fn set_uint16(&mut self, offset: usize, value: u16, little_endian: bool) -> Result<(), JSError> {
352 self.check_bounds(offset, 2)?;
353 let mut buffer = self.buffer.borrow_mut();
354 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
355 buffer.data[self.byte_offset + offset..self.byte_offset + offset + 2].copy_from_slice(&bytes);
356 Ok(())
357 }
358
359 pub fn set_int32(&mut self, offset: usize, value: i32, little_endian: bool) -> Result<(), JSError> {
361 self.check_bounds(offset, 4)?;
362 let mut buffer = self.buffer.borrow_mut();
363 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
364 buffer.data[self.byte_offset + offset..self.byte_offset + offset + 4].copy_from_slice(&bytes);
365 Ok(())
366 }
367
368 pub fn set_uint32(&mut self, offset: usize, value: u32, little_endian: bool) -> Result<(), JSError> {
370 self.check_bounds(offset, 4)?;
371 let mut buffer = self.buffer.borrow_mut();
372 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
373 buffer.data[self.byte_offset + offset..self.byte_offset + offset + 4].copy_from_slice(&bytes);
374 Ok(())
375 }
376
377 pub fn set_float32(&mut self, offset: usize, value: f32, little_endian: bool) -> Result<(), JSError> {
379 self.check_bounds(offset, 4)?;
380 let mut buffer = self.buffer.borrow_mut();
381 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
382 buffer.data[self.byte_offset + offset..self.byte_offset + offset + 4].copy_from_slice(&bytes);
383 Ok(())
384 }
385
386 pub fn set_float64(&mut self, offset: usize, value: f64, little_endian: bool) -> Result<(), JSError> {
388 self.check_bounds(offset, 8)?;
389 let mut buffer = self.buffer.borrow_mut();
390 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
391 buffer.data[self.byte_offset + offset..self.byte_offset + offset + 8].copy_from_slice(&bytes);
392 Ok(())
393 }
394
395 pub fn set_big_int64(&mut self, offset: usize, value: i64, little_endian: bool) -> Result<(), JSError> {
397 self.check_bounds(offset, 8)?;
398 let mut buffer = self.buffer.borrow_mut();
399 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
400 buffer.data[self.byte_offset + offset..self.byte_offset + offset + 8].copy_from_slice(&bytes);
401 Ok(())
402 }
403
404 pub fn set_big_uint64(&mut self, offset: usize, value: u64, little_endian: bool) -> Result<(), JSError> {
406 self.check_bounds(offset, 8)?;
407 let mut buffer = self.buffer.borrow_mut();
408 let bytes = if little_endian { value.to_le_bytes() } else { value.to_be_bytes() };
409 buffer.data[self.byte_offset + offset..self.byte_offset + offset + 8].copy_from_slice(&bytes);
410 Ok(())
411 }
412
413 fn check_bounds(&self, offset: usize, size: usize) -> Result<(), JSError> {
415 let buffer = self.buffer.borrow();
416 if buffer.detached {
417 return Err(raise_type_error!("ArrayBuffer is detached"));
418 }
419 if offset + size > self.byte_length {
420 return Err(raise_type_error!("Offset out of bounds"));
421 }
422 Ok(())
423 }
424}
425
426#[derive(Clone, Debug)]
427pub enum GeneratorState {
428 NotStarted,
429 Running { pc: usize, stack: Vec<Value> }, Suspended { pc: usize, stack: Vec<Value> }, Completed,
432}
433
434pub type JSObjectDataPtr = Rc<RefCell<JSObjectData>>;
435
436#[derive(Clone, Default)]
437pub struct JSObjectData {
438 pub properties: std::collections::HashMap<PropertyKey, Rc<RefCell<Value>>>,
439 pub constants: std::collections::HashSet<String>,
440 pub prototype: Option<Rc<RefCell<JSObjectData>>>,
441 pub is_function_scope: bool,
442}
443
444impl std::fmt::Debug for JSObjectData {
445 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446 write!(
447 f,
448 "JSObjectData {{ properties: {}, constants: {}, prototype: {}, is_function_scope: {} }}",
449 self.properties.len(),
450 self.constants.len(),
451 self.prototype.is_some(),
452 self.is_function_scope
453 )
454 }
455}
456
457impl JSObjectData {
458 pub fn new() -> Self {
459 JSObjectData::default()
460 }
461
462 pub fn insert(&mut self, key: PropertyKey, val: Rc<RefCell<Value>>) {
463 self.properties.insert(key, val);
464 }
465
466 pub fn get(&self, key: &PropertyKey) -> Option<Rc<RefCell<Value>>> {
467 self.properties.get(key).cloned()
468 }
469
470 pub fn contains_key(&self, key: &PropertyKey) -> bool {
471 self.properties.contains_key(key)
472 }
473
474 pub fn remove(&mut self, key: &PropertyKey) -> Option<Rc<RefCell<Value>>> {
475 self.properties.remove(key)
476 }
477
478 pub fn keys(&self) -> std::collections::hash_map::Keys<'_, PropertyKey, Rc<RefCell<Value>>> {
479 self.properties.keys()
480 }
481
482 pub fn is_const(&self, key: &str) -> bool {
483 self.constants.contains(key)
484 }
485
486 pub fn set_const(&mut self, key: String) {
487 self.constants.insert(key);
488 }
489}
490
491#[derive(Clone, Debug)]
492pub struct SymbolData {
493 pub description: Option<String>,
494}
495
496#[derive(Clone)]
497pub enum Value {
498 Number(f64),
499 BigInt(String),
501 String(Vec<u16>), Boolean(bool),
503 Undefined,
504 Object(JSObjectDataPtr), Function(String), Closure(Vec<String>, Vec<Statement>, JSObjectDataPtr), AsyncClosure(Vec<String>, Vec<Statement>, JSObjectDataPtr), GeneratorFunction(Vec<String>, Vec<Statement>, JSObjectDataPtr), ClassDefinition(Rc<ClassDefinition>), Getter(Vec<Statement>, JSObjectDataPtr), Setter(Vec<String>, Vec<Statement>, JSObjectDataPtr), Property {
513 value: Option<Rc<RefCell<Value>>>,
515 getter: Option<(Vec<Statement>, JSObjectDataPtr)>,
516 setter: Option<(Vec<String>, Vec<Statement>, JSObjectDataPtr)>,
517 },
518 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>>), }
530
531impl std::fmt::Debug for Value {
532 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
533 match self {
534 Value::Number(n) => write!(f, "Number({})", n),
535 Value::BigInt(s) => write!(f, "BigInt({})", s),
536 Value::String(s) => write!(f, "String({})", String::from_utf16_lossy(s)),
537 Value::Boolean(b) => write!(f, "Boolean({})", b),
538 Value::Undefined => write!(f, "Undefined"),
539 Value::Object(obj) => write!(f, "Object({:p})", Rc::as_ptr(obj)),
540 Value::Function(name) => write!(f, "Function({})", name),
541 Value::Closure(_, _, _) => write!(f, "Closure"),
542 Value::AsyncClosure(_, _, _) => write!(f, "AsyncClosure"),
543 Value::GeneratorFunction(_, _, _) => write!(f, "GeneratorFunction"),
544 Value::ClassDefinition(_) => write!(f, "ClassDefinition"),
545 Value::Getter(_, _) => write!(f, "Getter"),
546 Value::Setter(_, _, _) => write!(f, "Setter"),
547 Value::Property { .. } => write!(f, "Property"),
548 Value::Promise(p) => write!(f, "Promise({:p})", Rc::as_ptr(p)),
549 Value::Symbol(_) => write!(f, "Symbol"),
550 Value::Map(m) => write!(f, "Map({:p})", Rc::as_ptr(m)),
551 Value::Set(s) => write!(f, "Set({:p})", Rc::as_ptr(s)),
552 Value::WeakMap(wm) => write!(f, "WeakMap({:p})", Rc::as_ptr(wm)),
553 Value::WeakSet(ws) => write!(f, "WeakSet({:p})", Rc::as_ptr(ws)),
554 Value::Generator(g) => write!(f, "Generator({:p})", Rc::as_ptr(g)),
555 Value::Proxy(p) => write!(f, "Proxy({:p})", Rc::as_ptr(p)),
556 Value::ArrayBuffer(ab) => write!(f, "ArrayBuffer({:p})", Rc::as_ptr(ab)),
557 Value::DataView(dv) => write!(f, "DataView({:p})", Rc::as_ptr(dv)),
558 Value::TypedArray(ta) => write!(f, "TypedArray({:p})", Rc::as_ptr(ta)),
559 }
560 }
561}
562
563pub fn is_truthy(val: &Value) -> bool {
564 match val {
565 Value::BigInt(s) => {
566 let s_no_n = if s.ends_with('n') { &s[..s.len() - 1] } else { s.as_str() };
568 #[allow(clippy::if_same_then_else)]
569 let s_no_prefix = if s_no_n.starts_with("0x") || s_no_n.starts_with("0X") {
570 &s_no_n[2..]
571 } else if s_no_n.starts_with("0b") || s_no_n.starts_with("0B") {
572 &s_no_n[2..]
573 } else if s_no_n.starts_with("0o") || s_no_n.starts_with("0O") {
574 &s_no_n[2..]
575 } else {
576 s_no_n
577 };
578 !s_no_prefix.chars().all(|c| c == '0')
579 }
580 Value::Number(n) => *n != 0.0 && !n.is_nan(),
581 Value::String(s) => !s.is_empty(),
582 Value::Boolean(b) => *b,
583 Value::Undefined => false,
584 Value::Object(_) => true,
585 Value::Function(_) => true,
586 Value::Closure(_, _, _) => true,
587 Value::AsyncClosure(_, _, _) => true,
588 Value::GeneratorFunction(_, _, _) => true,
589 Value::ClassDefinition(_) => true,
590 Value::Getter(_, _) => true,
591 Value::Setter(_, _, _) => true,
592 Value::Property { .. } => true,
593 Value::Promise(_) => true,
594 Value::Symbol(_) => true,
595 Value::Map(_) => true,
596 Value::Set(_) => true,
597 Value::WeakMap(_) => true,
598 Value::WeakSet(_) => true,
599 Value::Generator(_) => true,
600 Value::Proxy(_) => true,
601 Value::ArrayBuffer(_) => true,
602 Value::DataView(_) => true,
603 Value::TypedArray(_) => true,
604 }
605}
606
607pub fn values_equal(a: &Value, b: &Value) -> bool {
609 match (a, b) {
610 (Value::BigInt(sa), Value::BigInt(sb)) => sa == sb,
611 (Value::Number(na), Value::Number(nb)) => na == nb,
612 (Value::String(sa), Value::String(sb)) => sa == sb,
613 (Value::Boolean(ba), Value::Boolean(bb)) => ba == bb,
614 (Value::Undefined, Value::Undefined) => true,
615 (Value::Object(_), Value::Object(_)) => false, (Value::Symbol(sa), Value::Symbol(sb)) => Rc::ptr_eq(sa, sb), _ => false, }
619}
620
621pub fn value_to_string(val: &Value) -> String {
623 match val {
624 Value::Number(n) => n.to_string(),
625 Value::BigInt(s) => s.clone(),
626 Value::String(s) => String::from_utf16_lossy(s),
627 Value::Boolean(b) => b.to_string(),
628 Value::Undefined => "undefined".to_string(),
629 Value::Object(_) => "[object Object]".to_string(),
630 Value::Function(name) => format!("function {}", name),
631 Value::Closure(_, _, _) => "function".to_string(),
632 Value::AsyncClosure(_, _, _) => "function".to_string(),
633 Value::GeneratorFunction(_, _, _) => "function".to_string(),
634 Value::ClassDefinition(_) => "class".to_string(),
635 Value::Getter(_, _) => "getter".to_string(),
636 Value::Setter(_, _, _) => "setter".to_string(),
637 Value::Property { .. } => "[property]".to_string(),
638 Value::Promise(_) => "[object Promise]".to_string(),
639 Value::Symbol(desc) => match desc.description.as_ref() {
640 Some(d) => format!("Symbol({})", d),
641 None => "Symbol()".to_string(),
642 },
643 Value::Map(_) => "[object Map]".to_string(),
644 Value::Set(_) => "[object Set]".to_string(),
645 Value::WeakMap(_) => "[object WeakMap]".to_string(),
646 Value::WeakSet(_) => "[object WeakSet]".to_string(),
647 Value::Generator(_) => "[object Generator]".to_string(),
648 Value::Proxy(_) => "[object Proxy]".to_string(),
649 Value::ArrayBuffer(_) => "[object ArrayBuffer]".to_string(),
650 Value::DataView(_) => "[object DataView]".to_string(),
651 Value::TypedArray(_) => "[object TypedArray]".to_string(),
652 }
653}
654
655pub fn to_primitive(val: &Value, hint: &str) -> Result<Value, JSError> {
657 match val {
658 Value::Number(_) | Value::String(_) | Value::Boolean(_) | Value::Undefined | Value::Symbol(_) => Ok(val.clone()),
659 Value::Object(obj_map) => {
660 if let Some(tp_sym) = get_well_known_symbol_rc("toPrimitive") {
662 let key = PropertyKey::Symbol(tp_sym.clone());
663 if let Some(method_rc) = obj_get_value(obj_map, &key)? {
664 let method_val = method_rc.borrow().clone();
665 match method_val {
666 Value::Closure(params, body, captured_env) | Value::AsyncClosure(params, body, captured_env) => {
667 let func_env = Rc::new(RefCell::new(JSObjectData::new()));
669 func_env.borrow_mut().prototype = Some(captured_env.clone());
670 env_set(&func_env, "this", Value::Object(obj_map.clone()))?;
671 if !params.is_empty() {
673 env_set(&func_env, ¶ms[0], Value::String(utf8_to_utf16(hint)))?;
674 }
675 let result = evaluate_statements(&func_env, &body)?;
676 match result {
677 Value::Number(_) | Value::String(_) | Value::Boolean(_) | Value::Symbol(_) => return Ok(result),
678 _ => {
679 return Err(raise_type_error!("[Symbol.toPrimitive] must return a primitive"));
680 }
681 }
682 }
683 _ => {
684 }
686 }
687 }
688 }
689
690 if hint == "string" {
692 let to_s = crate::js_object::handle_to_string_method(&Value::Object(obj_map.clone()), &[])?;
694 if matches!(to_s, Value::String(_) | Value::Number(_) | Value::Boolean(_)) {
695 return Ok(to_s);
696 }
697 let val_of = crate::js_object::handle_value_of_method(&Value::Object(obj_map.clone()), &[])?;
698 if matches!(val_of, Value::String(_) | Value::Number(_) | Value::Boolean(_)) {
699 return Ok(val_of);
700 }
701 } else {
702 let val_of = crate::js_object::handle_value_of_method(&Value::Object(obj_map.clone()), &[])?;
704 if matches!(val_of, Value::Number(_) | Value::String(_) | Value::Boolean(_)) {
705 return Ok(val_of);
706 }
707 let to_s = crate::js_object::handle_to_string_method(&Value::Object(obj_map.clone()), &[])?;
708 if matches!(to_s, Value::String(_) | Value::Number(_) | Value::Boolean(_)) {
709 return Ok(to_s);
710 }
711 }
712
713 Err(raise_type_error!("Cannot convert object to primitive"))
714 }
715 _ => Ok(val.clone()),
716 }
717}
718
719pub fn value_to_sort_string(val: &Value) -> String {
721 match val {
722 Value::Number(n) => {
723 if n.is_nan() {
724 "NaN".to_string()
725 } else if *n == f64::INFINITY {
726 "Infinity".to_string()
727 } else if *n == f64::NEG_INFINITY {
728 "-Infinity".to_string()
729 } else {
730 n.to_string()
731 }
732 }
733 Value::BigInt(s) => s.clone(),
734 Value::String(s) => String::from_utf16_lossy(s),
735 Value::Boolean(b) => b.to_string(),
736 Value::Undefined => "undefined".to_string(),
737 Value::Object(_) => "[object Object]".to_string(),
738 Value::Function(name) => format!("[function {}]", name),
739 Value::Closure(_, _, _) | Value::AsyncClosure(_, _, _) | Value::GeneratorFunction(_, _, _) => "[function]".to_string(),
740 Value::ClassDefinition(_) => "[class]".to_string(),
741 Value::Getter(_, _) => "[getter]".to_string(),
742 Value::Setter(_, _, _) => "[setter]".to_string(),
743 Value::Property { .. } => "[property]".to_string(),
744 Value::Promise(_) => "[object Promise]".to_string(),
745 Value::Symbol(_) => "[object Symbol]".to_string(),
746 Value::Map(_) => "[object Map]".to_string(),
747 Value::Set(_) => "[object Set]".to_string(),
748 Value::WeakMap(_) => "[object WeakMap]".to_string(),
749 Value::WeakSet(_) => "[object WeakSet]".to_string(),
750 Value::Generator(_) => "[object Generator]".to_string(),
751 Value::Proxy(_) => "[object Proxy]".to_string(),
752 Value::ArrayBuffer(_) => "[object ArrayBuffer]".to_string(),
753 Value::DataView(_) => "[object DataView]".to_string(),
754 Value::TypedArray(_) => "[object TypedArray]".to_string(),
755 }
756}
757
758pub fn obj_get_value(js_obj: &JSObjectDataPtr, key: &PropertyKey) -> Result<Option<Rc<RefCell<Value>>>, JSError> {
760 if let Some(proxy_val) = js_obj.borrow().get(&"__proxy__".into())
762 && let Value::Proxy(proxy) = &*proxy_val.borrow()
763 {
764 return crate::js_proxy::proxy_get_property(proxy, key);
765 }
766
767 let mut current: Option<JSObjectDataPtr> = Some(js_obj.clone());
770 while let Some(cur) = current {
771 if let Some(val) = cur.borrow().get(key) {
772 let val_clone = val.borrow().clone();
775 match val_clone {
776 Value::Property { value, getter, .. } => {
777 log::trace!("obj_get_value - property descriptor found for key {}", key);
778 if let Some((body, env)) = getter {
779 let getter_env = Rc::new(RefCell::new(JSObjectData::new()));
781 getter_env.borrow_mut().prototype = Some(env);
782 env_set(&getter_env, "this", Value::Object(js_obj.clone()))?;
783 let result = evaluate_statements(&getter_env, &body)?;
784 return Ok(Some(Rc::new(RefCell::new(result))));
785 } else if let Some(val_rc) = value {
786 return Ok(Some(val_rc));
787 } else {
788 return Ok(Some(Rc::new(RefCell::new(Value::Undefined))));
789 }
790 }
791 Value::Getter(body, env) => {
792 log::trace!("obj_get_value - getter found for key {}", key);
793 let getter_env = Rc::new(RefCell::new(JSObjectData::new()));
794 getter_env.borrow_mut().prototype = Some(env);
795 env_set(&getter_env, "this", Value::Object(js_obj.clone()))?;
796 let result = evaluate_statements(&getter_env, &body)?;
797 return Ok(Some(Rc::new(RefCell::new(result))));
798 }
799 _ => {
800 log::trace!("obj_get_value - raw value found for key {}", key);
801 return Ok(Some(val.clone()));
802 }
803 }
804 }
805 current = cur.borrow().prototype.clone();
807 }
808
809 if let PropertyKey::Symbol(sym_rc) = key {
814 if let Some(iter_sym_rc) = get_well_known_symbol_rc("iterator")
816 && let (Value::Symbol(iter_sd), Value::Symbol(req_sd)) = (&*iter_sym_rc.borrow(), &*sym_rc.borrow())
817 && Rc::ptr_eq(iter_sd, req_sd)
818 {
819 if is_array(js_obj) {
821 let next_body = vec![
823 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
824 Statement::If(
825 Expr::Binary(
826 Box::new(Expr::Var("idx".to_string())),
827 BinaryOp::LessThan,
828 Box::new(Expr::Property(Box::new(Expr::Var("__array".to_string())), "length".to_string())),
829 ),
830 vec![
831 Statement::Let(
832 "v".to_string(),
833 Some(Expr::Index(
834 Box::new(Expr::Var("__array".to_string())),
835 Box::new(Expr::Var("idx".to_string())),
836 )),
837 ),
838 Statement::Expr(Expr::Assign(
839 Box::new(Expr::Var("__i".to_string())),
840 Box::new(Expr::Binary(
841 Box::new(Expr::Var("idx".to_string())),
842 BinaryOp::Add,
843 Box::new(Expr::Value(Value::Number(1.0))),
844 )),
845 )),
846 Statement::Return(Some(Expr::Object(vec![
847 ("value".to_string(), Expr::Var("v".to_string())),
848 ("done".to_string(), Expr::Value(Value::Boolean(false))),
849 ]))),
850 ],
851 Some(vec![Statement::Return(Some(Expr::Object(vec![(
852 "done".to_string(),
853 Expr::Value(Value::Boolean(true)),
854 )])))]),
855 ),
856 ];
857
858 let arr_iter_body = vec![
859 Statement::Let("__i".to_string(), Some(Expr::Value(Value::Number(0.0)))),
860 Statement::Return(Some(Expr::Object(vec![(
861 "next".to_string(),
862 Expr::Function(Vec::new(), next_body),
863 )]))),
864 ];
865
866 let captured_env = Rc::new(RefCell::new(JSObjectData::new()));
867 captured_env.borrow_mut().insert(
868 PropertyKey::String("__array".to_string()),
869 Rc::new(RefCell::new(Value::Object(js_obj.clone()))),
870 );
871 let closure = Value::Closure(Vec::new(), arr_iter_body, captured_env.clone());
872 return Ok(Some(Rc::new(RefCell::new(closure))));
873 }
874
875 if let Some(map_val) = js_obj.borrow().get(&"__map__".into())
877 && let Value::Map(map_rc) = &*map_val.borrow()
878 {
879 let map_entries = map_rc.borrow().entries.clone();
880
881 let next_body = vec![
883 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
884 Statement::If(
885 Expr::Binary(
886 Box::new(Expr::Var("idx".to_string())),
887 BinaryOp::LessThan,
888 Box::new(Expr::Value(Value::Number(map_entries.len() as f64))),
889 ),
890 vec![
891 Statement::Let(
892 "entry".to_string(),
893 Some(Expr::Array(vec![
894 Expr::Property(
895 Box::new(Expr::Index(
896 Box::new(Expr::Var("__entries".to_string())),
897 Box::new(Expr::Var("idx".to_string())),
898 )),
899 "0".to_string(),
900 ),
901 Expr::Property(
902 Box::new(Expr::Index(
903 Box::new(Expr::Var("__entries".to_string())),
904 Box::new(Expr::Var("idx".to_string())),
905 )),
906 "1".to_string(),
907 ),
908 ])),
909 ),
910 Statement::Expr(Expr::Assign(
911 Box::new(Expr::Var("__i".to_string())),
912 Box::new(Expr::Binary(
913 Box::new(Expr::Var("__i".to_string())),
914 BinaryOp::Add,
915 Box::new(Expr::Value(Value::Number(1.0))),
916 )),
917 )),
918 Statement::Return(Some(Expr::Object(vec![
919 ("value".to_string(), Expr::Var("entry".to_string())),
920 ("done".to_string(), Expr::Value(Value::Boolean(false))),
921 ]))),
922 ],
923 Some(vec![Statement::Return(Some(Expr::Object(vec![(
924 "done".to_string(),
925 Expr::Value(Value::Boolean(true)),
926 )])))]),
927 ),
928 ];
929
930 let map_iter_body = vec![
931 Statement::Let("__i".to_string(), Some(Expr::Value(Value::Number(0.0)))),
932 Statement::Return(Some(Expr::Object(vec![(
933 "next".to_string(),
934 Expr::Function(Vec::new(), next_body),
935 )]))),
936 ];
937
938 let captured_env = Rc::new(RefCell::new(JSObjectData::new()));
939 let mut entries_obj = JSObjectData::new();
941 for (i, (key, value)) in map_entries.iter().enumerate() {
942 let mut entry_obj = JSObjectData::new();
943 entry_obj.insert("0".into(), Rc::new(RefCell::new(key.clone())));
944 entry_obj.insert("1".into(), Rc::new(RefCell::new(value.clone())));
945 entries_obj.insert(
946 i.to_string().into(),
947 Rc::new(RefCell::new(Value::Object(Rc::new(RefCell::new(entry_obj))))),
948 );
949 }
950 captured_env.borrow_mut().insert(
951 "__entries".into(),
952 Rc::new(RefCell::new(Value::Object(Rc::new(RefCell::new(entries_obj))))),
953 );
954
955 let closure = Value::Closure(Vec::new(), map_iter_body, captured_env.clone());
956 return Ok(Some(Rc::new(RefCell::new(closure))));
957 }
958
959 if let Some(set_val) = js_obj.borrow().get(&"__set__".into())
961 && let Value::Set(set_rc) = &*set_val.borrow()
962 {
963 let set_values = set_rc.borrow().values.clone();
964 let next_body = vec![
966 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
967 Statement::If(
968 Expr::Binary(
969 Box::new(Expr::Var("idx".to_string())),
970 BinaryOp::LessThan,
971 Box::new(Expr::Value(Value::Number(set_values.len() as f64))),
972 ),
973 vec![
974 Statement::Let(
975 "value".to_string(),
976 Some(Expr::Index(
977 Box::new(Expr::Var("__values".to_string())),
978 Box::new(Expr::Var("idx".to_string())),
979 )),
980 ),
981 Statement::Expr(Expr::Assign(
982 Box::new(Expr::Var("__i".to_string())),
983 Box::new(Expr::Binary(
984 Box::new(Expr::Var("idx".to_string())),
985 BinaryOp::Add,
986 Box::new(Expr::Value(Value::Number(1.0))),
987 )),
988 )),
989 Statement::Return(Some(Expr::Object(vec![
990 ("value".to_string(), Expr::Var("value".to_string())),
991 ("done".to_string(), Expr::Value(Value::Boolean(false))),
992 ]))),
993 ],
994 Some(vec![Statement::Return(Some(Expr::Object(vec![(
995 "done".to_string(),
996 Expr::Value(Value::Boolean(true)),
997 )])))]),
998 ),
999 ];
1000
1001 let set_iter_body = vec![
1002 Statement::Let("__i".to_string(), Some(Expr::Value(Value::Number(0.0)))),
1003 Statement::Return(Some(Expr::Object(vec![(
1004 "next".to_string(),
1005 Expr::Function(Vec::new(), next_body),
1006 )]))),
1007 ];
1008
1009 let captured_env = Rc::new(RefCell::new(JSObjectData::new()));
1010 let mut values_obj = JSObjectData::new();
1012 for (i, value) in set_values.iter().enumerate() {
1013 values_obj.insert(i.to_string().into(), Rc::new(RefCell::new(value.clone())));
1014 }
1015 captured_env.borrow_mut().insert(
1016 "__values".into(),
1017 Rc::new(RefCell::new(Value::Object(Rc::new(RefCell::new(values_obj))))),
1018 );
1019
1020 let closure = Value::Closure(Vec::new(), set_iter_body, captured_env.clone());
1021 return Ok(Some(Rc::new(RefCell::new(closure))));
1022 }
1023
1024 if let Some(wrapped) = js_obj.borrow().get(&"__value__".into())
1026 && let Value::String(_) = &*wrapped.borrow()
1027 {
1028 let next_body = vec![
1029 Statement::Let("idx".to_string(), Some(Expr::Var("__i".to_string()))),
1030 Statement::If(
1031 Expr::Binary(
1032 Box::new(Expr::Var("idx".to_string())),
1033 BinaryOp::LessThan,
1034 Box::new(Expr::Property(Box::new(Expr::Var("__s".to_string())), "length".to_string())),
1035 ),
1036 vec![
1037 Statement::Let(
1038 "v".to_string(),
1039 Some(Expr::Index(
1040 Box::new(Expr::Var("__s".to_string())),
1041 Box::new(Expr::Var("idx".to_string())),
1042 )),
1043 ),
1044 Statement::Expr(Expr::Assign(
1045 Box::new(Expr::Var("__i".to_string())),
1046 Box::new(Expr::Binary(
1047 Box::new(Expr::Var("idx".to_string())),
1048 BinaryOp::Add,
1049 Box::new(Expr::Value(Value::Number(1.0))),
1050 )),
1051 )),
1052 Statement::Return(Some(Expr::Object(vec![
1053 ("value".to_string(), Expr::Var("v".to_string())),
1054 ("done".to_string(), Expr::Value(Value::Boolean(false))),
1055 ]))),
1056 ],
1057 Some(vec![Statement::Return(Some(Expr::Object(vec![(
1058 "done".to_string(),
1059 Expr::Value(Value::Boolean(true)),
1060 )])))]),
1061 ),
1062 ];
1063
1064 let str_iter_body = vec![
1065 Statement::Let("__i".to_string(), Some(Expr::Value(Value::Number(0.0)))),
1066 Statement::Return(Some(Expr::Object(vec![(
1067 "next".to_string(),
1068 Expr::Function(Vec::new(), next_body),
1069 )]))),
1070 ];
1071
1072 let captured_env = Rc::new(RefCell::new(JSObjectData::new()));
1073 captured_env.borrow_mut().insert(
1074 PropertyKey::String("__s".to_string()),
1075 Rc::new(RefCell::new(wrapped.borrow().clone())),
1076 );
1077 let closure = Value::Closure(Vec::new(), str_iter_body, captured_env.clone());
1078 return Ok(Some(Rc::new(RefCell::new(closure))));
1079 }
1080 }
1081 if let Some(tag_sym_rc) = get_well_known_symbol_rc("toStringTag")
1082 && let (Value::Symbol(tag_sd), Value::Symbol(req_sd)) = (&*tag_sym_rc.borrow(), &*sym_rc.borrow())
1083 && Rc::ptr_eq(tag_sd, req_sd)
1084 {
1085 if is_array(js_obj) {
1086 return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("Array"))))));
1087 }
1088 if let Some(wrapped) = js_obj.borrow().get(&"__value__".into()) {
1089 match &*wrapped.borrow() {
1090 Value::String(_) => return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("String")))))),
1091 Value::Number(_) => return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("Number")))))),
1092 Value::Boolean(_) => return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("Boolean")))))),
1093 _ => {}
1094 }
1095 }
1096 if js_obj.borrow().contains_key(&"__timestamp".into()) {
1097 return Ok(Some(Rc::new(RefCell::new(Value::String(utf8_to_utf16("Date"))))));
1098 }
1099 }
1100 }
1101
1102 Ok(None)
1103}
1104
1105pub fn obj_set_value(js_obj: &JSObjectDataPtr, key: &PropertyKey, val: Value) -> Result<(), JSError> {
1106 if let Some(proxy_val) = js_obj.borrow().get(&"__proxy__".into())
1108 && let Value::Proxy(proxy) = &*proxy_val.borrow()
1109 {
1110 let success = crate::js_proxy::proxy_set_property(proxy, key, val)?;
1111 if !success {
1112 return Err(raise_eval_error!("Proxy set trap returned false"));
1113 }
1114 return Ok(());
1115 }
1116
1117 let existing_opt = js_obj.borrow().get(key);
1119 if let Some(existing) = existing_opt {
1120 match existing.borrow().clone() {
1121 Value::Property { value: _, getter, setter } => {
1122 if let Some((param, body, env)) = setter {
1123 let setter_env = Rc::new(RefCell::new(JSObjectData::new()));
1125 setter_env.borrow_mut().prototype = Some(env);
1126 env_set(&setter_env, "this", Value::Object(js_obj.clone()))?;
1127 env_set(&setter_env, ¶m[0], val)?;
1128 let _v = evaluate_statements(&setter_env, &body)?;
1129 } else {
1130 let value = Some(Rc::new(RefCell::new(val)));
1132 let new_prop = Value::Property { value, getter, setter };
1133 js_obj.borrow_mut().insert(key.clone(), Rc::new(RefCell::new(new_prop)));
1134 }
1135 return Ok(());
1136 }
1137 Value::Setter(param, body, env) => {
1138 let setter_env = Rc::new(RefCell::new(JSObjectData::new()));
1140 setter_env.borrow_mut().prototype = Some(env);
1141 env_set(&setter_env, "this", Value::Object(js_obj.clone()))?;
1142 env_set(&setter_env, ¶m[0], val)?;
1143 evaluate_statements(&setter_env, &body)?;
1144 return Ok(());
1145 }
1146 _ => {}
1147 }
1148 }
1149 js_obj.borrow_mut().insert(key.clone(), Rc::new(RefCell::new(val)));
1151 Ok(())
1152}
1153
1154pub fn obj_set_rc(map: &JSObjectDataPtr, key: &PropertyKey, val_rc: Rc<RefCell<Value>>) {
1155 map.borrow_mut().insert(key.clone(), val_rc);
1156}
1157
1158pub fn obj_delete(map: &JSObjectDataPtr, key: &PropertyKey) -> Result<bool, JSError> {
1159 if let Some(proxy_val) = map.borrow().get(&"__proxy__".into())
1161 && let Value::Proxy(proxy) = &*proxy_val.borrow()
1162 {
1163 return crate::js_proxy::proxy_delete_property(proxy, key);
1164 }
1165
1166 map.borrow_mut().remove(key);
1167 Ok(true) }
1169
1170pub fn env_get<T: AsRef<str>>(env: &JSObjectDataPtr, key: T) -> Option<Rc<RefCell<Value>>> {
1171 env.borrow().get(&key.as_ref().into())
1172}
1173
1174pub fn env_set<T: AsRef<str>>(env: &JSObjectDataPtr, key: T, val: Value) -> Result<(), JSError> {
1175 let key = key.as_ref();
1176 if env.borrow().is_const(key) {
1177 return Err(raise_type_error!(format!("Assignment to constant variable '{key}'")));
1178 }
1179 env.borrow_mut()
1180 .insert(PropertyKey::String(key.to_string()), Rc::new(RefCell::new(val)));
1181 Ok(())
1182}
1183
1184pub fn env_set_recursive<T: AsRef<str>>(env: &JSObjectDataPtr, key: T, val: Value) -> Result<(), JSError> {
1185 let key_str = key.as_ref();
1186 let mut current = env.clone();
1187 loop {
1188 if current.borrow().contains_key(&key_str.into()) {
1189 return env_set(¤t, key_str, val);
1190 }
1191 let parent_opt = current.borrow().prototype.clone();
1192 if let Some(parent) = parent_opt {
1193 current = parent;
1194 } else {
1195 return env_set(env, key_str, val);
1197 }
1198 }
1199}
1200
1201pub fn env_set_var(env: &JSObjectDataPtr, key: &str, val: Value) -> Result<(), JSError> {
1202 let mut current = env.clone();
1203 loop {
1204 if current.borrow().is_function_scope {
1205 return env_set(¤t, key, val);
1206 }
1207 let parent_opt = current.borrow().prototype.clone();
1208 if let Some(parent) = parent_opt {
1209 current = parent;
1210 } else {
1211 return env_set(env, key, val);
1213 }
1214 }
1215}
1216
1217pub fn env_set_const(env: &JSObjectDataPtr, key: &str, val: Value) {
1218 let mut env_mut = env.borrow_mut();
1219 env_mut.insert(PropertyKey::String(key.to_string()), Rc::new(RefCell::new(val)));
1220 env_mut.set_const(key.to_string());
1221}