Skip to main content

script_bindings/
js.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::ffi::CStr;
6
7use bitflags::bitflags;
8use js::context::JSContext;
9use js::jsapi::{JSPROP_ENUMERATE, JSPROP_PERMANENT, JSPROP_READONLY};
10use js::rust::{HandleObject, HandleValue};
11
12bitflags! {
13    /// Flags for property definitions in JavaScript.
14    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
15    pub struct PropertyFlags: u8 {
16        /// The property shows up when enumerating properties on the object.
17        const ENUMERABLE = 1 << 0;
18        /// The property can be changed.
19        const WRITABLE = 1 << 1;
20        /// The property can be deleted or have its attributes changed.
21        const CONFIGURABLE = 1 << 2;
22    }
23}
24
25impl PropertyFlags {
26    /// Convert the flags to the underlying SpiderMonkey `JSPROP_*` attributes.
27    pub fn as_jsprop_flags(&self) -> u32 {
28        let mut attrs: u32 = 0;
29        if self.contains(Self::ENUMERABLE) {
30            attrs |= JSPROP_ENUMERATE as u32;
31        }
32        if !self.contains(Self::CONFIGURABLE) {
33            attrs |= JSPROP_PERMANENT as u32;
34        }
35        if !self.contains(Self::WRITABLE) {
36            attrs |= JSPROP_READONLY as u32;
37        }
38        attrs
39    }
40}
41
42/// Safe wrapper around [`js::rust::wrappers2::JS_DefineProperty`].
43#[allow(non_snake_case)]
44#[inline]
45pub fn JS_DefineProperty(
46    cx: &mut JSContext,
47    obj: HandleObject,
48    name: &CStr,
49    value: HandleValue,
50    flags: PropertyFlags,
51) -> bool {
52    let attrs = flags.as_jsprop_flags();
53    unsafe { js::rust::wrappers2::JS_DefineProperty(cx, obj, name.as_ptr(), value, attrs) }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_property_flags_mapping() {
62        // Enumerable, Writable, Configurable -> only JSPROP_ENUMERATE
63        let all = PropertyFlags::ENUMERABLE | PropertyFlags::WRITABLE | PropertyFlags::CONFIGURABLE;
64        assert_eq!(all.as_jsprop_flags(), JSPROP_ENUMERATE as u32);
65
66        // Constant property: Enumerable, but not Writable or Configurable
67        // -> JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT
68        let constant = PropertyFlags::ENUMERABLE;
69        assert_eq!(
70            constant.as_jsprop_flags(),
71            (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) as u32
72        );
73
74        // Non-enumerable, non-writable, non-configurable (empty)
75        // -> JSPROP_READONLY | JSPROP_PERMANENT
76        let empty = PropertyFlags::empty();
77        assert_eq!(
78            empty.as_jsprop_flags(),
79            (JSPROP_READONLY | JSPROP_PERMANENT) as u32
80        );
81
82        // Writable and Configurable, not Enumerable
83        // -> 0
84        let method = PropertyFlags::WRITABLE | PropertyFlags::CONFIGURABLE;
85        assert_eq!(method.as_jsprop_flags(), 0);
86    }
87}