/src/serenity/Userland/Libraries/LibJS/Runtime/GlobalEnvironment.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <LibJS/Runtime/Completion.h> |
9 | | #include <LibJS/Runtime/DeclarativeEnvironment.h> |
10 | | #include <LibJS/Runtime/EnvironmentCoordinate.h> |
11 | | #include <LibJS/Runtime/GlobalEnvironment.h> |
12 | | #include <LibJS/Runtime/GlobalObject.h> |
13 | | #include <LibJS/Runtime/ObjectEnvironment.h> |
14 | | |
15 | | namespace JS { |
16 | | |
17 | | JS_DEFINE_ALLOCATOR(GlobalEnvironment); |
18 | | |
19 | | // 9.1.2.5 NewGlobalEnvironment ( G, thisValue ), https://tc39.es/ecma262/#sec-newglobalenvironment |
20 | | GlobalEnvironment::GlobalEnvironment(Object& global_object, Object& this_value) |
21 | 52 | : Environment(nullptr) |
22 | 52 | , m_global_this_value(&this_value) |
23 | 52 | { |
24 | 52 | m_object_record = global_object.heap().allocate_without_realm<ObjectEnvironment>(global_object, ObjectEnvironment::IsWithEnvironment::No, nullptr); |
25 | 52 | m_declarative_record = global_object.heap().allocate_without_realm<DeclarativeEnvironment>(); |
26 | 52 | } |
27 | | |
28 | | void GlobalEnvironment::visit_edges(Cell::Visitor& visitor) |
29 | 0 | { |
30 | 0 | Base::visit_edges(visitor); |
31 | 0 | visitor.visit(m_object_record); |
32 | 0 | visitor.visit(m_global_this_value); |
33 | 0 | visitor.visit(m_declarative_record); |
34 | 0 | } |
35 | | |
36 | | // 9.1.1.4.11 GetThisBinding ( ), https://tc39.es/ecma262/#sec-global-environment-records-getthisbinding |
37 | | ThrowCompletionOr<Value> GlobalEnvironment::get_this_binding(VM&) const |
38 | 0 | { |
39 | | // 1. Return envRec.[[GlobalThisValue]]. |
40 | 0 | return m_global_this_value; |
41 | 0 | } |
42 | | |
43 | | // 9.1.1.4.1 HasBinding ( N ), https://tc39.es/ecma262/#sec-global-environment-records-hasbinding-n |
44 | | ThrowCompletionOr<bool> GlobalEnvironment::has_binding(DeprecatedFlyString const& name, Optional<size_t>*) const |
45 | 0 | { |
46 | | // 1. Let DclRec be envRec.[[DeclarativeRecord]]. |
47 | | // 2. If ! DclRec.HasBinding(N) is true, return true. |
48 | 0 | if (MUST(m_declarative_record->has_binding(name))) |
49 | 0 | return true; |
50 | | |
51 | | // 3. Let ObjRec be envRec.[[ObjectRecord]]. |
52 | | // 4. Return ? ObjRec.HasBinding(N). |
53 | 0 | return m_object_record->has_binding(name); |
54 | 0 | } |
55 | | |
56 | | // 9.1.1.4.2 CreateMutableBinding ( N, D ), https://tc39.es/ecma262/#sec-global-environment-records-createmutablebinding-n-d |
57 | | ThrowCompletionOr<void> GlobalEnvironment::create_mutable_binding(VM& vm, DeprecatedFlyString const& name, bool can_be_deleted) |
58 | 0 | { |
59 | | // 1. Let DclRec be envRec.[[DeclarativeRecord]]. |
60 | | // 2. If ! DclRec.HasBinding(N) is true, throw a TypeError exception. |
61 | 0 | if (MUST(m_declarative_record->has_binding(name))) |
62 | 0 | return vm.throw_completion<TypeError>(ErrorType::GlobalEnvironmentAlreadyHasBinding, name); |
63 | | |
64 | | // 3. Return ! DclRec.CreateMutableBinding(N, D). |
65 | 0 | return MUST(m_declarative_record->create_mutable_binding(vm, name, can_be_deleted)); |
66 | 0 | } |
67 | | |
68 | | // 9.1.1.4.3 CreateImmutableBinding ( N, S ), https://tc39.es/ecma262/#sec-global-environment-records-createimmutablebinding-n-s |
69 | | ThrowCompletionOr<void> GlobalEnvironment::create_immutable_binding(VM& vm, DeprecatedFlyString const& name, bool strict) |
70 | 0 | { |
71 | | // 1. Let DclRec be envRec.[[DeclarativeRecord]]. |
72 | | // 2. If ! DclRec.HasBinding(N) is true, throw a TypeError exception. |
73 | 0 | if (MUST(m_declarative_record->has_binding(name))) |
74 | 0 | return vm.throw_completion<TypeError>(ErrorType::GlobalEnvironmentAlreadyHasBinding, name); |
75 | | |
76 | | // 3. Return ! DclRec.CreateImmutableBinding(N, S). |
77 | 0 | return MUST(m_declarative_record->create_immutable_binding(vm, name, strict)); |
78 | 0 | } |
79 | | |
80 | | // 9.1.1.4.4 InitializeBinding ( N, V, hint ), https://tc39.es/ecma262/#sec-global-environment-records-initializebinding-n-v |
81 | | ThrowCompletionOr<void> GlobalEnvironment::initialize_binding(VM& vm, DeprecatedFlyString const& name, Value value, InitializeBindingHint hint) |
82 | 0 | { |
83 | | // 1. Let DclRec be envRec.[[DeclarativeRecord]]. |
84 | | // 2. If ! DclRec.HasBinding(N) is true, then |
85 | 0 | if (MUST(m_declarative_record->has_binding(name))) { |
86 | | // a. Return ! DclRec.InitializeBinding(N, V, hint). |
87 | 0 | return MUST(m_declarative_record->initialize_binding(vm, name, value, hint)); |
88 | 0 | } |
89 | | |
90 | | // 3. Assert: If the binding exists, it must be in the object Environment Record. |
91 | | // 4. Assert: hint is normal. |
92 | 0 | VERIFY(hint == Environment::InitializeBindingHint::Normal); |
93 | | // 5. Let ObjRec be envRec.[[ObjectRecord]]. |
94 | | // 6. Return ? ObjRec.InitializeBinding(N, V, normal). |
95 | 0 | return m_object_record->initialize_binding(vm, name, value, Environment::InitializeBindingHint::Normal); |
96 | 0 | } |
97 | | |
98 | | // 9.1.1.4.5 SetMutableBinding ( N, V, S ), https://tc39.es/ecma262/#sec-global-environment-records-setmutablebinding-n-v-s |
99 | | ThrowCompletionOr<void> GlobalEnvironment::set_mutable_binding(VM& vm, DeprecatedFlyString const& name, Value value, bool strict) |
100 | 0 | { |
101 | | // 1. Let DclRec be envRec.[[DeclarativeRecord]]. |
102 | | // 2. If ! DclRec.HasBinding(N) is true, then |
103 | 0 | if (MUST(m_declarative_record->has_binding(name))) { |
104 | | // a. Return ? DclRec.SetMutableBinding(N, V, S). |
105 | 0 | return m_declarative_record->set_mutable_binding(vm, name, value, strict); |
106 | 0 | } |
107 | | |
108 | | // 3. Let ObjRec be envRec.[[ObjectRecord]]. |
109 | | // 4. Return ? ObjRec.SetMutableBinding(N, V, S). |
110 | 0 | return m_object_record->set_mutable_binding(vm, name, value, strict); |
111 | 0 | } |
112 | | |
113 | | // 9.1.1.4.6 GetBindingValue ( N, S ), https://tc39.es/ecma262/#sec-global-environment-records-getbindingvalue-n-s |
114 | | ThrowCompletionOr<Value> GlobalEnvironment::get_binding_value(VM& vm, DeprecatedFlyString const& name, bool strict) |
115 | 0 | { |
116 | | // 1. Let DclRec be envRec.[[DeclarativeRecord]]. |
117 | | // 2. If ! DclRec.HasBinding(N) is true, then |
118 | 0 | Optional<size_t> index; |
119 | 0 | if (MUST(m_declarative_record->has_binding(name, &index))) { |
120 | | // a. Return ? DclRec.GetBindingValue(N, S). |
121 | 0 | if (index.has_value()) |
122 | 0 | return m_declarative_record->get_binding_value_direct(vm, index.value()); |
123 | 0 | return m_declarative_record->get_binding_value(vm, name, strict); |
124 | 0 | } |
125 | | |
126 | | // 3. Let ObjRec be envRec.[[ObjectRecord]]. |
127 | | // 4. Return ? ObjRec.GetBindingValue(N, S). |
128 | 0 | return m_object_record->get_binding_value(vm, name, strict); |
129 | 0 | } |
130 | | |
131 | | // 9.1.1.4.7 DeleteBinding ( N ), https://tc39.es/ecma262/#sec-global-environment-records-deletebinding-n |
132 | | ThrowCompletionOr<bool> GlobalEnvironment::delete_binding(VM& vm, DeprecatedFlyString const& name) |
133 | 0 | { |
134 | | // 1. Let DclRec be envRec.[[DeclarativeRecord]]. |
135 | | // 2. If ! DclRec.HasBinding(N) is true, then |
136 | 0 | if (MUST(m_declarative_record->has_binding(name))) { |
137 | | // a. Return ! DclRec.DeleteBinding(N). |
138 | 0 | return MUST(m_declarative_record->delete_binding(vm, name)); |
139 | 0 | } |
140 | | |
141 | | // 3. Let ObjRec be envRec.[[ObjectRecord]]. |
142 | | // 4. Let globalObject be ObjRec.[[BindingObject]]. |
143 | | |
144 | | // 5. Let existingProp be ? HasOwnProperty(globalObject, N). |
145 | 0 | bool existing_prop = TRY(m_object_record->binding_object().has_own_property(name)); |
146 | | |
147 | | // 6. If existingProp is true, then |
148 | 0 | if (existing_prop) { |
149 | | // a. Let status be ? ObjRec.DeleteBinding(N). |
150 | 0 | bool status = TRY(m_object_record->delete_binding(vm, name)); |
151 | | |
152 | | // b. If status is true, then |
153 | 0 | if (status) { |
154 | | // i. Let varNames be envRec.[[VarNames]]. |
155 | | // ii. If N is an element of varNames, remove that element from the varNames. |
156 | 0 | m_var_names.remove_all_matching([&](auto& entry) { return entry == name; }); |
157 | 0 | } |
158 | | |
159 | | // c. Return status. |
160 | 0 | return status; |
161 | 0 | } |
162 | | |
163 | | // 7. Return true. |
164 | 0 | return true; |
165 | 0 | } |
166 | | |
167 | | // 9.1.1.4.12 HasVarDeclaration ( N ), https://tc39.es/ecma262/#sec-hasvardeclaration |
168 | | bool GlobalEnvironment::has_var_declaration(DeprecatedFlyString const& name) const |
169 | 0 | { |
170 | | // 1. Let varDeclaredNames be envRec.[[VarNames]]. |
171 | | // 2. If varDeclaredNames contains N, return true. |
172 | | // 3. Return false. |
173 | 0 | return m_var_names.contains_slow(name); |
174 | 0 | } |
175 | | |
176 | | // 9.1.1.4.13 HasLexicalDeclaration ( N ), https://tc39.es/ecma262/#sec-haslexicaldeclaration |
177 | | bool GlobalEnvironment::has_lexical_declaration(DeprecatedFlyString const& name) const |
178 | 0 | { |
179 | | // 1. Let DclRec be envRec.[[DeclarativeRecord]]. |
180 | | // 2. Return ! DclRec.HasBinding(N). |
181 | 0 | return MUST(m_declarative_record->has_binding(name)); |
182 | 0 | } |
183 | | |
184 | | // 9.1.1.4.14 HasRestrictedGlobalProperty ( N ), https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty |
185 | | ThrowCompletionOr<bool> GlobalEnvironment::has_restricted_global_property(DeprecatedFlyString const& name) const |
186 | 0 | { |
187 | | // 1. Let ObjRec be envRec.[[ObjectRecord]]. |
188 | | // 2. Let globalObject be ObjRec.[[BindingObject]]. |
189 | 0 | auto& global_object = m_object_record->binding_object(); |
190 | | |
191 | | // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N). |
192 | 0 | auto existing_prop = TRY(global_object.internal_get_own_property(name)); |
193 | | |
194 | | // 4. If existingProp is undefined, return false. |
195 | 0 | if (!existing_prop.has_value()) |
196 | 0 | return false; |
197 | | |
198 | | // 5. If existingProp.[[Configurable]] is true, return false. |
199 | 0 | if (*existing_prop->configurable) |
200 | 0 | return false; |
201 | | |
202 | | // 6. Return true. |
203 | 0 | return true; |
204 | 0 | } |
205 | | |
206 | | // 9.1.1.4.15 CanDeclareGlobalVar ( N ), https://tc39.es/ecma262/#sec-candeclareglobalvar |
207 | | ThrowCompletionOr<bool> GlobalEnvironment::can_declare_global_var(DeprecatedFlyString const& name) const |
208 | 0 | { |
209 | | // 1. Let ObjRec be envRec.[[ObjectRecord]]. |
210 | | // 2. Let globalObject be ObjRec.[[BindingObject]]. |
211 | 0 | auto& global_object = m_object_record->binding_object(); |
212 | | |
213 | | // 3. Let hasProperty be ? HasOwnProperty(globalObject, N). |
214 | 0 | bool has_property = TRY(global_object.has_own_property(name)); |
215 | | |
216 | | // 4. If hasProperty is true, return true. |
217 | 0 | if (has_property) |
218 | 0 | return true; |
219 | | |
220 | | // 5. Return ? IsExtensible(globalObject). |
221 | 0 | return global_object.is_extensible(); |
222 | 0 | } |
223 | | |
224 | | // 9.1.1.4.16 CanDeclareGlobalFunction ( N ), https://tc39.es/ecma262/#sec-candeclareglobalfunction |
225 | | ThrowCompletionOr<bool> GlobalEnvironment::can_declare_global_function(DeprecatedFlyString const& name) const |
226 | 0 | { |
227 | | // 1. Let ObjRec be envRec.[[ObjectRecord]]. |
228 | | // 2. Let globalObject be ObjRec.[[BindingObject]]. |
229 | 0 | auto& global_object = m_object_record->binding_object(); |
230 | | |
231 | | // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N). |
232 | 0 | auto existing_prop = TRY(global_object.internal_get_own_property(name)); |
233 | | |
234 | | // 4. If existingProp is undefined, return ? IsExtensible(globalObject). |
235 | 0 | if (!existing_prop.has_value()) |
236 | 0 | return TRY(global_object.is_extensible()); |
237 | | |
238 | | // 5. If existingProp.[[Configurable]] is true, return true. |
239 | 0 | if (*existing_prop->configurable) |
240 | 0 | return true; |
241 | | |
242 | | // 6. If IsDataDescriptor(existingProp) is true and existingProp has attribute values { [[Writable]]: true, [[Enumerable]]: true }, return true. |
243 | 0 | if (existing_prop->is_data_descriptor() && *existing_prop->writable && *existing_prop->enumerable) |
244 | 0 | return true; |
245 | | |
246 | | // 7. Return false. |
247 | 0 | return false; |
248 | 0 | } |
249 | | |
250 | | // 9.1.1.4.17 CreateGlobalVarBinding ( N, D ), https://tc39.es/ecma262/#sec-createglobalvarbinding |
251 | | ThrowCompletionOr<void> GlobalEnvironment::create_global_var_binding(DeprecatedFlyString const& name, bool can_be_deleted) |
252 | 0 | { |
253 | 0 | auto& vm = this->vm(); |
254 | | |
255 | | // 1. Let ObjRec be envRec.[[ObjectRecord]]. |
256 | | // 2. Let globalObject be ObjRec.[[BindingObject]]. |
257 | 0 | auto& global_object = m_object_record->binding_object(); |
258 | | |
259 | | // 3. Let hasProperty be ? HasOwnProperty(globalObject, N). |
260 | 0 | auto has_property = TRY(global_object.has_own_property(name)); |
261 | | |
262 | | // 4. Let extensible be ? IsExtensible(globalObject). |
263 | 0 | auto extensible = TRY(global_object.is_extensible()); |
264 | | |
265 | | // 5. If hasProperty is false and extensible is true, then |
266 | 0 | if (!has_property && extensible) { |
267 | | // a. Perform ? ObjRec.CreateMutableBinding(N, D). |
268 | 0 | TRY(m_object_record->create_mutable_binding(vm, name, can_be_deleted)); |
269 | | |
270 | | // b. Perform ? ObjRec.InitializeBinding(N, undefined, normal). |
271 | 0 | TRY(m_object_record->initialize_binding(vm, name, js_undefined(), Environment::InitializeBindingHint::Normal)); |
272 | 0 | } |
273 | | |
274 | | // 6. Let varDeclaredNames be envRec.[[VarNames]]. |
275 | | // 7. If varDeclaredNames does not contain N, then |
276 | 0 | if (!m_var_names.contains_slow(name)) { |
277 | | // a. Append N to varDeclaredNames. |
278 | 0 | m_var_names.append(name); |
279 | 0 | } |
280 | | |
281 | | // 8. Return unused. |
282 | 0 | return {}; |
283 | 0 | } |
284 | | |
285 | | // 9.1.1.4.18 CreateGlobalFunctionBinding ( N, V, D ), https://tc39.es/ecma262/#sec-createglobalfunctionbinding |
286 | | ThrowCompletionOr<void> GlobalEnvironment::create_global_function_binding(DeprecatedFlyString const& name, Value value, bool can_be_deleted) |
287 | 0 | { |
288 | | // 1. Let ObjRec be envRec.[[ObjectRecord]]. |
289 | | // 2. Let globalObject be ObjRec.[[BindingObject]]. |
290 | 0 | auto& global_object = m_object_record->binding_object(); |
291 | | |
292 | | // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N). |
293 | 0 | auto existing_prop = TRY(global_object.internal_get_own_property(name)); |
294 | |
|
295 | 0 | PropertyDescriptor desc; |
296 | | |
297 | | // 4. If existingProp is undefined or existingProp.[[Configurable]] is true, then |
298 | 0 | if (!existing_prop.has_value() || *existing_prop->configurable) { |
299 | | // a. Let desc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }. |
300 | 0 | desc = { .value = value, .writable = true, .enumerable = true, .configurable = can_be_deleted }; |
301 | 0 | } |
302 | | // 5. Else, |
303 | 0 | else { |
304 | | // a. Let desc be the PropertyDescriptor { [[Value]]: V }. |
305 | 0 | desc = { .value = value }; |
306 | 0 | } |
307 | | |
308 | | // 6. Perform ? DefinePropertyOrThrow(globalObject, N, desc). |
309 | 0 | TRY(global_object.define_property_or_throw(name, desc)); |
310 | | |
311 | | // 7. Perform ? Set(globalObject, N, V, false). |
312 | 0 | TRY(global_object.set(name, value, Object::ShouldThrowExceptions::Yes)); |
313 | | |
314 | | // 8. Let varDeclaredNames be envRec.[[VarNames]]. |
315 | | // 9. If varDeclaredNames does not contain N, then |
316 | 0 | if (!m_var_names.contains_slow(name)) { |
317 | | // a. Append N to varDeclaredNames. |
318 | 0 | m_var_names.append(name); |
319 | 0 | } |
320 | | |
321 | | // 10. Return unused. |
322 | 0 | return {}; |
323 | 0 | } |
324 | | |
325 | | } |