Skip to main content

zuqe/object/
private.rs

1use crate::{
2    atom::{Atom, AtomKind},
3    context::ContextImpl,
4    error::{JS_ThrowTypeErrorPrivateNotFound, JSResult},
5    object::{JSProperty, Object},
6    utils::unlikely,
7    value::JSValue,
8};
9
10use super::JS_PROP_C_W_E;
11
12impl Object {
13    /// check if `self` has own property suggested by `func.<home_object>.<brand>`.
14    pub fn check_brand(&self, ctx: &ContextImpl, func: JSValue) -> Result<bool, i32> {
15        if let Some(home_obj) = func
16            .as_object_BCFunction()
17            .and_then(|o| o.payload().home_object())
18        {
19            if let Some((idx, _)) = home_obj.find_shape_property(Atom::CONST_Private_brand) {
20                let atom = if let JSProperty::Normal(JSValue::Symbol(a)) =
21                    home_obj.prop_values.get(idx).unwrap()
22                {
23                    *a
24                } else {
25                    ctx.throw_type_error_not_an_object();
26                    return Err(-1);
27                };
28                Ok(self.has_shape_property(atom))
29            } else {
30                ctx.throw_type_error("expecting <brand> private field");
31                Err(-1)
32            }
33        } else {
34            // not a function object, or can't find function's home object
35            ctx.throw_type_error_not_an_object();
36            Err(-1)
37        }
38    }
39
40    fn set_private_field(
41        &mut self,
42        ctx: &ContextImpl,
43        name: JSValue,
44        val: JSValue,
45    ) -> Result<(), i32> {
46        if let JSValue::Symbol(atom) = name {
47            if let Some((idx, _)) = self.find_shape_property(atom) {
48                self.prop_values[idx] = JSProperty::Normal(val);
49                Ok(())
50            } else {
51                JS_ThrowTypeErrorPrivateNotFound(ctx, atom);
52                Err(-1)
53            }
54        } else {
55            ctx.throw_type_error_not_a_symbol();
56            Err(-1)
57        }
58    }
59
60    fn get_private_field(&self, ctx: &ContextImpl, name: JSValue) -> JSResult {
61        if let JSValue::Symbol(atom) = name {
62            if let Some((idx, _)) = self.find_shape_property(atom) {
63                if let JSProperty::Normal(val) = self.prop_values.get(idx).unwrap() {
64                    Ok(*val)
65                } else {
66                    unreachable!()
67                }
68            } else {
69                JS_ThrowTypeErrorPrivateNotFound(ctx, atom);
70                Err(-1)
71            }
72        } else {
73            ctx.throw_type_error_not_a_symbol();
74            Err(-1)
75        }
76    }
77
78    /// Private fields can be added even on non extensible objects or Proxies
79    pub fn define_private_field(
80        &mut self,
81        ctx: &ContextImpl,
82        name: JSValue,
83        val: JSValue,
84    ) -> Result<(), i32> {
85        if let JSValue::Symbol(atom) = name {
86            if self.has_shape_property(atom) {
87                ctx.throw_type_error(&format!(
88                    "private class field '{}' already exists",
89                    atom.to_utf8()
90                ));
91                Err(-1)
92            } else {
93                self.add_own_property(ctx, atom, JS_PROP_C_W_E, JSProperty::Normal(val))
94            }
95        } else {
96            ctx.throw_type_error_not_a_symbol();
97            Err(-1)
98        }
99    }
100}
101
102impl JSValue {
103    /// check if `self` has own property suggested by `func.<home_object>.<brand>`.
104    pub fn check_brand(&self, ctx: &ContextImpl, func: JSValue) -> Result<bool, i32> {
105        if let JSValue::Object(ob) = self {
106            ob.check_brand(ctx, func)
107        } else {
108            ctx.throw_type_error_not_an_object();
109            Err(-1)
110        }
111    }
112
113    /// assign 'home_obj' a symbol as its ident, store that symbol in home_obj.<brand> field.
114    /// if self is an object, then defined a property with the home_obj's symbol as key.
115    pub fn add_brand(&mut self, ctx: &ContextImpl, home_obj: JSValue) -> Result<(), i32> {
116        if let JSValue::Object(mut home) = home_obj {
117            let brand_sym: JSValue =
118                if let Some((idx, _)) = home.find_shape_property(Atom::CONST_Private_brand) {
119                    if let JSProperty::Normal(val) = home.prop_values.get(idx).unwrap() {
120                        *val
121                    } else {
122                        unreachable!()
123                    }
124                } else {
125                    // the private <brand> prop is not present, add it
126                    let br = ctx.create_symbol_from_atom(Atom::CONST__brand_, AtomKind::Private)?;
127                    home.add_own_property(
128                        ctx,
129                        Atom::CONST_Private_brand,
130                        JS_PROP_C_W_E,
131                        JSProperty::Normal(br),
132                    )?;
133                    br
134                };
135
136            if let Some(ob) = self.as_object_mut() {
137                let brand_atom = brand_sym.to_atom(ctx).unwrap();
138                if ob.has_shape_property(brand_atom) {
139                    ctx.throw_type_error("private method is already present");
140                    Err(-1)
141                } else {
142                    ob.add_own_property(
143                        ctx,
144                        brand_atom,
145                        JS_PROP_C_W_E,
146                        JSProperty::Normal(JSValue::UNDEFINED),
147                    )
148                }
149            } else {
150                Ok(())
151            }
152        } else {
153            ctx.throw_type_error_not_an_object();
154            Err(-1)
155        }
156    }
157
158    pub fn set_private_field(
159        &mut self,
160        ctx: &ContextImpl,
161        name: JSValue,
162        val: JSValue,
163    ) -> Result<(), i32> {
164        if let JSValue::Object(o) = self {
165            if unlikely(!o.flags().extensible()) {
166                ctx.throw_type_error("object is nonextensible for private field");
167                Err(-1)
168            } else {
169                o.set_private_field(ctx, name, val)
170            }
171        } else {
172            ctx.throw_type_error_not_an_object();
173            Err(-1)
174        }
175    }
176
177    pub fn get_private_field(&self, ctx: &ContextImpl, name: JSValue) -> JSResult {
178        if let Self::Object(ob) = self {
179            ob.get_private_field(ctx, name)
180        } else {
181            ctx.throw_type_error_not_an_object();
182            Err(-1)
183        }
184    }
185
186    /// Private fields can be added even on non extensible objects or Proxies
187    pub fn define_private_field(
188        &mut self,
189        ctx: &ContextImpl,
190        name: JSValue,
191        val: JSValue,
192    ) -> Result<(), i32> {
193        if let Self::Object(ob) = self {
194            ob.define_private_field(ctx, name, val)
195        } else {
196            ctx.throw_type_error_not_an_object();
197            Err(-1)
198        }
199    }
200}