quickjs_oxide/engine/api/core/
property.rs1use super::RuntimeError;
4use super::value::{CallableRef, SymbolRef, Value};
5use crate::engine::value::JsString;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct PropertyKey(pub(crate) Key);
12
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub(crate) enum Key {
15 String(JsString),
16 Index(u32),
17 Symbol(SymbolRef),
18}
19
20impl PropertyKey {
21 pub fn try_clone(&self) -> Result<Self, RuntimeError> {
22 Ok(self.clone())
23 }
24
25 pub fn from_js_string(text: JsString) -> Self {
28 let units: Vec<u16> = text.utf16_units().collect();
29 match canonical_index(&units) {
30 Some(index) => PropertyKey(Key::Index(index)),
31 None => PropertyKey(Key::String(text)),
32 }
33 }
34
35 pub fn index(index: u32) -> Self {
36 if index < u32::MAX {
37 PropertyKey(Key::Index(index))
38 } else {
39 PropertyKey(Key::String(JsString::from_fresh_decimal_u32(index)))
40 }
41 }
42
43 pub fn as_symbol(&self) -> Option<&SymbolRef> {
44 match &self.0 {
45 Key::Symbol(symbol) => Some(symbol),
46 _ => None,
47 }
48 }
49
50 pub fn to_js_string(&self) -> Option<JsString> {
52 match &self.0 {
53 Key::String(text) => Some(text.clone()),
54 Key::Index(index) => Some(JsString::from_fresh_decimal_u32(*index)),
55 Key::Symbol(_) => None,
56 }
57 }
58}
59
60fn canonical_index(units: &[u16]) -> Option<u32> {
63 if units.is_empty() || units.len() > 10 || (units.len() > 1 && units[0] == u16::from(b'0')) {
64 return None;
65 }
66 let mut value: u64 = 0;
67 for &unit in units {
68 let digit = unit.checked_sub(u16::from(b'0')).filter(|&d| d < 10)?;
69 value = value * 10 + u64::from(digit);
70 }
71 (value < u64::from(u32::MAX)).then_some(value as u32)
72}
73
74impl From<SymbolRef> for PropertyKey {
75 fn from(symbol: SymbolRef) -> Self {
76 PropertyKey(Key::Symbol(symbol))
77 }
78}
79
80impl TryFrom<&SymbolRef> for PropertyKey {
81 type Error = RuntimeError;
82
83 fn try_from(symbol: &SymbolRef) -> Result<Self, RuntimeError> {
84 Ok(PropertyKey(Key::Symbol(symbol.clone())))
85 }
86}
87
88#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
91pub enum DescriptorField<T> {
92 #[default]
93 Absent,
94 Present(T),
95}
96
97impl<T> DescriptorField<T> {
98 pub fn try_map<U, E>(
99 &self,
100 map: impl FnOnce(&T) -> Result<U, E>,
101 ) -> Result<DescriptorField<U>, E> {
102 match self {
103 Self::Absent => Ok(DescriptorField::Absent),
104 Self::Present(value) => map(value).map(DescriptorField::Present),
105 }
106 }
107
108 pub const fn is_absent(&self) -> bool {
109 matches!(self, Self::Absent)
110 }
111
112 pub const fn is_present(&self) -> bool {
113 matches!(self, Self::Present(_))
114 }
115
116 pub const fn as_ref(&self) -> DescriptorField<&T> {
117 match self {
118 Self::Absent => DescriptorField::Absent,
119 Self::Present(value) => DescriptorField::Present(value),
120 }
121 }
122
123 pub fn map<U>(self, map: impl FnOnce(T) -> U) -> DescriptorField<U> {
124 match self {
125 Self::Absent => DescriptorField::Absent,
126 Self::Present(value) => DescriptorField::Present(map(value)),
127 }
128 }
129
130 pub fn into_option(self) -> Option<T> {
131 match self {
132 Self::Absent => None,
133 Self::Present(value) => Some(value),
134 }
135 }
136}
137
138impl<T> From<Option<T>> for DescriptorField<T> {
139 fn from(value: Option<T>) -> Self {
140 match value {
141 Some(value) => Self::Present(value),
142 None => Self::Absent,
143 }
144 }
145}
146
147#[derive(Clone, Debug, PartialEq)]
149pub enum AccessorValue {
150 Undefined,
151 Callable(CallableRef),
152}
153
154impl AccessorValue {
155 pub fn try_clone(&self) -> Result<Self, RuntimeError> {
156 Ok(self.clone())
157 }
158}
159
160#[derive(Clone, Debug, Default, PartialEq)]
162pub struct OrdinaryPropertyDescriptor {
163 pub value: DescriptorField<Value>,
164 pub writable: DescriptorField<bool>,
165 pub get: DescriptorField<AccessorValue>,
166 pub set: DescriptorField<AccessorValue>,
167 pub enumerable: DescriptorField<bool>,
168 pub configurable: DescriptorField<bool>,
169}
170
171impl OrdinaryPropertyDescriptor {
172 pub fn new() -> Self {
173 Self::default()
174 }
175
176 pub fn try_clone(&self) -> Result<Self, RuntimeError> {
177 Ok(self.clone())
178 }
179
180 pub fn data(value: Value, writable: bool, enumerable: bool, configurable: bool) -> Self {
182 OrdinaryPropertyDescriptor {
183 value: DescriptorField::Present(value),
184 writable: DescriptorField::Present(writable),
185 enumerable: DescriptorField::Present(enumerable),
186 configurable: DescriptorField::Present(configurable),
187 ..Self::default()
188 }
189 }
190}
191
192#[derive(Clone, Debug, PartialEq)]
194pub enum CompleteOrdinaryPropertyDescriptor {
195 Data {
196 value: Value,
197 writable: bool,
198 enumerable: bool,
199 configurable: bool,
200 },
201 Accessor {
202 get: Option<CallableRef>,
203 set: Option<CallableRef>,
204 enumerable: bool,
205 configurable: bool,
206 },
207}
208
209impl CompleteOrdinaryPropertyDescriptor {
210 pub fn try_clone(&self) -> Result<Self, RuntimeError> {
211 Ok(self.clone())
212 }
213
214 pub const fn enumerable(&self) -> bool {
215 match self {
216 Self::Data { enumerable, .. } | Self::Accessor { enumerable, .. } => *enumerable,
217 }
218 }
219
220 pub const fn configurable(&self) -> bool {
221 match self {
222 Self::Data { configurable, .. } | Self::Accessor { configurable, .. } => *configurable,
223 }
224 }
225}
226
227#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
229pub enum WellKnownSymbol {
230 ToPrimitive,
231 Iterator,
232 Match,
233 MatchAll,
234 Replace,
235 Search,
236 Split,
237 ToStringTag,
238 IsConcatSpreadable,
239 HasInstance,
240 Species,
241 Unscopables,
242 AsyncIterator,
243}
244
245impl WellKnownSymbol {
246 pub const ALL: [Self; 13] = [
247 Self::ToPrimitive,
248 Self::Iterator,
249 Self::Match,
250 Self::MatchAll,
251 Self::Replace,
252 Self::Search,
253 Self::Split,
254 Self::ToStringTag,
255 Self::IsConcatSpreadable,
256 Self::HasInstance,
257 Self::Species,
258 Self::Unscopables,
259 Self::AsyncIterator,
260 ];
261
262 pub const fn description(self) -> &'static str {
263 match self {
264 Self::ToPrimitive => "Symbol.toPrimitive",
265 Self::Iterator => "Symbol.iterator",
266 Self::Match => "Symbol.match",
267 Self::MatchAll => "Symbol.matchAll",
268 Self::Replace => "Symbol.replace",
269 Self::Search => "Symbol.search",
270 Self::Split => "Symbol.split",
271 Self::ToStringTag => "Symbol.toStringTag",
272 Self::IsConcatSpreadable => "Symbol.isConcatSpreadable",
273 Self::HasInstance => "Symbol.hasInstance",
274 Self::Species => "Symbol.species",
275 Self::Unscopables => "Symbol.unscopables",
276 Self::AsyncIterator => "Symbol.asyncIterator",
277 }
278 }
279
280 pub fn name(self) -> &'static str {
282 &self.description()["Symbol.".len()..]
283 }
284}