1use crate::{
18 gc::{Finalize, Trace},
19 JsString, JsSymbol, JsValue,
20};
21use std::{convert::TryFrom, fmt};
22
23mod attribute;
24pub use attribute::Attribute;
25use gc::unsafe_empty_trace;
26
27#[derive(Default, Debug, Clone, Trace, Finalize)]
50pub struct PropertyDescriptor {
51 enumerable: Option<bool>,
52 configurable: Option<bool>,
53 kind: DescriptorKind,
54}
55
56#[derive(Debug, Clone, Trace, Finalize)]
57pub enum DescriptorKind {
58 Data {
59 value: Option<JsValue>,
60 writable: Option<bool>,
61 },
62 Accessor {
63 get: Option<JsValue>,
64 set: Option<JsValue>,
65 },
66 Generic,
67}
68
69impl Default for DescriptorKind {
70 fn default() -> Self {
71 Self::Generic
72 }
73}
74
75impl PropertyDescriptor {
76 #[inline]
83 pub fn is_accessor_descriptor(&self) -> bool {
84 matches!(self.kind, DescriptorKind::Accessor { .. })
85 }
86
87 #[inline]
94 pub fn is_data_descriptor(&self) -> bool {
95 matches!(self.kind, DescriptorKind::Data { .. })
96 }
97
98 #[inline]
105 pub fn is_generic_descriptor(&self) -> bool {
106 matches!(self.kind, DescriptorKind::Generic)
107 }
108
109 #[inline]
110 pub fn is_empty(&self) -> bool {
111 self.is_generic_descriptor() && self.enumerable.is_none() && self.configurable.is_none()
112 }
113
114 #[inline]
115 pub fn enumerable(&self) -> Option<bool> {
116 self.enumerable
117 }
118
119 #[inline]
120 pub fn configurable(&self) -> Option<bool> {
121 self.configurable
122 }
123
124 #[inline]
125 pub fn writable(&self) -> Option<bool> {
126 match self.kind {
127 DescriptorKind::Data { writable, .. } => writable,
128 _ => None,
129 }
130 }
131
132 #[inline]
133 pub fn value(&self) -> Option<&JsValue> {
134 match &self.kind {
135 DescriptorKind::Data { value, .. } => value.as_ref(),
136 _ => None,
137 }
138 }
139
140 #[inline]
141 pub fn get(&self) -> Option<&JsValue> {
142 match &self.kind {
143 DescriptorKind::Accessor { get, .. } => get.as_ref(),
144 _ => None,
145 }
146 }
147
148 #[inline]
149 pub fn set(&self) -> Option<&JsValue> {
150 match &self.kind {
151 DescriptorKind::Accessor { set, .. } => set.as_ref(),
152 _ => None,
153 }
154 }
155
156 #[inline]
157 pub fn expect_enumerable(&self) -> bool {
158 if let Some(enumerable) = self.enumerable {
159 enumerable
160 } else {
161 panic!("[[enumerable]] field not in property descriptor")
162 }
163 }
164
165 #[inline]
166 pub fn expect_configurable(&self) -> bool {
167 if let Some(configurable) = self.configurable {
168 configurable
169 } else {
170 panic!("[[configurable]] field not in property descriptor")
171 }
172 }
173
174 #[inline]
175 pub fn expect_writable(&self) -> bool {
176 if let Some(writable) = self.writable() {
177 writable
178 } else {
179 panic!("[[writable]] field not in property descriptor")
180 }
181 }
182
183 #[inline]
184 pub fn expect_value(&self) -> &JsValue {
185 if let Some(value) = self.value() {
186 value
187 } else {
188 panic!("[[value]] field not in property descriptor")
189 }
190 }
191
192 #[inline]
193 pub fn expect_get(&self) -> &JsValue {
194 if let Some(get) = self.get() {
195 get
196 } else {
197 panic!("[[get]] field not in property descriptor")
198 }
199 }
200
201 #[inline]
202 pub fn expect_set(&self) -> &JsValue {
203 if let Some(set) = self.set() {
204 set
205 } else {
206 panic!("[[set]] field not in property descriptor")
207 }
208 }
209
210 #[inline]
211 pub fn kind(&self) -> &DescriptorKind {
212 &self.kind
213 }
214
215 #[inline]
216 pub fn builder() -> PropertyDescriptorBuilder {
217 PropertyDescriptorBuilder::new()
218 }
219
220 #[inline]
221 pub fn into_accessor_defaulted(mut self) -> Self {
222 self.kind = DescriptorKind::Accessor {
223 get: self.get().cloned(),
224 set: self.set().cloned(),
225 };
226 PropertyDescriptorBuilder { inner: self }
227 .complete_with_defaults()
228 .build()
229 }
230
231 pub fn into_data_defaulted(mut self) -> Self {
232 self.kind = DescriptorKind::Data {
233 value: self.value().cloned(),
234 writable: self.writable(),
235 };
236 PropertyDescriptorBuilder { inner: self }
237 .complete_with_defaults()
238 .build()
239 }
240
241 #[inline]
242 pub fn complete_property_descriptor(self) -> Self {
243 PropertyDescriptorBuilder { inner: self }
244 .complete_with_defaults()
245 .build()
246 }
247
248 #[inline]
249 pub fn fill_with(&mut self, desc: Self) {
250 match (&mut self.kind, &desc.kind) {
251 (
252 DescriptorKind::Data { value, writable },
253 DescriptorKind::Data {
254 value: desc_value,
255 writable: desc_writable,
256 },
257 ) => {
258 if let Some(desc_value) = desc_value {
259 *value = Some(desc_value.clone())
260 }
261 if let Some(desc_writable) = desc_writable {
262 *writable = Some(*desc_writable)
263 }
264 }
265 (
266 DescriptorKind::Accessor { get, set },
267 DescriptorKind::Accessor {
268 get: desc_get,
269 set: desc_set,
270 },
271 ) => {
272 if let Some(desc_get) = desc_get {
273 *get = Some(desc_get.clone())
274 }
275 if let Some(desc_set) = desc_set {
276 *set = Some(desc_set.clone())
277 }
278 }
279 (_, DescriptorKind::Generic) => {}
280 _ => panic!("Tried to fill a descriptor with an incompatible descriptor"),
281 }
282
283 if let Some(enumerable) = desc.enumerable {
284 self.enumerable = Some(enumerable)
285 }
286 if let Some(configurable) = desc.configurable {
287 self.configurable = Some(configurable)
288 }
289 }
290}
291
292#[derive(Default, Debug, Clone)]
293pub struct PropertyDescriptorBuilder {
294 inner: PropertyDescriptor,
295}
296
297impl PropertyDescriptorBuilder {
298 pub fn new() -> Self {
299 Self::default()
300 }
301
302 pub fn value<V: Into<JsValue>>(mut self, value: V) -> Self {
303 match self.inner.kind {
304 DescriptorKind::Data {
305 value: ref mut v, ..
306 } => *v = Some(value.into()),
307 _ => {
309 self.inner.kind = DescriptorKind::Data {
310 value: Some(value.into()),
311 writable: None,
312 }
313 }
314 }
315 self
316 }
317
318 pub fn writable(mut self, writable: bool) -> Self {
319 match self.inner.kind {
320 DescriptorKind::Data {
321 writable: ref mut w,
322 ..
323 } => *w = Some(writable),
324 _ => {
326 self.inner.kind = DescriptorKind::Data {
327 value: None,
328 writable: Some(writable),
329 }
330 }
331 }
332 self
333 }
334
335 pub fn get<V: Into<JsValue>>(mut self, get: V) -> Self {
336 match self.inner.kind {
337 DescriptorKind::Accessor { get: ref mut g, .. } => *g = Some(get.into()),
338 _ => {
340 self.inner.kind = DescriptorKind::Accessor {
341 get: Some(get.into()),
342 set: None,
343 }
344 }
345 }
346 self
347 }
348
349 pub fn set<V: Into<JsValue>>(mut self, set: V) -> Self {
350 match self.inner.kind {
351 DescriptorKind::Accessor { set: ref mut s, .. } => *s = Some(set.into()),
352 _ => {
354 self.inner.kind = DescriptorKind::Accessor {
355 set: Some(set.into()),
356 get: None,
357 }
358 }
359 }
360 self
361 }
362
363 pub fn maybe_enumerable(mut self, enumerable: Option<bool>) -> Self {
364 if let Some(enumerable) = enumerable {
365 self = self.enumerable(enumerable);
366 }
367 self
368 }
369
370 pub fn maybe_configurable(mut self, configurable: Option<bool>) -> Self {
371 if let Some(configurable) = configurable {
372 self = self.configurable(configurable);
373 }
374 self
375 }
376
377 pub fn maybe_value<V: Into<JsValue>>(mut self, value: Option<V>) -> Self {
378 if let Some(value) = value {
379 self = self.value(value);
380 }
381 self
382 }
383
384 pub fn maybe_writable(mut self, writable: Option<bool>) -> Self {
385 if let Some(writable) = writable {
386 self = self.writable(writable);
387 }
388 self
389 }
390
391 pub fn maybe_get<V: Into<JsValue>>(mut self, get: Option<V>) -> Self {
392 if let Some(get) = get {
393 self = self.get(get);
394 }
395 self
396 }
397
398 pub fn maybe_set<V: Into<JsValue>>(mut self, set: Option<V>) -> Self {
399 if let Some(set) = set {
400 self = self.set(set);
401 }
402 self
403 }
404
405 pub fn enumerable(mut self, enumerable: bool) -> Self {
406 self.inner.enumerable = Some(enumerable);
407 self
408 }
409 pub fn configurable(mut self, configurable: bool) -> Self {
410 self.inner.configurable = Some(configurable);
411 self
412 }
413
414 pub fn complete_with_defaults(mut self) -> Self {
415 match self.inner.kind {
416 DescriptorKind::Generic => {
417 self.inner.kind = DescriptorKind::Data {
418 value: Some(JsValue::undefined()),
419 writable: Some(false),
420 }
421 }
422 DescriptorKind::Data {
423 ref mut value,
424 ref mut writable,
425 } => {
426 if value.is_none() {
427 *value = Some(JsValue::undefined())
428 }
429 if writable.is_none() {
430 *writable = Some(false)
431 }
432 }
433 DescriptorKind::Accessor {
434 ref mut set,
435 ref mut get,
436 } => {
437 if set.is_none() {
438 *set = Some(JsValue::undefined())
439 }
440 if get.is_none() {
441 *get = Some(JsValue::undefined())
442 }
443 }
444 }
445 if self.inner.configurable.is_none() {
446 self.inner.configurable = Some(false);
447 }
448 if self.inner.enumerable.is_none() {
449 self.inner.enumerable = Some(false);
450 }
451 self
452 }
453
454 pub fn inner(&self) -> &PropertyDescriptor {
455 &self.inner
456 }
457
458 pub fn build(self) -> PropertyDescriptor {
459 self.inner
460 }
461}
462
463impl From<PropertyDescriptorBuilder> for PropertyDescriptor {
464 fn from(builder: PropertyDescriptorBuilder) -> Self {
465 builder.build()
466 }
467}
468
469#[derive(Trace, Finalize, PartialEq, Debug, Clone)]
477pub enum PropertyKey {
478 String(JsString),
479 Symbol(JsSymbol),
480 Index(u32),
481}
482
483impl From<JsString> for PropertyKey {
484 #[inline]
485 fn from(string: JsString) -> PropertyKey {
486 if let Ok(index) = string.parse() {
487 PropertyKey::Index(index)
488 } else {
489 PropertyKey::String(string)
490 }
491 }
492}
493
494impl From<&str> for PropertyKey {
495 #[inline]
496 fn from(string: &str) -> PropertyKey {
497 if let Ok(index) = string.parse() {
498 PropertyKey::Index(index)
499 } else {
500 PropertyKey::String(string.into())
501 }
502 }
503}
504
505impl From<String> for PropertyKey {
506 #[inline]
507 fn from(string: String) -> PropertyKey {
508 if let Ok(index) = string.parse() {
509 PropertyKey::Index(index)
510 } else {
511 PropertyKey::String(string.into())
512 }
513 }
514}
515
516impl From<Box<str>> for PropertyKey {
517 #[inline]
518 fn from(string: Box<str>) -> PropertyKey {
519 if let Ok(index) = string.parse() {
520 PropertyKey::Index(index)
521 } else {
522 PropertyKey::String(string.into())
523 }
524 }
525}
526
527impl From<JsSymbol> for PropertyKey {
528 #[inline]
529 fn from(symbol: JsSymbol) -> PropertyKey {
530 PropertyKey::Symbol(symbol)
531 }
532}
533
534impl fmt::Display for PropertyKey {
535 #[inline]
536 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
537 match self {
538 PropertyKey::String(ref string) => string.fmt(f),
539 PropertyKey::Symbol(ref symbol) => symbol.fmt(f),
540 PropertyKey::Index(index) => index.fmt(f),
541 }
542 }
543}
544
545impl From<&PropertyKey> for JsValue {
546 #[inline]
547 fn from(property_key: &PropertyKey) -> JsValue {
548 match property_key {
549 PropertyKey::String(ref string) => string.clone().into(),
550 PropertyKey::Symbol(ref symbol) => symbol.clone().into(),
551 PropertyKey::Index(index) => {
552 if let Ok(integer) = i32::try_from(*index) {
553 JsValue::new(integer)
554 } else {
555 JsValue::new(*index)
556 }
557 }
558 }
559 }
560}
561
562impl From<PropertyKey> for JsValue {
563 #[inline]
564 fn from(property_key: PropertyKey) -> JsValue {
565 match property_key {
566 PropertyKey::String(ref string) => string.clone().into(),
567 PropertyKey::Symbol(ref symbol) => symbol.clone().into(),
568 PropertyKey::Index(index) => {
569 if let Ok(integer) = i32::try_from(index) {
570 JsValue::new(integer)
571 } else {
572 JsValue::new(index)
573 }
574 }
575 }
576 }
577}
578
579impl From<u8> for PropertyKey {
580 fn from(value: u8) -> Self {
581 PropertyKey::Index(value.into())
582 }
583}
584
585impl From<u16> for PropertyKey {
586 fn from(value: u16) -> Self {
587 PropertyKey::Index(value.into())
588 }
589}
590
591impl From<u32> for PropertyKey {
592 fn from(value: u32) -> Self {
593 PropertyKey::Index(value)
594 }
595}
596
597impl From<usize> for PropertyKey {
598 fn from(value: usize) -> Self {
599 if let Ok(index) = u32::try_from(value) {
600 PropertyKey::Index(index)
601 } else {
602 PropertyKey::String(JsString::from(value.to_string()))
603 }
604 }
605}
606
607impl From<i64> for PropertyKey {
608 fn from(value: i64) -> Self {
609 if let Ok(index) = u32::try_from(value) {
610 PropertyKey::Index(index)
611 } else {
612 PropertyKey::String(JsString::from(value.to_string()))
613 }
614 }
615}
616
617impl From<u64> for PropertyKey {
618 fn from(value: u64) -> Self {
619 if let Ok(index) = u32::try_from(value) {
620 PropertyKey::Index(index)
621 } else {
622 PropertyKey::String(JsString::from(value.to_string()))
623 }
624 }
625}
626
627impl From<isize> for PropertyKey {
628 fn from(value: isize) -> Self {
629 if let Ok(index) = u32::try_from(value) {
630 PropertyKey::Index(index)
631 } else {
632 PropertyKey::String(JsString::from(value.to_string()))
633 }
634 }
635}
636
637impl From<i32> for PropertyKey {
638 fn from(value: i32) -> Self {
639 if let Ok(index) = u32::try_from(value) {
640 PropertyKey::Index(index)
641 } else {
642 PropertyKey::String(JsString::from(value.to_string()))
643 }
644 }
645}
646
647impl From<f64> for PropertyKey {
648 fn from(value: f64) -> Self {
649 use num_traits::cast::FromPrimitive;
650 if let Some(index) = u32::from_f64(value) {
651 return PropertyKey::Index(index);
652 }
653
654 PropertyKey::String(ryu_js::Buffer::new().format(value).into())
655 }
656}
657
658impl PartialEq<&str> for PropertyKey {
659 fn eq(&self, other: &&str) -> bool {
660 match self {
661 PropertyKey::String(ref string) => string == other,
662 _ => false,
663 }
664 }
665}
666
667#[derive(Debug, Clone, Copy, Finalize)]
668pub(crate) enum PropertyNameKind {
669 Key,
670 Value,
671 KeyAndValue,
672}
673
674unsafe impl Trace for PropertyNameKind {
675 unsafe_empty_trace!();
676}