/src/serenity/Userland/Libraries/LibJS/Runtime/ReflectObject.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/Function.h> |
8 | | #include <LibJS/Runtime/AbstractOperations.h> |
9 | | #include <LibJS/Runtime/Array.h> |
10 | | #include <LibJS/Runtime/Error.h> |
11 | | #include <LibJS/Runtime/FunctionObject.h> |
12 | | #include <LibJS/Runtime/GlobalObject.h> |
13 | | #include <LibJS/Runtime/NativeFunction.h> |
14 | | #include <LibJS/Runtime/ReflectObject.h> |
15 | | |
16 | | namespace JS { |
17 | | |
18 | | JS_DEFINE_ALLOCATOR(ReflectObject); |
19 | | |
20 | | ReflectObject::ReflectObject(Realm& realm) |
21 | 0 | : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype()) |
22 | 0 | { |
23 | 0 | } |
24 | | |
25 | | void ReflectObject::initialize(Realm& realm) |
26 | 0 | { |
27 | 0 | auto& vm = this->vm(); |
28 | 0 | Base::initialize(realm); |
29 | 0 | u8 attr = Attribute::Writable | Attribute::Configurable; |
30 | 0 | define_native_function(realm, vm.names.apply, apply, 3, attr); |
31 | 0 | define_native_function(realm, vm.names.construct, construct, 2, attr); |
32 | 0 | define_native_function(realm, vm.names.defineProperty, define_property, 3, attr); |
33 | 0 | define_native_function(realm, vm.names.deleteProperty, delete_property, 2, attr); |
34 | 0 | define_native_function(realm, vm.names.get, get, 2, attr); |
35 | 0 | define_native_function(realm, vm.names.getOwnPropertyDescriptor, get_own_property_descriptor, 2, attr); |
36 | 0 | define_native_function(realm, vm.names.getPrototypeOf, get_prototype_of, 1, attr); |
37 | 0 | define_native_function(realm, vm.names.has, has, 2, attr); |
38 | 0 | define_native_function(realm, vm.names.isExtensible, is_extensible, 1, attr); |
39 | 0 | define_native_function(realm, vm.names.ownKeys, own_keys, 1, attr); |
40 | 0 | define_native_function(realm, vm.names.preventExtensions, prevent_extensions, 1, attr); |
41 | 0 | define_native_function(realm, vm.names.set, set, 3, attr); |
42 | 0 | define_native_function(realm, vm.names.setPrototypeOf, set_prototype_of, 2, attr); |
43 | | |
44 | | // 28.1.14 Reflect [ @@toStringTag ], https://tc39.es/ecma262/#sec-reflect-@@tostringtag |
45 | 0 | define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.Reflect.as_string()), Attribute::Configurable); |
46 | 0 | } |
47 | | |
48 | | // 28.1.1 Reflect.apply ( target, thisArgument, argumentsList ), https://tc39.es/ecma262/#sec-reflect.apply |
49 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::apply) |
50 | 0 | { |
51 | 0 | auto target = vm.argument(0); |
52 | 0 | auto this_argument = vm.argument(1); |
53 | 0 | auto arguments_list = vm.argument(2); |
54 | | |
55 | | // 1. If IsCallable(target) is false, throw a TypeError exception. |
56 | 0 | if (!target.is_function()) |
57 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAFunction, target.to_string_without_side_effects()); |
58 | | |
59 | | // 2. Let args be ? CreateListFromArrayLike(argumentsList). |
60 | 0 | auto args = TRY(create_list_from_array_like(vm, arguments_list)); |
61 | | |
62 | | // 3. Perform PrepareForTailCall(). |
63 | | // 4. Return ? Call(target, thisArgument, args). |
64 | 0 | return TRY(call(vm, target.as_function(), this_argument, args.span())); |
65 | 0 | } |
66 | | |
67 | | // 28.1.2 Reflect.construct ( target, argumentsList [ , newTarget ] ), https://tc39.es/ecma262/#sec-reflect.construct |
68 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::construct) |
69 | 0 | { |
70 | 0 | auto target = vm.argument(0); |
71 | 0 | auto arguments_list = vm.argument(1); |
72 | 0 | auto new_target = vm.argument(2); |
73 | | |
74 | | // 1. If IsConstructor(target) is false, throw a TypeError exception. |
75 | 0 | if (!target.is_constructor()) |
76 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, target.to_string_without_side_effects()); |
77 | | |
78 | | // 2. If newTarget is not present, set newTarget to target. |
79 | 0 | if (vm.argument_count() < 3) |
80 | 0 | new_target = target; |
81 | | // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception. |
82 | 0 | else if (!new_target.is_constructor()) |
83 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, new_target.to_string_without_side_effects()); |
84 | | |
85 | | // 4. Let args be ? CreateListFromArrayLike(argumentsList). |
86 | 0 | auto args = TRY(create_list_from_array_like(vm, arguments_list)); |
87 | | |
88 | | // 5. Return ? Construct(target, args, newTarget). |
89 | 0 | return TRY(JS::construct(vm, target.as_function(), args.span(), &new_target.as_function())); |
90 | 0 | } |
91 | | |
92 | | // 28.1.3 Reflect.defineProperty ( target, propertyKey, attributes ), https://tc39.es/ecma262/#sec-reflect.defineproperty |
93 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::define_property) |
94 | 0 | { |
95 | 0 | auto target = vm.argument(0); |
96 | 0 | auto property_key = vm.argument(1); |
97 | 0 | auto attributes = vm.argument(2); |
98 | | |
99 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
100 | 0 | if (!target.is_object()) |
101 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
102 | | |
103 | | // 2. Let key be ? ToPropertyKey(propertyKey). |
104 | 0 | auto key = TRY(property_key.to_property_key(vm)); |
105 | | |
106 | | // 3. Let desc be ? ToPropertyDescriptor(attributes). |
107 | 0 | auto descriptor = TRY(to_property_descriptor(vm, attributes)); |
108 | | |
109 | | // 4. Return ? target.[[DefineOwnProperty]](key, desc). |
110 | 0 | return Value(TRY(target.as_object().internal_define_own_property(key, descriptor))); |
111 | 0 | } |
112 | | |
113 | | // 28.1.4 Reflect.deleteProperty ( target, propertyKey ), https://tc39.es/ecma262/#sec-reflect.deleteproperty |
114 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::delete_property) |
115 | 0 | { |
116 | 0 | auto target = vm.argument(0); |
117 | 0 | auto property_key = vm.argument(1); |
118 | | |
119 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
120 | 0 | if (!target.is_object()) |
121 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
122 | | |
123 | | // 2. Let key be ? ToPropertyKey(propertyKey). |
124 | 0 | auto key = TRY(property_key.to_property_key(vm)); |
125 | | |
126 | | // 3. Return ? target.[[Delete]](key). |
127 | 0 | return Value(TRY(target.as_object().internal_delete(key))); |
128 | 0 | } |
129 | | |
130 | | // 28.1.5 Reflect.get ( target, propertyKey [ , receiver ] ), https://tc39.es/ecma262/#sec-reflect.get |
131 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get) |
132 | 0 | { |
133 | 0 | auto target = vm.argument(0); |
134 | 0 | auto property_key = vm.argument(1); |
135 | 0 | auto receiver = vm.argument(2); |
136 | | |
137 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
138 | 0 | if (!target.is_object()) |
139 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
140 | | |
141 | | // 2. Let key be ? ToPropertyKey(propertyKey). |
142 | 0 | auto key = TRY(property_key.to_property_key(vm)); |
143 | | |
144 | | // 3. If receiver is not present, then |
145 | 0 | if (vm.argument_count() < 3) { |
146 | | // a. Set receiver to target. |
147 | 0 | receiver = target; |
148 | 0 | } |
149 | | |
150 | | // 4. Return ? target.[[Get]](key, receiver). |
151 | 0 | return TRY(target.as_object().internal_get(key, receiver)); |
152 | 0 | } |
153 | | |
154 | | // 28.1.6 Reflect.getOwnPropertyDescriptor ( target, propertyKey ), https://tc39.es/ecma262/#sec-reflect.getownpropertydescriptor |
155 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get_own_property_descriptor) |
156 | 0 | { |
157 | 0 | auto target = vm.argument(0); |
158 | 0 | auto property_key = vm.argument(1); |
159 | | |
160 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
161 | 0 | if (!target.is_object()) |
162 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
163 | | |
164 | | // 2. Let key be ? ToPropertyKey(propertyKey). |
165 | 0 | auto key = TRY(property_key.to_property_key(vm)); |
166 | | |
167 | | // 3. Let desc be ? target.[[GetOwnProperty]](key). |
168 | 0 | auto descriptor = TRY(target.as_object().internal_get_own_property(key)); |
169 | | |
170 | | // 4. Return FromPropertyDescriptor(desc). |
171 | 0 | return from_property_descriptor(vm, descriptor); |
172 | 0 | } |
173 | | |
174 | | // 28.1.7 Reflect.getPrototypeOf ( target ), https://tc39.es/ecma262/#sec-reflect.getprototypeof |
175 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get_prototype_of) |
176 | 0 | { |
177 | 0 | auto target = vm.argument(0); |
178 | | |
179 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
180 | 0 | if (!target.is_object()) |
181 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
182 | | |
183 | | // 2. Return ? target.[[GetPrototypeOf]](). |
184 | 0 | return TRY(target.as_object().internal_get_prototype_of()); |
185 | 0 | } |
186 | | |
187 | | // 28.1.8 Reflect.has ( target, propertyKey ), https://tc39.es/ecma262/#sec-reflect.has |
188 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::has) |
189 | 0 | { |
190 | 0 | auto target = vm.argument(0); |
191 | 0 | auto property_key = vm.argument(1); |
192 | | |
193 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
194 | 0 | if (!target.is_object()) |
195 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
196 | | |
197 | | // 2. Let key be ? ToPropertyKey(propertyKey). |
198 | 0 | auto key = TRY(property_key.to_property_key(vm)); |
199 | | |
200 | | // 3. Return ? target.[[HasProperty]](key). |
201 | 0 | return Value(TRY(target.as_object().internal_has_property(key))); |
202 | 0 | } |
203 | | |
204 | | // 28.1.9 Reflect.isExtensible ( target ), https://tc39.es/ecma262/#sec-reflect.isextensible |
205 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::is_extensible) |
206 | 0 | { |
207 | 0 | auto target = vm.argument(0); |
208 | | |
209 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
210 | 0 | if (!target.is_object()) |
211 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
212 | | |
213 | | // 2. Return ? target.[[IsExtensible]](). |
214 | 0 | return Value(TRY(target.as_object().internal_is_extensible())); |
215 | 0 | } |
216 | | |
217 | | // 28.1.10 Reflect.ownKeys ( target ), https://tc39.es/ecma262/#sec-reflect.ownkeys |
218 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::own_keys) |
219 | 0 | { |
220 | 0 | auto& realm = *vm.current_realm(); |
221 | |
|
222 | 0 | auto target = vm.argument(0); |
223 | | |
224 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
225 | 0 | if (!target.is_object()) |
226 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
227 | | |
228 | | // 2. Let keys be ? target.[[OwnPropertyKeys]](). |
229 | 0 | auto keys = TRY(target.as_object().internal_own_property_keys()); |
230 | | |
231 | | // 3. Return CreateArrayFromList(keys). |
232 | 0 | return Array::create_from(realm, keys); |
233 | 0 | } |
234 | | |
235 | | // 28.1.11 Reflect.preventExtensions ( target ), https://tc39.es/ecma262/#sec-reflect.preventextensions |
236 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::prevent_extensions) |
237 | 0 | { |
238 | 0 | auto target = vm.argument(0); |
239 | | |
240 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
241 | 0 | if (!target.is_object()) |
242 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
243 | | |
244 | | // 2. Return ? target.[[PreventExtensions]](). |
245 | 0 | return Value(TRY(target.as_object().internal_prevent_extensions())); |
246 | 0 | } |
247 | | |
248 | | // 28.1.12 Reflect.set ( target, propertyKey, V [ , receiver ] ), https://tc39.es/ecma262/#sec-reflect.set |
249 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set) |
250 | 0 | { |
251 | 0 | auto target = vm.argument(0); |
252 | 0 | auto property_key = vm.argument(1); |
253 | 0 | auto value = vm.argument(2); |
254 | 0 | auto receiver = vm.argument(3); |
255 | | |
256 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
257 | 0 | if (!target.is_object()) |
258 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
259 | | |
260 | | // 2. Let key be ? ToPropertyKey(propertyKey). |
261 | 0 | auto key = TRY(property_key.to_property_key(vm)); |
262 | | |
263 | | // 3. If receiver is not present, then |
264 | 0 | if (vm.argument_count() < 4) { |
265 | | // a. Set receiver to target. |
266 | 0 | receiver = target; |
267 | 0 | } |
268 | | |
269 | | // 4. Return ? target.[[Set]](key, V, receiver). |
270 | 0 | return Value(TRY(target.as_object().internal_set(key, value, receiver))); |
271 | 0 | } |
272 | | |
273 | | // 28.1.13 Reflect.setPrototypeOf ( target, proto ), https://tc39.es/ecma262/#sec-reflect.setprototypeof |
274 | | JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set_prototype_of) |
275 | 0 | { |
276 | 0 | auto target = vm.argument(0); |
277 | 0 | auto proto = vm.argument(1); |
278 | | |
279 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
280 | 0 | if (!target.is_object()) |
281 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects()); |
282 | | |
283 | | // 2. If Type(proto) is not Object and proto is not null, throw a TypeError exception. |
284 | 0 | if (!proto.is_object() && !proto.is_null()) |
285 | 0 | return vm.throw_completion<TypeError>(ErrorType::ObjectPrototypeWrongType); |
286 | | |
287 | | // 3. Return ? target.[[SetPrototypeOf]](proto). |
288 | 0 | return Value(TRY(target.as_object().internal_set_prototype_of(proto.is_null() ? nullptr : &proto.as_object()))); |
289 | 0 | } |
290 | | |
291 | | } |