1use indexmap::IndexMap;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::sync::Arc;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub enum Value {
8 Undefined,
9 Null,
10 Bool(bool),
11 Int(i64),
12 Float(f64),
13 String(Arc<str>),
14 Array(Vec<Value>),
15 Object(IndexMap<Arc<str>, Value>),
16 Function(Closure),
17 #[serde(skip)]
20 Generator(GeneratorObject),
21 #[serde(skip)]
24 BuiltinMethod {
25 object_name: Arc<str>,
26 method_name: Arc<str>,
27 },
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
32pub struct FunctionId(pub usize);
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Closure {
37 pub func_id: FunctionId,
38 pub captured: Vec<(String, Value)>,
39}
40
41#[derive(Debug, Clone)]
43pub struct GeneratorObject {
44 pub id: u64,
46 pub func_id: FunctionId,
48 pub captured: Vec<(String, Value)>,
50 pub suspended: Option<SuspendedFrame>,
52 pub done: bool,
54}
55
56#[derive(Debug, Clone)]
58pub struct SuspendedFrame {
59 pub ip: usize,
60 pub locals: Vec<Value>,
61 pub stack: Vec<Value>,
62}
63
64impl Value {
65 pub fn type_name(&self) -> &'static str {
66 match self {
67 Value::Undefined => "undefined",
68 Value::Null => "null",
69 Value::Bool(_) => "boolean",
70 Value::Int(_) | Value::Float(_) => "number",
71 Value::String(_) => "string",
72 Value::Array(_) => "object",
73 Value::Object(_) => "object",
74 Value::Function(_) | Value::BuiltinMethod { .. } => "function",
75 Value::Generator(_) => "object",
76 }
77 }
78
79 pub fn is_truthy(&self) -> bool {
80 match self {
81 Value::Undefined | Value::Null => false,
82 Value::Bool(b) => *b,
83 Value::Int(n) => *n != 0,
84 Value::Float(n) => *n != 0.0 && !n.is_nan(),
85 Value::String(s) => !s.is_empty(),
86 Value::Array(_)
87 | Value::Object(_)
88 | Value::Function(_)
89 | Value::BuiltinMethod { .. }
90 | Value::Generator(_) => true,
91 }
92 }
93
94 pub fn to_number(&self) -> f64 {
95 match self {
96 Value::Undefined => f64::NAN,
97 Value::Null => 0.0,
98 Value::Bool(true) => 1.0,
99 Value::Bool(false) => 0.0,
100 Value::Int(n) => *n as f64,
101 Value::Float(n) => *n,
102 Value::String(s) => s.parse::<f64>().unwrap_or(f64::NAN),
103 _ => f64::NAN,
104 }
105 }
106
107 pub fn to_js_string(&self) -> String {
108 match self {
109 Value::Undefined => "undefined".to_string(),
110 Value::Null => "null".to_string(),
111 Value::Bool(b) => b.to_string(),
112 Value::Int(n) => n.to_string(),
113 Value::Float(n) => {
114 if n.is_infinite() {
115 if *n > 0.0 {
116 "Infinity".to_string()
117 } else {
118 "-Infinity".to_string()
119 }
120 } else if n.is_nan() {
121 "NaN".to_string()
122 } else {
123 n.to_string()
125 }
126 }
127 Value::String(s) => s.to_string(),
128 Value::Array(arr) => {
129 let items: Vec<String> = arr.iter().map(|v| v.to_js_string()).collect();
130 items.join(",")
131 }
132 Value::Object(_) => "[object Object]".to_string(),
133 Value::Function(_) | Value::BuiltinMethod { .. } => "function".to_string(),
134 Value::Generator(_) => "[object Generator]".to_string(),
135 }
136 }
137
138 pub fn strict_eq(&self, other: &Value) -> bool {
140 match (self, other) {
141 (Value::Undefined, Value::Undefined) | (Value::Null, Value::Null) => true,
142 (Value::Bool(a), Value::Bool(b)) => a == b,
143 (Value::Int(a), Value::Int(b)) => a == b,
144 (Value::Float(a), Value::Float(b)) => a == b,
145 (Value::Int(a), Value::Float(b)) => (*a as f64) == *b,
146 (Value::Float(a), Value::Int(b)) => *a == (*b as f64),
147 (Value::String(a), Value::String(b)) => a == b,
148 _ => false,
150 }
151 }
152}
153
154impl fmt::Display for Value {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 write!(f, "{}", self.to_js_string())
157 }
158}
159
160impl PartialEq for Value {
161 fn eq(&self, other: &Self) -> bool {
162 self.strict_eq(other)
163 }
164}