1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! This module implements the `Attribute` struct which contains the attibutes for property descriptors.

use bitflags::bitflags;
use gc::{unsafe_empty_trace, Finalize, Trace};

#[cfg(test)]
mod tests;

bitflags! {
    /// This struct constains the property flags as describen in the ECMAScript specification.
    ///
    /// It contains the following flags:
    ///  - `[[Writable]]` (`WRITABLE`) - If `false`, attempts by ECMAScript code to change the property's `[[Value]]` attribute using `[[Set]]` will not succeed.
    ///  - `[[Enumerable]]` (`ENUMERABLE`) - If the property will be enumerated by a for-in enumeration.
    ///  - `[[Configurable]]` (`CONFIGURABLE`) - If `false`, attempts to delete the property, change the property to be an `accessor property`, or change its attributes (other than `[[Value]]`, or changing `[[Writable]]` to `false`) will fail.
    ///
    /// Additionaly there are flags for when the flags are defined.
    #[derive(Finalize)]
    pub struct Attribute: u8 {
        /// The `Writable` attribute decides whether the value associated with the property can be changed or not, from its initial value.
        const WRITABLE = 0b0000_0011;

        /// The property is not writable.
        const READONLY = 0b0000_0010;

        /// Is the `Writable` flag defined.
        const HAS_WRITABLE = 0b0000_0010;

        /// If the property can be enumerated by a `for-in` loop.
        const ENUMERABLE = 0b0000_1100;

        /// The property can not be enumerated in a `for-in` loop.
        const NON_ENUMERABLE = 0b0000_1000;

        /// Is the `Enumerable` flag defined.
        const HAS_ENUMERABLE = 0b0000_1000;

        /// If the property descriptor can be changed later.
        const CONFIGURABLE = 0b0011_0000;

        /// The property descriptor cannot be changed.
        const PERMANENT = 0b0010_0000;

        /// Is the `Configurable` flag defined.
        const HAS_CONFIGURABLE = 0b0010_0000;

    }
}

// We implement `Trace` manualy rather that wih derive, beacuse `rust-gc`,
// derive `Trace` does not allow `Copy` and `Trace` to be both implemented.
//
// SAFETY: The `Attribute` struct only contains an `u8`
// and therefore it should be safe to implement an empty trace.
unsafe impl Trace for Attribute {
    unsafe_empty_trace!();
}

impl Attribute {
    /// Clear all flags.
    #[inline]
    pub fn clear(&mut self) {
        self.bits = 0;
    }

    /// Is the `writable` flag defined.
    #[inline]
    pub fn has_writable(self) -> bool {
        self.contains(Self::HAS_WRITABLE)
    }

    /// Sets the `writable` flag.
    #[inline]
    pub fn set_writable(&mut self, value: bool) {
        if value {
            *self |= Self::WRITABLE;
        } else {
            *self |= *self & !Self::WRITABLE;
        }
    }

    /// Gets the `writable` flag.
    #[inline]
    pub fn writable(self) -> bool {
        debug_assert!(self.has_writable());
        self.contains(Self::WRITABLE)
    }

    /// Is the `enumerable` flag defined.
    #[inline]
    pub fn has_enumerable(self) -> bool {
        self.contains(Self::HAS_ENUMERABLE)
    }

    /// Sets the `enumerable` flag.
    #[inline]
    pub fn set_enumerable(&mut self, value: bool) {
        if value {
            *self |= Self::ENUMERABLE;
        } else {
            *self |= *self & !Self::ENUMERABLE;
        }
    }

    /// Gets the `enumerable` flag.
    #[inline]
    pub fn enumerable(self) -> bool {
        debug_assert!(self.has_enumerable());
        self.contains(Self::ENUMERABLE)
    }

    /// Is the `configurable` flag defined.
    #[inline]
    pub fn has_configurable(self) -> bool {
        self.contains(Self::HAS_CONFIGURABLE)
    }

    /// Sets the `configurable` flag.
    #[inline]
    pub fn set_configurable(&mut self, value: bool) {
        if value {
            *self |= Self::CONFIGURABLE;
        } else {
            *self |= *self & !Self::CONFIGURABLE;
        }
    }

    /// Gets the `configurable` flag.
    #[inline]
    pub fn configurable(self) -> bool {
        debug_assert!(self.has_configurable());
        self.contains(Self::CONFIGURABLE)
    }
}

impl Default for Attribute {
    /// Returns the default flags according to the [ECMAScript specification][spec].
    ///
    /// [spec]: https://tc39.es/ecma262/#table-default-attribute-values
    fn default() -> Self {
        Self::READONLY | Self::NON_ENUMERABLE | Self::PERMANENT
    }
}