/src/serenity/Userland/Libraries/LibJS/Runtime/PropertyDescriptor.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibJS/Runtime/Error.h> |
8 | | #include <LibJS/Runtime/FunctionObject.h> |
9 | | #include <LibJS/Runtime/GlobalObject.h> |
10 | | #include <LibJS/Runtime/Object.h> |
11 | | #include <LibJS/Runtime/PropertyDescriptor.h> |
12 | | #include <LibJS/Runtime/Value.h> |
13 | | #include <LibJS/Runtime/ValueInlines.h> |
14 | | |
15 | | namespace JS { |
16 | | |
17 | | // 6.2.5.1 IsAccessorDescriptor ( Desc ), https://tc39.es/ecma262/#sec-isaccessordescriptor |
18 | | bool PropertyDescriptor::is_accessor_descriptor() const |
19 | 28.3k | { |
20 | | // 1. If Desc is undefined, return false. |
21 | | |
22 | | // 2. If Desc has a [[Get]] field, return true. |
23 | 28.3k | if (get.has_value()) |
24 | 0 | return true; |
25 | | |
26 | | // 3. If Desc has a [[Set]] field, return true. |
27 | 28.3k | if (set.has_value()) |
28 | 0 | return true; |
29 | | |
30 | | // 4. Return false. |
31 | 28.3k | return false; |
32 | 28.3k | } |
33 | | |
34 | | // 6.2.5.2 IsDataDescriptor ( Desc ), https://tc39.es/ecma262/#sec-isdatadescriptor |
35 | | bool PropertyDescriptor::is_data_descriptor() const |
36 | 3 | { |
37 | | // 1. If Desc is undefined, return false. |
38 | | |
39 | | // 2. If Desc has a [[Value]] field, return true. |
40 | 3 | if (value.has_value()) |
41 | 3 | return true; |
42 | | |
43 | | // 3. If Desc has a [[Writable]] field, return true. |
44 | 0 | if (writable.has_value()) |
45 | 0 | return true; |
46 | | |
47 | | // 4. Return false. |
48 | 0 | return false; |
49 | 0 | } |
50 | | |
51 | | // 6.2.5.3 IsGenericDescriptor ( Desc ), https://tc39.es/ecma262/#sec-isgenericdescriptor |
52 | | bool PropertyDescriptor::is_generic_descriptor() const |
53 | 3 | { |
54 | | // 1. If Desc is undefined, return false. |
55 | | |
56 | | // 2. If IsAccessorDescriptor(Desc) is true, return false. |
57 | 3 | if (is_accessor_descriptor()) |
58 | 0 | return false; |
59 | | |
60 | | // 3. If IsDataDescriptor(Desc) is true, return false. |
61 | 3 | if (is_data_descriptor()) |
62 | 3 | return false; |
63 | | |
64 | | // 4. Return true. |
65 | 0 | return true; |
66 | 3 | } |
67 | | |
68 | | // 6.2.5.4 FromPropertyDescriptor ( Desc ), https://tc39.es/ecma262/#sec-frompropertydescriptor |
69 | | Value from_property_descriptor(VM& vm, Optional<PropertyDescriptor> const& property_descriptor) |
70 | 0 | { |
71 | 0 | auto& realm = *vm.current_realm(); |
72 | |
|
73 | 0 | if (!property_descriptor.has_value()) |
74 | 0 | return js_undefined(); |
75 | 0 | auto object = Object::create(realm, realm.intrinsics().object_prototype()); |
76 | 0 | if (property_descriptor->value.has_value()) |
77 | 0 | MUST(object->create_data_property_or_throw(vm.names.value, *property_descriptor->value)); |
78 | 0 | if (property_descriptor->writable.has_value()) |
79 | 0 | MUST(object->create_data_property_or_throw(vm.names.writable, Value(*property_descriptor->writable))); |
80 | 0 | if (property_descriptor->get.has_value()) |
81 | 0 | MUST(object->create_data_property_or_throw(vm.names.get, *property_descriptor->get ? Value(*property_descriptor->get) : js_undefined())); |
82 | 0 | if (property_descriptor->set.has_value()) |
83 | 0 | MUST(object->create_data_property_or_throw(vm.names.set, *property_descriptor->set ? Value(*property_descriptor->set) : js_undefined())); |
84 | 0 | if (property_descriptor->enumerable.has_value()) |
85 | 0 | MUST(object->create_data_property_or_throw(vm.names.enumerable, Value(*property_descriptor->enumerable))); |
86 | 0 | if (property_descriptor->configurable.has_value()) |
87 | 0 | MUST(object->create_data_property_or_throw(vm.names.configurable, Value(*property_descriptor->configurable))); |
88 | 0 | return object; |
89 | 0 | } |
90 | | |
91 | | // 6.2.5.5 ToPropertyDescriptor ( Obj ), https://tc39.es/ecma262/#sec-topropertydescriptor |
92 | | ThrowCompletionOr<PropertyDescriptor> to_property_descriptor(VM& vm, Value argument) |
93 | 0 | { |
94 | | // 1. If Type(Obj) is not Object, throw a TypeError exception. |
95 | 0 | if (!argument.is_object()) |
96 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, argument.to_string_without_side_effects()); |
97 | | |
98 | 0 | auto& object = argument.as_object(); |
99 | | |
100 | | // 2. Let desc be a new Property Descriptor that initially has no fields. |
101 | 0 | PropertyDescriptor descriptor; |
102 | | |
103 | | // 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable"). |
104 | 0 | auto has_enumerable = TRY(object.has_property(vm.names.enumerable)); |
105 | | |
106 | | // 4. If hasEnumerable is true, then |
107 | 0 | if (has_enumerable) { |
108 | | // a. Let enumerable be ToBoolean(? Get(Obj, "enumerable")). |
109 | 0 | auto enumerable = TRY(object.get(vm.names.enumerable)).to_boolean(); |
110 | | |
111 | | // b. Set desc.[[Enumerable]] to enumerable. |
112 | 0 | descriptor.enumerable = enumerable; |
113 | 0 | } |
114 | | |
115 | | // 5. Let hasConfigurable be ? HasProperty(Obj, "configurable"). |
116 | 0 | auto has_configurable = TRY(object.has_property(vm.names.configurable)); |
117 | | |
118 | | // 6. If hasConfigurable is true, then |
119 | 0 | if (has_configurable) { |
120 | | // a. Let configurable be ToBoolean(? Get(Obj, "configurable")). |
121 | 0 | auto configurable = TRY(object.get(vm.names.configurable)).to_boolean(); |
122 | | |
123 | | // b. Set desc.[[Configurable]] to configurable. |
124 | 0 | descriptor.configurable = configurable; |
125 | 0 | } |
126 | | |
127 | | // 7. Let hasValue be ? HasProperty(Obj, "value"). |
128 | 0 | auto has_value = TRY(object.has_property(vm.names.value)); |
129 | | |
130 | | // 8. If hasValue is true, then |
131 | 0 | if (has_value) { |
132 | | // a. Let value be ? Get(Obj, "value"). |
133 | 0 | auto value = TRY(object.get(vm.names.value)); |
134 | | |
135 | | // b. Set desc.[[Value]] to value. |
136 | 0 | descriptor.value = value; |
137 | 0 | } |
138 | | |
139 | | // 9. Let hasWritable be ? HasProperty(Obj, "writable"). |
140 | 0 | auto has_writable = TRY(object.has_property(vm.names.writable)); |
141 | | |
142 | | // 10. If hasWritable is true, then |
143 | 0 | if (has_writable) { |
144 | | // a. Let writable be ToBoolean(? Get(Obj, "writable")). |
145 | 0 | auto writable = TRY(object.get(vm.names.writable)).to_boolean(); |
146 | | |
147 | | // b. Set desc.[[Writable]] to writable. |
148 | 0 | descriptor.writable = writable; |
149 | 0 | } |
150 | | |
151 | | // 11. Let hasGet be ? HasProperty(Obj, "get"). |
152 | 0 | auto has_get = TRY(object.has_property(vm.names.get)); |
153 | | |
154 | | // 12. If hasGet is true, then |
155 | 0 | if (has_get) { |
156 | | // a. Let getter be ? Get(Obj, "get"). |
157 | 0 | auto getter = TRY(object.get(vm.names.get)); |
158 | | |
159 | | // b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception. |
160 | 0 | if (!getter.is_function() && !getter.is_undefined()) |
161 | 0 | return vm.throw_completion<TypeError>(ErrorType::AccessorBadField, "get"); |
162 | | |
163 | | // c. Set desc.[[Get]] to getter. |
164 | 0 | descriptor.get = getter.is_function() ? &getter.as_function() : nullptr; |
165 | 0 | } |
166 | | |
167 | | // 13. Let hasSet be ? HasProperty(Obj, "set"). |
168 | 0 | auto has_set = TRY(object.has_property(vm.names.set)); |
169 | | |
170 | | // 14. If hasSet is true, then |
171 | 0 | if (has_set) { |
172 | | // a. Let setter be ? Get(Obj, "set"). |
173 | 0 | auto setter = TRY(object.get(vm.names.set)); |
174 | | |
175 | | // b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception. |
176 | 0 | if (!setter.is_function() && !setter.is_undefined()) |
177 | 0 | return vm.throw_completion<TypeError>(ErrorType::AccessorBadField, "set"); |
178 | | |
179 | | // c. Set desc.[[Set]] to setter. |
180 | 0 | descriptor.set = setter.is_function() ? &setter.as_function() : nullptr; |
181 | 0 | } |
182 | | |
183 | | // 15. If desc has a [[Get]] field or desc has a [[Set]] field, then |
184 | 0 | if (descriptor.get.has_value() || descriptor.set.has_value()) { |
185 | | // a. If desc has a [[Value]] field or desc has a [[Writable]] field, throw a TypeError exception. |
186 | 0 | if (descriptor.value.has_value() || descriptor.writable.has_value()) |
187 | 0 | return vm.throw_completion<TypeError>(ErrorType::AccessorValueOrWritable); |
188 | 0 | } |
189 | | |
190 | | // 16. Return desc. |
191 | 0 | return descriptor; |
192 | 0 | } |
193 | | |
194 | | // 6.2.5.6 CompletePropertyDescriptor ( Desc ), https://tc39.es/ecma262/#sec-completepropertydescriptor |
195 | | void PropertyDescriptor::complete() |
196 | 0 | { |
197 | 0 | if (is_generic_descriptor() || is_data_descriptor()) { |
198 | 0 | if (!value.has_value()) |
199 | 0 | value = Value {}; |
200 | 0 | if (!writable.has_value()) |
201 | 0 | writable = false; |
202 | 0 | } else { |
203 | 0 | if (!get.has_value()) |
204 | 0 | get = nullptr; |
205 | 0 | if (!set.has_value()) |
206 | 0 | set = nullptr; |
207 | 0 | } |
208 | 0 | if (!enumerable.has_value()) |
209 | 0 | enumerable = false; |
210 | 0 | if (!configurable.has_value()) |
211 | 0 | configurable = false; |
212 | 0 | } |
213 | | |
214 | | // Non-standard, just a convenient way to get from three Optional<bool> to PropertyAttributes. |
215 | | PropertyAttributes PropertyDescriptor::attributes() const |
216 | 28.3k | { |
217 | 28.3k | u8 attributes = 0; |
218 | 28.3k | if (writable.value_or(false)) |
219 | 1.04k | attributes |= Attribute::Writable; |
220 | 28.3k | if (enumerable.value_or(false)) |
221 | 1.04k | attributes |= Attribute::Enumerable; |
222 | 28.3k | if (configurable.value_or(false)) |
223 | 28.3k | attributes |= Attribute::Configurable; |
224 | 28.3k | return { attributes }; |
225 | 28.3k | } |
226 | | |
227 | | } |