/src/serenity/Userland/Libraries/LibJS/Runtime/ArgumentsObject.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibJS/Runtime/ArgumentsObject.h> |
8 | | #include <LibJS/Runtime/Completion.h> |
9 | | #include <LibJS/Runtime/GlobalObject.h> |
10 | | |
11 | | namespace JS { |
12 | | |
13 | | JS_DEFINE_ALLOCATOR(ArgumentsObject); |
14 | | |
15 | | ArgumentsObject::ArgumentsObject(Realm& realm, Environment& environment) |
16 | 0 | : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype(), MayInterfereWithIndexedPropertyAccess::Yes) |
17 | 0 | , m_environment(environment) |
18 | 0 | { |
19 | 0 | } |
20 | | |
21 | | void ArgumentsObject::initialize(Realm& realm) |
22 | 0 | { |
23 | 0 | Base::initialize(realm); |
24 | 0 | set_has_parameter_map(); |
25 | 0 | m_parameter_map = Object::create(realm, nullptr); |
26 | 0 | } |
27 | | |
28 | | void ArgumentsObject::visit_edges(Cell::Visitor& visitor) |
29 | 0 | { |
30 | 0 | Base::visit_edges(visitor); |
31 | 0 | visitor.visit(m_environment); |
32 | 0 | visitor.visit(m_parameter_map); |
33 | 0 | } |
34 | | |
35 | | // 10.4.4.3 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-get-p-receiver |
36 | | ThrowCompletionOr<Value> ArgumentsObject::internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const |
37 | 0 | { |
38 | | // 1. Let map be args.[[ParameterMap]]. |
39 | 0 | auto& map = *m_parameter_map; |
40 | | |
41 | | // 2. Let isMapped be ! HasOwnProperty(map, P). |
42 | 0 | bool is_mapped = MUST(m_parameter_map->has_own_property(property_key)); |
43 | | |
44 | | // 3. If isMapped is false, then |
45 | 0 | if (!is_mapped) { |
46 | | // a. Return ? OrdinaryGet(args, P, Receiver). |
47 | 0 | return Object::internal_get(property_key, receiver, cacheable_metadata, phase); |
48 | 0 | } |
49 | | |
50 | | // FIXME: a. Assert: map contains a formal parameter mapping for P. |
51 | | |
52 | | // b. Return ! Get(map, P). |
53 | 0 | return MUST(map.get(property_key)); |
54 | 0 | } |
55 | | |
56 | | // 10.4.4.4 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver |
57 | | ThrowCompletionOr<bool> ArgumentsObject::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*) |
58 | 0 | { |
59 | 0 | bool is_mapped = false; |
60 | | |
61 | | // 1. If SameValue(args, Receiver) is false, then |
62 | 0 | if (!same_value(this, receiver)) { |
63 | | // a. Let isMapped be false. |
64 | 0 | is_mapped = false; |
65 | 0 | } else { |
66 | | // a. Let map be args.[[ParameterMap]]. |
67 | | // b. Let isMapped be ! HasOwnProperty(map, P). |
68 | 0 | is_mapped = MUST(parameter_map().has_own_property(property_key)); |
69 | 0 | } |
70 | | |
71 | | // 3. If isMapped is true, then |
72 | 0 | if (is_mapped) { |
73 | | // a. Assert: The following Set will succeed, since formal parameters mapped by arguments objects are always writable. |
74 | | |
75 | | // b. Perform ! Set(map, P, V, false). |
76 | 0 | MUST(m_parameter_map->set(property_key, value, Object::ShouldThrowExceptions::No)); |
77 | 0 | } |
78 | | |
79 | | // 4. Return ? OrdinarySet(args, P, V, Receiver). |
80 | 0 | return Object::internal_set(property_key, value, receiver); |
81 | 0 | } |
82 | | |
83 | | // 10.4.4.5 [[Delete]] ( P ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-delete-p |
84 | | ThrowCompletionOr<bool> ArgumentsObject::internal_delete(PropertyKey const& property_key) |
85 | 0 | { |
86 | | // 1. Let map be args.[[ParameterMap]]. |
87 | 0 | auto& map = parameter_map(); |
88 | | |
89 | | // 2. Let isMapped be ! HasOwnProperty(map, P). |
90 | 0 | bool is_mapped = MUST(map.has_own_property(property_key)); |
91 | | |
92 | | // 3. Let result be ? OrdinaryDelete(args, P). |
93 | 0 | bool result = TRY(Object::internal_delete(property_key)); |
94 | | |
95 | | // 4. If result is true and isMapped is true, then |
96 | 0 | if (result && is_mapped) { |
97 | | // a. Perform ! map.[[Delete]](P). |
98 | 0 | MUST(map.internal_delete(property_key)); |
99 | 0 | } |
100 | | |
101 | | // 5. Return result. |
102 | 0 | return result; |
103 | 0 | } |
104 | | |
105 | | // 10.4.4.1 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-getownproperty-p |
106 | | ThrowCompletionOr<Optional<PropertyDescriptor>> ArgumentsObject::internal_get_own_property(PropertyKey const& property_key) const |
107 | 0 | { |
108 | | // 1. Let desc be OrdinaryGetOwnProperty(args, P). |
109 | 0 | auto desc = MUST(Object::internal_get_own_property(property_key)); |
110 | | |
111 | | // 2. If desc is undefined, return desc. |
112 | 0 | if (!desc.has_value()) |
113 | 0 | return desc; |
114 | | |
115 | | // 3. Let map be args.[[ParameterMap]]. |
116 | | // 4. Let isMapped be ! HasOwnProperty(map, P). |
117 | 0 | bool is_mapped = MUST(m_parameter_map->has_own_property(property_key)); |
118 | | |
119 | | // 5. If isMapped is true, then |
120 | 0 | if (is_mapped) { |
121 | | // a. Set desc.[[Value]] to ! Get(map, P). |
122 | 0 | desc->value = MUST(m_parameter_map->get(property_key)); |
123 | 0 | } |
124 | | |
125 | | // 6. Return desc. |
126 | 0 | return desc; |
127 | 0 | } |
128 | | |
129 | | // 10.4.4.2 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-defineownproperty-p-desc |
130 | | ThrowCompletionOr<bool> ArgumentsObject::internal_define_own_property(PropertyKey const& property_key, PropertyDescriptor const& descriptor, Optional<PropertyDescriptor>* precomputed_get_own_property) |
131 | 0 | { |
132 | | // 1. Let map be args.[[ParameterMap]]. |
133 | 0 | auto& map = parameter_map(); |
134 | | |
135 | | // 2. Let isMapped be ! HasOwnProperty(map, P). |
136 | 0 | bool is_mapped = MUST(map.has_own_property(property_key)); |
137 | | |
138 | | // 3. Let newArgDesc be Desc. |
139 | 0 | auto new_arg_desc = descriptor; |
140 | | |
141 | | // 4. If isMapped is true and IsDataDescriptor(Desc) is true, then |
142 | 0 | if (is_mapped && descriptor.is_data_descriptor()) { |
143 | | // a. If Desc does not have a [[Value]] field and Desc has a [[Writable]] field, and Desc.[[Writable]] is false, then |
144 | 0 | if (!descriptor.value.has_value() && descriptor.writable.has_value() && descriptor.writable == false) { |
145 | | // i. Set newArgDesc to a copy of Desc. |
146 | 0 | new_arg_desc = descriptor; |
147 | | // ii. Set newArgDesc.[[Value]] to ! Get(map, P). |
148 | 0 | new_arg_desc.value = MUST(map.get(property_key)); |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | | // 5. Let allowed be ! OrdinaryDefineOwnProperty(args, P, newArgDesc). |
153 | 0 | bool allowed = MUST(Object::internal_define_own_property(property_key, new_arg_desc, precomputed_get_own_property)); |
154 | | |
155 | | // 6. If allowed is false, return false. |
156 | 0 | if (!allowed) |
157 | 0 | return false; |
158 | | |
159 | | // 7. If isMapped is true, then |
160 | 0 | if (is_mapped) { |
161 | | // a. If IsAccessorDescriptor(Desc) is true, then |
162 | 0 | if (descriptor.is_accessor_descriptor()) { |
163 | | // i. Perform ! map.[[Delete]](P). |
164 | 0 | MUST(map.internal_delete(property_key)); |
165 | 0 | } else { |
166 | | // i. If Desc has a [[Value]] field, then |
167 | 0 | if (descriptor.value.has_value()) { |
168 | | // 1. Assert: The following Set will succeed, since formal parameters mapped by arguments objects are always writable. |
169 | | |
170 | | // 2. Perform ! Set(map, P, Desc.[[Value]], false). |
171 | 0 | MUST(map.set(property_key, descriptor.value.value(), Object::ShouldThrowExceptions::No)); |
172 | 0 | } |
173 | | // ii. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, then |
174 | 0 | if (descriptor.writable == false) { |
175 | | // 1. Perform ! map.[[Delete]](P). |
176 | 0 | MUST(map.internal_delete(property_key)); |
177 | 0 | } |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | | // 8. Return true. |
182 | 0 | return true; |
183 | 0 | } |
184 | | |
185 | | } |