1mod attribute;
19mod nonmaxu32;
20
21use crate::{
22 JsString, JsSymbol, JsValue, js_string, object::shape::slot::SlotAttributes, string::JsStr,
23};
24use boa_gc::{Finalize, Trace};
25use std::{fmt, iter::FusedIterator};
26
27pub use {attribute::Attribute, nonmaxu32::NonMaxU32};
28
29#[derive(Default, Debug, Clone, Trace, Finalize)]
52pub struct PropertyDescriptor {
53 enumerable: Option<bool>,
54 configurable: Option<bool>,
55 kind: DescriptorKind,
56}
57
58#[derive(Debug, Default, Clone, Trace, Finalize)]
60pub enum DescriptorKind {
61 Data {
63 value: Option<JsValue>,
65
66 writable: Option<bool>,
68 },
69
70 Accessor {
72 get: Option<JsValue>,
74
75 set: Option<JsValue>,
77 },
78
79 #[default]
81 Generic,
82}
83
84impl PropertyDescriptor {
85 #[inline]
92 #[must_use]
93 pub const fn is_accessor_descriptor(&self) -> bool {
94 matches!(self.kind, DescriptorKind::Accessor { .. })
95 }
96
97 #[inline]
104 #[must_use]
105 pub const fn is_data_descriptor(&self) -> bool {
106 matches!(self.kind, DescriptorKind::Data { .. })
107 }
108
109 #[inline]
116 #[must_use]
117 pub const fn is_generic_descriptor(&self) -> bool {
118 matches!(self.kind, DescriptorKind::Generic)
119 }
120
121 #[inline]
123 #[must_use]
124 pub const fn is_empty(&self) -> bool {
125 self.is_generic_descriptor() && self.enumerable.is_none() && self.configurable.is_none()
126 }
127
128 #[inline]
131 #[must_use]
132 pub const fn enumerable(&self) -> Option<bool> {
133 self.enumerable
134 }
135
136 #[inline]
139 #[must_use]
140 pub const fn configurable(&self) -> Option<bool> {
141 self.configurable
142 }
143
144 #[inline]
147 #[must_use]
148 pub const fn writable(&self) -> Option<bool> {
149 match self.kind {
150 DescriptorKind::Data { writable, .. } => writable,
151 _ => None,
152 }
153 }
154
155 #[inline]
158 #[must_use]
159 pub const fn value(&self) -> Option<&JsValue> {
160 match &self.kind {
161 DescriptorKind::Data { value, .. } => value.as_ref(),
162 _ => None,
163 }
164 }
165
166 #[inline]
169 #[must_use]
170 pub const fn get(&self) -> Option<&JsValue> {
171 match &self.kind {
172 DescriptorKind::Accessor { get, .. } => get.as_ref(),
173 _ => None,
174 }
175 }
176
177 #[inline]
180 #[must_use]
181 pub const fn set(&self) -> Option<&JsValue> {
182 match &self.kind {
183 DescriptorKind::Accessor { set, .. } => set.as_ref(),
184 _ => None,
185 }
186 }
187
188 #[inline]
194 #[must_use]
195 pub fn expect_enumerable(&self) -> bool {
196 self.enumerable
197 .expect("[[enumerable]] field not in property descriptor")
198 }
199
200 #[inline]
206 #[must_use]
207 pub fn expect_configurable(&self) -> bool {
208 self.configurable
209 .expect("[[configurable]] field not in property descriptor")
210 }
211
212 #[inline]
218 #[must_use]
219 pub fn expect_writable(&self) -> bool {
220 self.writable()
221 .expect("[[writable]] field not in property descriptor")
222 }
223
224 #[inline]
230 #[must_use]
231 pub fn expect_value(&self) -> &JsValue {
232 self.value()
233 .expect("[[value]] field not in property descriptor")
234 }
235
236 #[inline]
242 #[must_use]
243 pub fn expect_get(&self) -> &JsValue {
244 self.get()
245 .expect("[[get]] field not in property descriptor")
246 }
247
248 #[inline]
254 #[must_use]
255 pub fn expect_set(&self) -> &JsValue {
256 self.set()
257 .expect("[[set]] field not in property descriptor")
258 }
259
260 #[inline]
262 #[must_use]
263 pub const fn kind(&self) -> &DescriptorKind {
264 &self.kind
265 }
266
267 #[inline]
269 #[must_use]
270 pub fn builder() -> PropertyDescriptorBuilder {
271 PropertyDescriptorBuilder::new()
272 }
273
274 #[inline]
276 #[must_use]
277 pub fn into_accessor_defaulted(mut self) -> Self {
278 match &mut self.kind {
279 DescriptorKind::Accessor { set, get } => {
280 if set.is_none() {
281 *set = Some(JsValue::undefined());
282 }
283 if get.is_none() {
284 *get = Some(JsValue::undefined());
285 }
286 }
287 _ => {
288 self.kind = DescriptorKind::Accessor {
289 get: Some(JsValue::undefined()),
290 set: Some(JsValue::undefined()),
291 };
292 }
293 }
294 self.configurable = self.configurable.or(Some(false));
295 self.enumerable = self.enumerable.or(Some(false));
296 self
297 }
298
299 #[must_use]
301 pub fn into_data_defaulted(mut self) -> Self {
302 match &mut self.kind {
303 DescriptorKind::Data { value, writable } => {
304 if value.is_none() {
305 *value = Some(JsValue::undefined());
306 }
307 if writable.is_none() {
308 *writable = Some(false);
309 }
310 }
311 _ => {
312 self.kind = DescriptorKind::Data {
313 value: Some(JsValue::undefined()),
314 writable: Some(false),
315 };
316 }
317 }
318 self.configurable = self.configurable.or(Some(false));
319 self.enumerable = self.enumerable.or(Some(false));
320 self
321 }
322
323 #[inline]
325 #[must_use]
326 pub fn complete_property_descriptor(self) -> Self {
327 PropertyDescriptorBuilder { inner: self }
328 .complete_with_defaults()
329 .build()
330 }
331
332 #[inline]
339 pub fn fill_with(&mut self, mut desc: Self) {
340 match (&mut self.kind, &mut desc.kind) {
341 (
342 DescriptorKind::Data { value, writable },
343 DescriptorKind::Data {
344 value: desc_value,
345 writable: desc_writable,
346 },
347 ) => {
348 if desc_value.is_some() {
349 std::mem::swap(value, desc_value);
350 }
351 if desc_writable.is_some() {
352 std::mem::swap(writable, desc_writable);
353 }
354 }
355 (
356 DescriptorKind::Accessor { get, set },
357 DescriptorKind::Accessor {
358 get: desc_get,
359 set: desc_set,
360 },
361 ) => {
362 if desc_get.is_some() {
363 std::mem::swap(get, desc_get);
364 }
365 if desc_set.is_some() {
366 std::mem::swap(set, desc_set);
367 }
368 }
369 (_, DescriptorKind::Generic) => {}
370 _ => panic!("Tried to fill a descriptor with an incompatible descriptor"),
371 }
372
373 if let Some(enumerable) = desc.enumerable {
374 self.enumerable = Some(enumerable);
375 }
376 if let Some(configurable) = desc.configurable {
377 self.configurable = Some(configurable);
378 }
379 }
380
381 pub(crate) fn to_slot_attributes(&self) -> SlotAttributes {
382 let mut attributes = SlotAttributes::empty();
383 attributes.set(SlotAttributes::CONFIGURABLE, self.expect_configurable());
384 attributes.set(SlotAttributes::ENUMERABLE, self.expect_enumerable());
385 if self.is_data_descriptor() {
386 attributes.set(SlotAttributes::WRITABLE, self.expect_writable());
387 } else {
388 attributes.set(SlotAttributes::GET, self.get().is_some());
389 attributes.set(SlotAttributes::SET, self.set().is_some());
390 }
391 attributes
392 }
393}
394
395#[derive(Default, Debug, Clone)]
397pub struct PropertyDescriptorBuilder {
398 inner: PropertyDescriptor,
399}
400
401impl PropertyDescriptorBuilder {
402 #[must_use]
404 pub fn new() -> Self {
405 Self::default()
406 }
407
408 #[must_use]
410 pub fn value<V: Into<JsValue>>(mut self, value: V) -> Self {
411 match self.inner.kind {
412 DescriptorKind::Data {
413 value: ref mut v, ..
414 } => *v = Some(value.into()),
415 _ => {
417 self.inner.kind = DescriptorKind::Data {
418 value: Some(value.into()),
419 writable: None,
420 }
421 }
422 }
423 self
424 }
425
426 #[must_use]
428 pub fn writable(mut self, writable: bool) -> Self {
429 match self.inner.kind {
430 DescriptorKind::Data {
431 writable: ref mut w,
432 ..
433 } => *w = Some(writable),
434 _ => {
436 self.inner.kind = DescriptorKind::Data {
437 value: None,
438 writable: Some(writable),
439 }
440 }
441 }
442 self
443 }
444
445 #[must_use]
447 pub fn get<V: Into<JsValue>>(mut self, get: V) -> Self {
448 match self.inner.kind {
449 DescriptorKind::Accessor { get: ref mut g, .. } => *g = Some(get.into()),
450 _ => {
452 self.inner.kind = DescriptorKind::Accessor {
453 get: Some(get.into()),
454 set: None,
455 }
456 }
457 }
458 self
459 }
460
461 #[must_use]
463 pub fn set<V: Into<JsValue>>(mut self, set: V) -> Self {
464 match self.inner.kind {
465 DescriptorKind::Accessor { set: ref mut s, .. } => *s = Some(set.into()),
466 _ => {
468 self.inner.kind = DescriptorKind::Accessor {
469 set: Some(set.into()),
470 get: None,
471 }
472 }
473 }
474 self
475 }
476
477 #[must_use]
479 pub const fn maybe_enumerable(mut self, enumerable: Option<bool>) -> Self {
480 if let Some(enumerable) = enumerable {
481 self = self.enumerable(enumerable);
482 }
483 self
484 }
485
486 #[must_use]
488 pub const fn maybe_configurable(mut self, configurable: Option<bool>) -> Self {
489 if let Some(configurable) = configurable {
490 self = self.configurable(configurable);
491 }
492 self
493 }
494
495 #[must_use]
497 pub fn maybe_value<V: Into<JsValue>>(mut self, value: Option<V>) -> Self {
498 if let Some(value) = value {
499 self = self.value(value);
500 }
501 self
502 }
503
504 #[must_use]
506 pub fn maybe_writable(mut self, writable: Option<bool>) -> Self {
507 if let Some(writable) = writable {
508 self = self.writable(writable);
509 }
510 self
511 }
512
513 #[must_use]
515 pub fn maybe_get<V: Into<JsValue>>(mut self, get: Option<V>) -> Self {
516 if let Some(get) = get {
517 self = self.get(get);
518 }
519 self
520 }
521
522 #[must_use]
524 pub fn maybe_set<V: Into<JsValue>>(mut self, set: Option<V>) -> Self {
525 if let Some(set) = set {
526 self = self.set(set);
527 }
528 self
529 }
530
531 #[must_use]
533 pub const fn enumerable(mut self, enumerable: bool) -> Self {
534 self.inner.enumerable = Some(enumerable);
535 self
536 }
537
538 #[must_use]
540 pub const fn configurable(mut self, configurable: bool) -> Self {
541 self.inner.configurable = Some(configurable);
542 self
543 }
544
545 #[must_use]
547 pub fn complete_with_defaults(mut self) -> Self {
548 match self.inner.kind {
549 DescriptorKind::Generic => {
550 self.inner.kind = DescriptorKind::Data {
551 value: Some(JsValue::undefined()),
552 writable: Some(false),
553 }
554 }
555 DescriptorKind::Data {
556 ref mut value,
557 ref mut writable,
558 } => {
559 if value.is_none() {
560 *value = Some(JsValue::undefined());
561 }
562 if writable.is_none() {
563 *writable = Some(false);
564 }
565 }
566 DescriptorKind::Accessor {
567 ref mut set,
568 ref mut get,
569 } => {
570 if set.is_none() {
571 *set = Some(JsValue::undefined());
572 }
573 if get.is_none() {
574 *get = Some(JsValue::undefined());
575 }
576 }
577 }
578 if self.inner.configurable.is_none() {
579 self.inner.configurable = Some(false);
580 }
581 if self.inner.enumerable.is_none() {
582 self.inner.enumerable = Some(false);
583 }
584 self
585 }
586
587 #[must_use]
589 pub const fn inner(&self) -> &PropertyDescriptor {
590 &self.inner
591 }
592
593 #[must_use]
595 #[allow(clippy::missing_const_for_fn)]
596 pub fn build(self) -> PropertyDescriptor {
597 self.inner
598 }
599}
600
601impl From<PropertyDescriptorBuilder> for PropertyDescriptor {
602 fn from(builder: PropertyDescriptorBuilder) -> Self {
603 builder.build()
604 }
605}
606
607#[derive(Finalize, PartialEq, Debug, Clone, Eq, Hash)]
615pub enum PropertyKey {
616 String(JsString),
618
619 Symbol(JsSymbol),
621
622 Index(NonMaxU32),
624}
625
626fn parse_u32_index<I, T>(mut input: I) -> Option<NonMaxU32>
628where
629 I: Iterator<Item = T> + ExactSizeIterator + FusedIterator,
630 T: Into<u16>,
631{
632 const MAX_CHAR_COUNT: usize = 10;
637
638 const CHAR_ZERO: u16 = b'0' as u16;
639 const CHAR_NINE: u16 = b'9' as u16;
640
641 let len = input.len();
643 if len > MAX_CHAR_COUNT {
644 return None;
645 }
646
647 let to_digit = |c: u16| -> Option<u32> {
649 if matches!(c, CHAR_ZERO..=CHAR_NINE) {
650 Some(u32::from(c - CHAR_ZERO))
651 } else {
652 None
653 }
654 };
655
656 let byte = input.next()?.into();
657 if byte == CHAR_ZERO {
658 if len == 1 {
659 return unsafe { Some(NonMaxU32::new_unchecked(0)) };
661 }
662
663 return None;
665 }
666
667 let mut result = to_digit(byte)?;
668
669 if len == MAX_CHAR_COUNT {
672 for c in input {
673 result = result.checked_mul(10)?.checked_add(to_digit(c.into())?)?;
674 }
675
676 NonMaxU32::new(result)
677 } else {
678 for c in input {
679 result = result * 10 + to_digit(c.into())?;
680 }
681
682 unsafe { Some(NonMaxU32::new_unchecked(result)) }
685 }
686}
687
688impl From<JsStr<'_>> for PropertyKey {
689 #[inline]
690 fn from(string: JsStr<'_>) -> Self {
691 parse_u32_index(string.iter()).map_or_else(|| Self::String(string.into()), Self::Index)
692 }
693}
694
695impl From<JsString> for PropertyKey {
696 #[inline]
697 fn from(string: JsString) -> Self {
698 parse_u32_index(string.as_str().iter()).map_or(Self::String(string), Self::Index)
699 }
700}
701
702impl From<JsSymbol> for PropertyKey {
703 #[inline]
704 fn from(symbol: JsSymbol) -> Self {
705 Self::Symbol(symbol)
706 }
707}
708
709impl fmt::Display for PropertyKey {
710 #[inline]
711 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712 match self {
713 Self::String(string) => string.to_std_string_escaped().fmt(f),
714 Self::Symbol(symbol) => symbol.descriptive_string().to_std_string_escaped().fmt(f),
715 Self::Index(index) => index.get().fmt(f),
716 }
717 }
718}
719
720impl From<&PropertyKey> for JsValue {
721 #[inline]
722 fn from(property_key: &PropertyKey) -> Self {
723 match property_key {
724 PropertyKey::String(string) => string.clone().into(),
725 PropertyKey::Symbol(symbol) => symbol.clone().into(),
726 PropertyKey::Index(index) => {
727 i32::try_from(index.get()).map_or_else(|_| Self::new(index.get()), Self::new)
728 }
729 }
730 }
731}
732
733impl From<PropertyKey> for JsValue {
734 #[inline]
735 fn from(property_key: PropertyKey) -> Self {
736 match property_key {
737 PropertyKey::String(ref string) => string.clone().into(),
738 PropertyKey::Symbol(ref symbol) => symbol.clone().into(),
739 PropertyKey::Index(index) => js_string!(index.get()).into(),
740 }
741 }
742}
743
744impl From<u8> for PropertyKey {
745 fn from(value: u8) -> Self {
746 unsafe { Self::Index(NonMaxU32::new_unchecked(value.into())) }
748 }
749}
750
751impl From<u16> for PropertyKey {
752 fn from(value: u16) -> Self {
753 unsafe { Self::Index(NonMaxU32::new_unchecked(value.into())) }
755 }
756}
757
758impl From<u32> for PropertyKey {
759 fn from(value: u32) -> Self {
760 NonMaxU32::new(value).map_or_else(|| Self::String(value.into()), Self::Index)
761 }
762}
763
764impl From<usize> for PropertyKey {
765 fn from(value: usize) -> Self {
766 u32::try_from(value)
767 .ok()
768 .and_then(NonMaxU32::new)
769 .map_or_else(|| Self::String(value.into()), Self::Index)
770 }
771}
772
773impl From<i64> for PropertyKey {
774 fn from(value: i64) -> Self {
775 u32::try_from(value)
776 .ok()
777 .and_then(NonMaxU32::new)
778 .map_or_else(|| Self::String(value.into()), Self::Index)
779 }
780}
781
782impl From<u64> for PropertyKey {
783 fn from(value: u64) -> Self {
784 u32::try_from(value)
785 .ok()
786 .and_then(NonMaxU32::new)
787 .map_or_else(|| Self::String(value.into()), Self::Index)
788 }
789}
790
791impl From<isize> for PropertyKey {
792 fn from(value: isize) -> Self {
793 u32::try_from(value)
794 .ok()
795 .and_then(NonMaxU32::new)
796 .map_or_else(|| Self::String(value.into()), Self::Index)
797 }
798}
799
800impl From<i32> for PropertyKey {
801 fn from(value: i32) -> Self {
802 if !value.is_negative() {
803 return Self::Index(unsafe { NonMaxU32::new_unchecked(value as u32) });
805 }
806 Self::String(value.into())
807 }
808}
809
810impl From<f64> for PropertyKey {
811 fn from(value: f64) -> Self {
812 use num_traits::cast::FromPrimitive;
813
814 u32::from_f64(value)
815 .and_then(NonMaxU32::new)
816 .map_or_else(|| Self::String(value.into()), Self::Index)
817 }
818}
819
820impl PartialEq<[u16]> for PropertyKey {
821 fn eq(&self, other: &[u16]) -> bool {
822 match self {
823 Self::String(string) => string == other,
824 _ => false,
825 }
826 }
827}
828
829#[derive(Debug, Clone, Copy)]
830pub(crate) enum PropertyNameKind {
831 Key,
832 Value,
833 KeyAndValue,
834}