1use alloc::boxed::Box;
7use indexmap::IndexMap;
8use alloc::format;
9use alloc::rc::Rc;
10use alloc::string::String;
11use alloc::vec::Vec;
12use core::cell::RefCell;
13
14use super::ast::{Param, Statement};
15use super::bigint::BigInt;
16use super::interp::{Interp, Scope};
17
18#[derive(Default)]
26pub struct FnvHasher(u64);
27impl core::hash::Hasher for FnvHasher {
28 fn finish(&self) -> u64 {
29 self.0
30 }
31 fn write(&mut self, bytes: &[u8]) {
32 const FNV_OFFSET: u64 = 0xcbf29ce484222325;
33 const FNV_PRIME: u64 = 0x100000001b3;
34 let mut hash = if self.0 == 0 { FNV_OFFSET } else { self.0 };
35 for &b in bytes {
36 hash ^= b as u64;
37 hash = hash.wrapping_mul(FNV_PRIME);
38 }
39 self.0 = hash;
40 }
41}
42pub type PropMap<V> = IndexMap<String, V, core::hash::BuildHasherDefault<FnvHasher>>;
44
45pub type ObjRef = Rc<RefCell<Obj>>;
47
48pub type NativeFn = fn(&mut Interp, this: Value, args: &[Value]) -> Result<Value, Value>;
50
51#[derive(Clone)]
53pub enum Value {
54 Undefined,
55 Null,
56 Bool(bool),
57 Number(f64),
58 Str(Rc<String>),
59 BigInt(Rc<BigInt>),
61 Object(ObjRef),
62}
63
64#[derive(Clone)]
66pub struct FunctionData {
67 pub name: String,
68 pub params: Vec<Param>,
69 pub body: Rc<Vec<Statement>>,
70 pub closure: Rc<RefCell<Scope>>,
72 pub is_arrow: bool,
74 pub is_async: bool,
76 pub is_generator: bool,
78 pub bound_this: Option<Box<Value>>,
80}
81
82#[derive(Clone)]
84pub enum ObjKind {
85 Plain,
86 Array(Vec<Value>),
87 Function(FunctionData),
88 Native {
89 name: String,
90 func: NativeFn,
91 },
92 DomElement(usize),
94 Host(String),
96 MapObj(Vec<(Value, Value)>),
98 SetObj(Vec<Value>),
100 PromiseObj(Rc<RefCell<PromiseState>>),
102 Resolver {
104 state: Rc<RefCell<PromiseState>>,
105 reject: bool,
106 },
107 Generator(Rc<RefCell<GenState>>),
109 RegExpObj(Rc<RefCell<super::regex::RegExpData>>),
111 Proxy {
113 target: ObjRef,
114 handler: ObjRef,
115 },
116 Bound {
119 target: Value,
120 bound_this: Value,
121 bound_args: Vec<Value>,
122 },
123 DateObj(f64),
125}
126
127#[derive(Clone)]
129pub enum GenCompletion {
130 Normal(Value),
131 Throw(Value),
132 Return(Value),
133}
134
135pub struct GenState {
144 pub func: FunctionData,
146 pub args: Vec<Value>,
148 pub this: Value,
150 pub sent: Vec<GenCompletion>,
152 pub started: bool,
154 pub done: bool,
156 pub returned: Value,
158}
159
160#[derive(Debug, Clone, Copy, PartialEq)]
161pub struct PropertyAttributes {
162 pub writable: bool,
163 pub configurable: bool,
164 pub enumerable: bool,
165}
166
167impl Default for PropertyAttributes {
168 fn default() -> Self {
169 PropertyAttributes {
170 writable: true,
171 configurable: true,
172 enumerable: true,
173 }
174 }
175}
176
177#[derive(Clone, Copy, PartialEq)]
179pub enum PromiseStatus {
180 Pending,
181 Fulfilled,
182 Rejected,
183}
184
185pub struct Reaction {
187 pub on_fulfilled: Option<Value>,
188 pub on_rejected: Option<Value>,
189 pub result: Rc<RefCell<PromiseState>>,
191}
192
193pub struct PromiseState {
194 pub status: PromiseStatus,
195 pub value: Value,
197 pub reactions: Vec<Reaction>,
199}
200
201impl PromiseState {
202 pub fn pending() -> Self {
203 PromiseState {
204 status: PromiseStatus::Pending,
205 value: Value::Undefined,
206 reactions: Vec::new(),
207 }
208 }
209}
210
211pub struct Job {
213 pub handler: Option<Value>,
215 pub arg: Value,
216 pub is_fulfill: bool,
217 pub result: Rc<RefCell<PromiseState>>,
218}
219
220pub struct Accessor {
222 pub get: Option<Value>,
223 pub set: Option<Value>,
224}
225
226pub struct Obj {
228 pub props: PropMap<Value>,
229 pub accessors: PropMap<Accessor>,
231 pub attrs: PropMap<PropertyAttributes>,
232 pub proto: Option<ObjRef>,
233 pub kind: ObjKind,
234 pub frozen: bool,
237 pub sealed: bool,
240 pub non_extensible: bool,
243 pub null_proto: bool,
251}
252
253impl Obj {
254 pub fn plain() -> ObjRef {
255 Rc::new(RefCell::new(Obj {
256 props: PropMap::default(),
257 accessors: PropMap::default(),
258 attrs: PropMap::default(),
259 proto: None,
260 frozen: false,
261 sealed: false,
262 non_extensible: false,
263 null_proto: false,
264 kind: ObjKind::Plain,
265 }))
266 }
267 pub fn array(items: Vec<Value>) -> ObjRef {
268 Rc::new(RefCell::new(Obj {
269 props: PropMap::default(),
270 accessors: PropMap::default(),
271 attrs: PropMap::default(),
272 proto: None,
273 frozen: false,
274 sealed: false,
275 non_extensible: false,
276 null_proto: false,
277 kind: ObjKind::Array(items),
278 }))
279 }
280 pub fn function(data: FunctionData) -> ObjRef {
281 Rc::new(RefCell::new(Obj {
282 props: PropMap::default(),
283 accessors: PropMap::default(),
284 attrs: PropMap::default(),
285 proto: None,
286 frozen: false,
287 sealed: false,
288 non_extensible: false,
289 null_proto: false,
290 kind: ObjKind::Function(data),
291 }))
292 }
293 pub fn native(name: &str, func: NativeFn) -> ObjRef {
294 Rc::new(RefCell::new(Obj {
295 props: PropMap::default(),
296 accessors: PropMap::default(),
297 attrs: PropMap::default(),
298 proto: None,
299 frozen: false,
300 sealed: false,
301 non_extensible: false,
302 null_proto: false,
303 kind: ObjKind::Native {
304 name: String::from(name),
305 func,
306 },
307 }))
308 }
309 pub fn host(tag: &str) -> ObjRef {
310 Rc::new(RefCell::new(Obj {
311 props: PropMap::default(),
312 accessors: PropMap::default(),
313 attrs: PropMap::default(),
314 proto: None,
315 frozen: false,
316 sealed: false,
317 non_extensible: false,
318 null_proto: false,
319 kind: ObjKind::Host(String::from(tag)),
320 }))
321 }
322 pub fn dom(node_idx: usize) -> ObjRef {
323 Rc::new(RefCell::new(Obj {
324 props: PropMap::default(),
325 accessors: PropMap::default(),
326 attrs: PropMap::default(),
327 proto: None,
328 frozen: false,
329 sealed: false,
330 non_extensible: false,
331 null_proto: false,
332 kind: ObjKind::DomElement(node_idx),
333 }))
334 }
335 pub fn map_obj(entries: Vec<(Value, Value)>) -> ObjRef {
336 Rc::new(RefCell::new(Obj {
337 props: PropMap::default(),
338 accessors: PropMap::default(),
339 attrs: PropMap::default(),
340 proto: None,
341 frozen: false,
342 sealed: false,
343 non_extensible: false,
344 null_proto: false,
345 kind: ObjKind::MapObj(entries),
346 }))
347 }
348 pub fn set_obj(items: Vec<Value>) -> ObjRef {
349 Rc::new(RefCell::new(Obj {
350 props: PropMap::default(),
351 accessors: PropMap::default(),
352 attrs: PropMap::default(),
353 proto: None,
354 frozen: false,
355 sealed: false,
356 non_extensible: false,
357 null_proto: false,
358 kind: ObjKind::SetObj(items),
359 }))
360 }
361 pub fn promise(state: Rc<RefCell<PromiseState>>) -> ObjRef {
362 Rc::new(RefCell::new(Obj {
363 props: PropMap::default(),
364 accessors: PropMap::default(),
365 attrs: PropMap::default(),
366 proto: None,
367 frozen: false,
368 sealed: false,
369 non_extensible: false,
370 null_proto: false,
371 kind: ObjKind::PromiseObj(state),
372 }))
373 }
374 pub fn resolver(state: Rc<RefCell<PromiseState>>, reject: bool) -> ObjRef {
375 Rc::new(RefCell::new(Obj {
376 props: PropMap::default(),
377 accessors: PropMap::default(),
378 attrs: PropMap::default(),
379 proto: None,
380 frozen: false,
381 sealed: false,
382 non_extensible: false,
383 null_proto: false,
384 kind: ObjKind::Resolver { state, reject },
385 }))
386 }
387 pub fn generator(state: GenState) -> ObjRef {
388 Rc::new(RefCell::new(Obj {
389 props: PropMap::default(),
390 accessors: PropMap::default(),
391 attrs: PropMap::default(),
392 proto: None,
393 frozen: false,
394 sealed: false,
395 non_extensible: false,
396 null_proto: false,
397 kind: ObjKind::Generator(Rc::new(RefCell::new(state))),
398 }))
399 }
400 pub fn regexp(data: super::regex::RegExpData) -> ObjRef {
401 Rc::new(RefCell::new(Obj {
402 props: PropMap::default(),
403 accessors: PropMap::default(),
404 attrs: PropMap::default(),
405 proto: None,
406 frozen: false,
407 sealed: false,
408 non_extensible: false,
409 null_proto: false,
410 kind: ObjKind::RegExpObj(Rc::new(RefCell::new(data))),
411 }))
412 }
413 pub fn proxy(target: ObjRef, handler: ObjRef) -> ObjRef {
414 Rc::new(RefCell::new(Obj {
415 props: PropMap::default(),
416 accessors: PropMap::default(),
417 attrs: PropMap::default(),
418 proto: None,
419 frozen: false,
420 sealed: false,
421 non_extensible: false,
422 null_proto: false,
423 kind: ObjKind::Proxy { target, handler },
424 }))
425 }
426 pub fn date_obj(epoch_ms: f64) -> ObjRef {
427 Rc::new(RefCell::new(Obj {
428 props: PropMap::default(),
429 accessors: PropMap::default(),
430 attrs: PropMap::default(),
431 proto: None,
432 frozen: false,
433 sealed: false,
434 non_extensible: false,
435 null_proto: false,
436 kind: ObjKind::DateObj(epoch_ms),
437 }))
438 }
439 pub fn bound(target: Value, bound_this: Value, bound_args: Vec<Value>) -> ObjRef {
440 Rc::new(RefCell::new(Obj {
441 props: PropMap::default(),
442 accessors: PropMap::default(),
443 attrs: PropMap::default(),
444 proto: None,
445 frozen: false,
446 sealed: false,
447 non_extensible: false,
448 null_proto: false,
449 kind: ObjKind::Bound {
450 target,
451 bound_this,
452 bound_args,
453 },
454 }))
455 }
456 pub fn is_callable(&self) -> bool {
457 match &self.kind {
458 ObjKind::Function(_) | ObjKind::Native { .. } | ObjKind::Resolver { .. } => true,
459 ObjKind::Bound { .. } => true,
460 ObjKind::Proxy { target, .. } => target.borrow().is_callable(),
462 _ => false,
463 }
464 }
465}
466
467impl Value {
468 pub fn str(s: impl Into<String>) -> Value {
469 Value::Str(Rc::new(s.into()))
470 }
471 pub fn bigint(b: BigInt) -> Value {
472 Value::BigInt(Rc::new(b))
473 }
474 pub fn object(o: ObjRef) -> Value {
475 Value::Object(o)
476 }
477
478 pub fn truthy(&self) -> bool {
480 match self {
481 Value::Undefined | Value::Null => false,
482 Value::Bool(b) => *b,
483 Value::Number(n) => *n != 0.0 && !n.is_nan(),
484 Value::Str(s) => !s.is_empty(),
485 Value::BigInt(b) => !b.is_zero(),
486 Value::Object(_) => true,
487 }
488 }
489
490 pub fn to_number(&self) -> f64 {
492 match self {
493 Value::Undefined => f64::NAN,
494 Value::Null => 0.0,
495 Value::Bool(b) => {
496 if *b {
497 1.0
498 } else {
499 0.0
500 }
501 }
502 Value::Number(n) => *n,
503 Value::Str(s) => {
504 let t = s.trim();
505 if t.is_empty() {
506 0.0
507 } else if let Some(rest) = t.strip_prefix("0x").or_else(|| t.strip_prefix("0X")) {
508 u64::from_str_radix(rest, 16).map(|n| n as f64).unwrap_or(f64::NAN)
511 } else if let Some(rest) = t.strip_prefix("0o").or_else(|| t.strip_prefix("0O")) {
512 u64::from_str_radix(rest, 8).map(|n| n as f64).unwrap_or(f64::NAN)
513 } else if let Some(rest) = t.strip_prefix("0b").or_else(|| t.strip_prefix("0B")) {
514 u64::from_str_radix(rest, 2).map(|n| n as f64).unwrap_or(f64::NAN)
515 } else {
516 t.parse::<f64>().unwrap_or(f64::NAN)
517 }
518 }
519 Value::BigInt(b) => b.to_f64(),
520 Value::Object(o) => match &o.borrow().kind {
529 ObjKind::DateObj(ms) => *ms,
530 ObjKind::Array(_) => {
531 let s = self.to_js_string();
532 let t = s.trim();
533 if t.is_empty() {
534 0.0
535 } else {
536 t.parse::<f64>().unwrap_or(f64::NAN)
537 }
538 }
539 ObjKind::Proxy { target, .. } => Value::Object(target.clone()).to_number(),
545 _ => f64::NAN,
546 },
547 }
548 }
549
550 pub fn type_of(&self) -> &'static str {
552 match self {
553 Value::Undefined => "undefined",
554 Value::Null => "object",
555 Value::Bool(_) => "boolean",
556 Value::Number(_) => "number",
557 Value::Str(_) => "string",
558 Value::BigInt(_) => "bigint",
559 Value::Object(o) => {
560 if o.borrow().is_callable() {
561 "function"
562 } else {
563 "object"
564 }
565 }
566 }
567 }
568
569 pub fn to_js_string(&self) -> String {
571 let mut seen = Vec::new();
572 self.to_js_string_seen(&mut seen)
573 }
574
575 fn to_js_string_seen(&self, seen: &mut Vec<ObjRef>) -> String {
589 if seen.len() > 30 {
590 return String::new();
591 }
592 match self {
593 Value::Undefined => String::from("undefined"),
594 Value::Null => String::from("null"),
595 Value::Bool(b) => {
596 if *b {
597 String::from("true")
598 } else {
599 String::from("false")
600 }
601 }
602 Value::Number(n) => fmt_number(*n),
603 Value::Str(s) => (**s).clone(),
604 Value::BigInt(b) => b.to_decimal_string(),
605 Value::Object(o) => {
606 let b = o.borrow();
607 match &b.kind {
608 ObjKind::Array(items) => {
609 if seen.iter().any(|s| Rc::ptr_eq(s, o)) {
610 return String::new();
611 }
612 seen.push(o.clone());
613 let parts: Vec<String> = items
614 .iter()
615 .map(|v| match v {
616 Value::Undefined | Value::Null => String::new(),
617 _ => v.to_js_string_seen(seen),
618 })
619 .collect();
620 seen.pop();
621 parts.join(",")
622 }
623 ObjKind::Function(f) => format!("function {}() {{ [code] }}", f.name),
624 ObjKind::Native { name, .. } => {
625 format!("function {}() {{ [native code] }}", name)
626 }
627 ObjKind::Bound { .. } => String::from("function () { [native code] }"),
631 ObjKind::MapObj(_) => String::from("[object Map]"),
632 ObjKind::SetObj(_) => String::from("[object Set]"),
633 ObjKind::PromiseObj(_) => String::from("[object Promise]"),
634 ObjKind::Resolver { .. } => String::from("function () { [native code] }"),
635 ObjKind::Generator(_) => String::from("[object Generator]"),
636 ObjKind::RegExpObj(r) => {
637 let d = r.borrow();
638 format!("/{}/{}", d.re.source, d.re.flags)
639 }
640 ObjKind::Proxy { target, .. } => {
647 Value::Object(target.clone()).to_js_string_seen(seen)
648 }
649 ObjKind::DateObj(ms) => date_to_iso_string_repr(*ms),
653 _ => String::from("[object Object]"),
654 }
655 }
656 }
657 }
658
659 pub fn strict_eq(&self, other: &Value) -> bool {
661 match (self, other) {
662 (Value::Undefined, Value::Undefined) => true,
663 (Value::Null, Value::Null) => true,
664 (Value::Bool(a), Value::Bool(b)) => a == b,
665 (Value::Number(a), Value::Number(b)) => a == b,
666 (Value::Str(a), Value::Str(b)) => a == b,
667 (Value::BigInt(a), Value::BigInt(b)) => a.cmp(b) == core::cmp::Ordering::Equal,
668 (Value::Object(a), Value::Object(b)) => {
674 match (&a.borrow().kind, &b.borrow().kind) {
675 (ObjKind::DomElement(ia), ObjKind::DomElement(ib)) => ia == ib,
676 _ => Rc::ptr_eq(a, b),
677 }
678 }
679 _ => false,
680 }
681 }
682
683 pub fn same_ref(&self, other: &Value) -> bool {
685 match (self, other) {
686 (Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b),
687 _ => self.strict_eq(other),
688 }
689 }
690}
691
692pub fn fmt_number(n: f64) -> String {
694 if n.is_nan() {
695 return String::from("NaN");
696 }
697 if n.is_infinite() {
698 return String::from(if n > 0.0 { "Infinity" } else { "-Infinity" });
699 }
700 if n == 0.0 {
701 return String::from("0");
702 }
703 if libm::trunc(n) == n && libm::fabs(n) < 1e21 {
705 return format!("{}", n as i64);
706 }
707 let mut s = format!("{}", n);
709 if s.contains('.') {
711 while s.ends_with('0') {
712 s.pop();
713 }
714 if s.ends_with('.') {
715 s.pop();
716 }
717 }
718 s
719}
720
721fn date_to_iso_string_repr(ms: f64) -> String {
726 let total_ms = libm::floor(ms) as i64;
727 let days = total_ms.div_euclid(86_400_000);
728 let ms_of_day = total_ms.rem_euclid(86_400_000);
729 let z = days + 719468;
730 let era = (if z >= 0 { z } else { z - 146096 }) / 146097;
731 let doe = z - era * 146097;
732 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
733 let y = yoe + era * 400;
734 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
735 let mp = (5 * doy + 2) / 153;
736 let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
737 let m = (if mp < 10 { mp + 3 } else { mp - 9 }) as u32;
738 let y = if m <= 2 { y + 1 } else { y };
739 let hh = (ms_of_day / 3_600_000) as u32;
740 let mm = ((ms_of_day / 60_000) % 60) as u32;
741 let ss = ((ms_of_day / 1000) % 60) as u32;
742 let mms = (ms_of_day % 1000) as u32;
743 format!(
744 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z",
745 y, m, d, hh, mm, ss, mms
746 )
747}