boa/environment/object_environment_record.rs
1//! # Object Records
2//!
3//! Each object Environment Record is associated with an object called its binding object.
4//! An object Environment Record binds the set of string identifier names that directly
5//! correspond to the property names of its binding object.
6//! Property keys that are not strings in the form of an `IdentifierName` are not included in the set of bound identifiers.
7//! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records)
8
9use gc::Gc;
10
11use crate::{
12 environment::{
13 environment_record_trait::EnvironmentRecordTrait,
14 lexical_environment::{Environment, EnvironmentType},
15 },
16 gc::{Finalize, Trace},
17 object::JsObject,
18 property::PropertyDescriptor,
19 symbol::WellKnownSymbols,
20 Context, JsResult, JsValue,
21};
22
23#[derive(Debug, Trace, Finalize, Clone)]
24pub struct ObjectEnvironmentRecord {
25 pub bindings: JsObject,
26 pub with_environment: bool,
27 pub outer_env: Option<Environment>,
28}
29
30impl ObjectEnvironmentRecord {
31 pub fn new(object: JsObject, environment: Option<Environment>) -> ObjectEnvironmentRecord {
32 ObjectEnvironmentRecord {
33 bindings: object,
34 outer_env: environment,
35 /// Object Environment Records created for with statements (13.11)
36 /// can provide their binding object as an implicit this value for use in function calls.
37 /// The capability is controlled by a withEnvironment Boolean value that is associated
38 /// with each object Environment Record. By default, the value of withEnvironment is false
39 /// for any object Environment Record.
40 with_environment: false,
41 }
42 }
43}
44
45impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
46 /// `9.1.1.2.1 HasBinding ( N )`
47 ///
48 /// More information:
49 /// - [ECMAScript reference][spec]
50 ///
51 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-hasbinding-n
52 fn has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
53 // 1. Let bindingObject be envRec.[[BindingObject]].
54 // 2. Let foundBinding be ? HasProperty(bindingObject, N).
55 // 3. If foundBinding is false, return false.
56 if !self.bindings.has_property(name, context)? {
57 return Ok(false);
58 }
59
60 // 4. If envRec.[[IsWithEnvironment]] is false, return true.
61 if !self.with_environment {
62 return Ok(true);
63 }
64
65 // 5. Let unscopables be ? Get(bindingObject, @@unscopables).
66 // 6. If Type(unscopables) is Object, then
67 if let Some(unscopables) = self
68 .bindings
69 .get(WellKnownSymbols::unscopables(), context)?
70 .as_object()
71 {
72 // a. Let blocked be ! ToBoolean(? Get(unscopables, N)).
73 // b. If blocked is true, return false.
74 if unscopables.get(name, context)?.to_boolean() {
75 return Ok(false);
76 }
77 }
78
79 // 7. Return true.
80 Ok(true)
81 }
82
83 /// `9.1.1.2.2 CreateMutableBinding ( N, D )`
84 ///
85 /// More information:
86 /// - [ECMAScript reference][spec]
87 ///
88 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-createmutablebinding-n-d
89 fn create_mutable_binding(
90 &self,
91 name: &str,
92 deletion: bool,
93 _allow_name_reuse: bool,
94 context: &mut Context,
95 ) -> JsResult<()> {
96 // 1. Let bindingObject be envRec.[[BindingObject]].
97 // 2. Return ? DefinePropertyOrThrow(bindingObject, N, PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }).
98 self.bindings.define_property_or_throw(
99 name,
100 PropertyDescriptor::builder()
101 .value(JsValue::undefined())
102 .writable(true)
103 .enumerable(true)
104 .configurable(deletion),
105 context,
106 )?;
107 Ok(())
108 }
109
110 /// `9.1.1.2.3 CreateImmutableBinding ( N, S )`
111 ///
112 /// More information:
113 /// - [ECMAScript reference][spec]
114 ///
115 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-createimmutablebinding-n-s
116 fn create_immutable_binding(
117 &self,
118 _name: &str,
119 _strict: bool,
120 _context: &mut Context,
121 ) -> JsResult<()> {
122 Ok(())
123 }
124
125 /// `9.1.1.2.4 InitializeBinding ( N, V )`
126 ///
127 /// More information:
128 /// - [ECMAScript reference][spec]
129 ///
130 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-initializebinding-n-v
131 fn initialize_binding(
132 &self,
133 name: &str,
134 value: JsValue,
135 context: &mut Context,
136 ) -> JsResult<()> {
137 // 1. Return ? envRec.SetMutableBinding(N, V, false).
138 self.set_mutable_binding(name, value, false, context)
139 }
140
141 /// `9.1.1.2.5 SetMutableBinding ( N, V, S )`
142 ///
143 /// More information:
144 /// - [ECMAScript reference][spec]
145 ///
146 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-setmutablebinding-n-v-s
147 fn set_mutable_binding(
148 &self,
149 name: &str,
150 value: JsValue,
151 strict: bool,
152 context: &mut Context,
153 ) -> JsResult<()> {
154 // 1. Let bindingObject be envRec.[[BindingObject]].
155 // 2. Let stillExists be ? HasProperty(bindingObject, N).
156 let still_exists = self.bindings.has_property(name, context)?;
157
158 // 3. If stillExists is false and S is true, throw a ReferenceError exception.
159 if !still_exists && strict {
160 return Err(context.construct_reference_error("Binding already exists"));
161 }
162
163 // 4. Return ? Set(bindingObject, N, V, S).
164 self.bindings.set(name, value, strict, context)?;
165 Ok(())
166 }
167
168 /// `9.1.1.2.6 GetBindingValue ( N, S )`
169 ///
170 /// More information:
171 /// - [ECMAScript reference][spec]
172 ///
173 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-getbindingvalue-n-s
174 fn get_binding_value(
175 &self,
176 name: &str,
177 strict: bool,
178 context: &mut Context,
179 ) -> JsResult<JsValue> {
180 // 1. Let bindingObject be envRec.[[BindingObject]].
181 // 2. Let value be ? HasProperty(bindingObject, N).
182 // 3. If value is false, then
183 if !self.bindings.__has_property__(&name.into(), context)? {
184 // a. If S is false, return the value undefined; otherwise throw a ReferenceError exception.
185 if !strict {
186 return Ok(JsValue::undefined());
187 } else {
188 return context.throw_reference_error(format!("{} has no binding", name));
189 }
190 }
191
192 // 4. Return ? Get(bindingObject, N).
193 self.bindings.get(name, context)
194 }
195
196 /// `9.1.1.2.7 DeleteBinding ( N )`
197 ///
198 /// More information:
199 /// - [ECMAScript reference][spec]
200 ///
201 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-deletebinding-n
202 fn delete_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
203 // 1. Let bindingObject be envRec.[[BindingObject]].
204 // 2. Return ? bindingObject.[[Delete]](N).
205 self.bindings.__delete__(&name.into(), context)
206 }
207
208 /// `9.1.1.2.8 HasThisBinding ( )`
209 ///
210 /// More information:
211 /// - [ECMAScript reference][spec]
212 ///
213 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-hasthisbinding
214 fn has_this_binding(&self) -> bool {
215 // 1. Return false.
216 false
217 }
218
219 /// `9.1.1.2.9 HasSuperBinding ( )`
220 ///
221 /// More information:
222 /// - [ECMAScript reference][spec]
223 ///
224 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-hassuperbinding
225 fn has_super_binding(&self) -> bool {
226 // 1. Return false.
227 false
228 }
229
230 /// `9.1.1.2.10 WithBaseObject ( )`
231 ///
232 /// More information:
233 /// - [ECMAScript reference][spec]
234 ///
235 /// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-hassuperbinding
236 fn with_base_object(&self) -> Option<JsObject> {
237 // 1. If envRec.[[IsWithEnvironment]] is true, return envRec.[[BindingObject]].
238 // 2. Otherwise, return undefined.
239 if self.with_environment {
240 Some(self.bindings.clone())
241 } else {
242 None
243 }
244 }
245
246 fn get_this_binding(&self, _context: &mut Context) -> JsResult<JsValue> {
247 Ok(JsValue::undefined())
248 }
249
250 fn get_outer_environment_ref(&self) -> Option<&Environment> {
251 self.outer_env.as_ref()
252 }
253
254 fn set_outer_environment(&mut self, env: Environment) {
255 self.outer_env = Some(env);
256 }
257
258 fn get_environment_type(&self) -> EnvironmentType {
259 EnvironmentType::Function
260 }
261}
262
263impl From<ObjectEnvironmentRecord> for Environment {
264 fn from(env: ObjectEnvironmentRecord) -> Environment {
265 Gc::new(Box::new(env))
266 }
267}