Coverage Report

Created: 2025-08-28 06:26

/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
}