1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/builtins/builtins_reflect.h"
17
18#include "ecmascript/interpreter/interpreter.h"
19#include "ecmascript/js_object-inl.h"
20
21namespace panda::ecmascript::builtins {
22// ecma 26.1.1 Reflect.apply (target, thisArgument, argumentsList)
23JSTaggedValue BuiltinsReflect::ReflectApply(EcmaRuntimeCallInfo *argv)
24{
25    ASSERT(argv);
26    BUILTINS_API_TRACE(argv->GetThread(), Reflect, Apply);
27    JSThread *thread = argv->GetThread();
28    [[maybe_unused]] EcmaHandleScope handleScope(thread);
29    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
30    JSHandle<JSTaggedValue> thisArgument = GetCallArg(argv, 1);
31    JSHandle<JSTaggedValue> argumentsList = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
32    return ReflectApplyInternal(thread, target, thisArgument, argumentsList);
33}
34
35JSTaggedValue BuiltinsReflect::ReflectApplyInternal(JSThread *thread, JSHandle<JSTaggedValue> target,
36                                                    JSHandle<JSTaggedValue> thisArgument,
37                                                    JSHandle<JSTaggedValue> argumentsList)
38{
39    [[maybe_unused]] EcmaHandleScope handleScope(thread);
40    // 1. If IsCallable(target) is false, throw a TypeError exception.
41    if (!target->IsCallable()) {
42        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.apply target is not callable", JSTaggedValue::Exception());
43    }
44    // 2. Let args be ? CreateListFromArrayLike(argumentsList).
45    JSHandle<JSTaggedValue> argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList);
46    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
47    JSHandle<TaggedArray> args = JSHandle<TaggedArray>::Cast(argOrAbrupt);
48
49    // 3. Perform PrepareForTailCall().
50    // 4. Return ? Call(target, thisArgument, args).
51    const uint32_t argsLength = args->GetLength();
52    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
53    EcmaRuntimeCallInfo *info =
54        EcmaInterpreter::NewRuntimeCallInfo(thread, target, thisArgument, undefined, argsLength);
55    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
56    info->SetCallArg(argsLength, args);
57    return JSFunction::Call(info);
58}
59
60// ecma 26.1.2 Reflect.construct (target, argumentsList [ , newTarget])
61JSTaggedValue BuiltinsReflect::ReflectConstruct(EcmaRuntimeCallInfo *argv)
62{
63    ASSERT(argv);
64    BUILTINS_API_TRACE(argv->GetThread(), Reflect, Constructor);
65    JSThread *thread = argv->GetThread();
66    [[maybe_unused]] EcmaHandleScope handleScope(thread);
67    // 1. If IsConstructor(target) is false, throw a TypeError exception.
68    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
69    if (!target->IsConstructor()) {
70        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct target is not constructor", JSTaggedValue::Exception());
71    }
72    // 2. If newTarget is not present, set newTarget to target.
73    JSHandle<JSTaggedValue> newTarget =
74        argv->GetArgsNumber() > 2 ? GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD) : target;  // 2: num args
75    // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception.
76    if (!newTarget->IsConstructor()) {
77        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct newTarget is present, but not constructor",
78                                    JSTaggedValue::Exception());
79    }
80    // 4. Let args be ? CreateListFromArrayLike(argumentsList).
81    JSHandle<JSTaggedValue> argumentsList = GetCallArg(argv, 1);
82    JSHandle<JSTaggedValue> argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList);
83    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
84    JSHandle<TaggedArray> args = JSHandle<TaggedArray>::Cast(argOrAbrupt);
85    return ReflectConstructInternal(thread, target, args, newTarget);
86}
87
88JSTaggedValue BuiltinsReflect::ReflectConstructInternal(JSThread *thread, JSHandle<JSTaggedValue> target,
89                                                        JSHandle<TaggedArray> args, JSHandle<JSTaggedValue> newTarget)
90{
91    [[maybe_unused]] EcmaHandleScope handleScope(thread);
92    // 5. Return ? Construct(target, args, newTarget).
93    const uint32_t argsLength = args->GetLength();
94    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
95    EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, target, undefined, newTarget, argsLength);
96    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
97    info->SetCallArg(argsLength, args);
98    return JSFunction::Construct(info);
99}
100
101// ecma 26.1.3 Reflect.defineProperty (target, propertyKey, attributes)
102JSTaggedValue BuiltinsReflect::ReflectDefineProperty(EcmaRuntimeCallInfo *argv)
103{
104    ASSERT(argv);
105    BUILTINS_API_TRACE(argv->GetThread(), Reflect, DefineProperty);
106    JSThread *thread = argv->GetThread();
107    [[maybe_unused]] EcmaHandleScope handleScope(thread);
108    // 1. If Type(target) is not Object, throw a TypeError exception.
109    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
110    if (!target->IsECMAObject()) {
111        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.defineProperty target is not object", JSTaggedValue::Exception());
112    }
113    // 2. Let key be ? ToPropertyKey(propertyKey).
114    JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
115    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
116    // 3. Let desc be ? ToPropertyDescriptor(attributes).
117    JSHandle<JSTaggedValue> attributes = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
118    PropertyDescriptor desc(thread);
119    JSObject::ToPropertyDescriptor(thread, attributes, desc);
120    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
121    // 4. Return ? target.[[DefineOwnProperty]](key, desc).
122    return GetTaggedBoolean(JSTaggedValue::DefineOwnProperty(thread, target, key, desc));
123}
124
125// ecma 21.1.4 Reflect.deleteProperty (target, propertyKey)
126JSTaggedValue BuiltinsReflect::ReflectDeleteProperty(EcmaRuntimeCallInfo *argv)
127{
128    ASSERT(argv);
129    BUILTINS_API_TRACE(argv->GetThread(), Reflect, DeleteProperty);
130    JSThread *thread = argv->GetThread();
131    [[maybe_unused]] EcmaHandleScope handleScope(thread);
132    // 1. If Type(target) is not Object, throw a TypeError exception.
133    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
134    if (!target->IsECMAObject()) {
135        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.deleteProperty target is not object", JSTaggedValue::Exception());
136    }
137    // 2. Let key be ? ToPropertyKey(propertyKey).
138    JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
139    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
140    // 3. Return ? target.[[Delete]](key).
141    return GetTaggedBoolean(JSTaggedValue::DeleteProperty(thread, target, key));
142}
143
144// ecma 26.1.5 Reflect.get (target, propertyKey [ , receiver])
145JSTaggedValue BuiltinsReflect::ReflectGet(EcmaRuntimeCallInfo *argv)
146{
147    ASSERT(argv);
148    BUILTINS_API_TRACE(argv->GetThread(), Reflect, Get);
149    JSThread *thread = argv->GetThread();
150    [[maybe_unused]] EcmaHandleScope handleScope(thread);
151    // 1. If Type(target) is not Object, throw a TypeError exception.
152    JSHandle<JSTaggedValue> val = GetCallArg(argv, 0);
153    if (!val->IsECMAObject()) {
154        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception());
155    }
156    // 2. Let key be ? ToPropertyKey(propertyKey).
157    JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
158    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
159    // 3. If receiver is not present, then
160    //     a. Set receiver to target.
161    // 4. Return ? target.[[Get]](key, receiver).
162    if (argv->GetArgsNumber() == 2) {  // 2: 2 means that there are 2 args in total
163        return JSTaggedValue::GetProperty(thread, val, key).GetValue().GetTaggedValue();
164    }
165    JSHandle<JSTaggedValue> receiver = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
166    return JSTaggedValue::GetProperty(thread, val, key, receiver).GetValue().GetTaggedValue();
167}
168
169// ecma 26.1.6 Reflect.getOwnPropertyDescriptor ( target, propertyKey )
170JSTaggedValue BuiltinsReflect::ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo *argv)
171{
172    ASSERT(argv);
173    BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetOwnPropertyDescriptor);
174    JSThread *thread = argv->GetThread();
175    [[maybe_unused]] EcmaHandleScope handleScope(thread);
176    // 1. If Type(target) is not Object, throw a TypeError exception.
177    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
178    if (!target->IsECMAObject()) {
179        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getOwnPropertyDescriptor target is not object",
180                                    JSTaggedValue::Exception());
181    }
182    // 2. Let key be ? ToPropertyKey(propertyKey).
183    JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
184    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
185    // 3. Let desc be ? target.[[GetOwnProperty]](key).
186    PropertyDescriptor desc(thread);
187    if (!JSTaggedValue::GetOwnProperty(thread, target, key, desc)) {
188        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
189    }
190    // 4. Return FromPropertyDescriptor(desc).
191    JSHandle<JSTaggedValue> res = JSObject::FromPropertyDescriptor(thread, desc);
192    return res.GetTaggedValue();
193}
194
195// ecma 21.1.7 Reflect.getPrototypeOf (target)
196JSTaggedValue BuiltinsReflect::ReflectGetPrototypeOf(EcmaRuntimeCallInfo *argv)
197{
198    ASSERT(argv);
199    BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetPrototypeOf);
200    JSThread *thread = argv->GetThread();
201    [[maybe_unused]] EcmaHandleScope handleScope(thread);
202    // 1. If Type(target) is not Object, throw a TypeError exception.
203    JSHandle<JSTaggedValue> val = GetCallArg(argv, 0);
204    if (!val->IsECMAObject()) {
205        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getPrototypeOf target is not object", JSTaggedValue::Exception());
206    }
207    // 2. Return ? target.[[GetPrototypeOf]]().
208    return JSTaggedValue::GetPrototype(thread, val);
209}
210
211// ecma 26.1.8 Reflect.has (target, propertyKey)
212JSTaggedValue BuiltinsReflect::ReflectHas(EcmaRuntimeCallInfo *argv)
213{
214    ASSERT(argv);
215    BUILTINS_API_TRACE(argv->GetThread(), Reflect, Has);
216    JSThread *thread = argv->GetThread();
217    [[maybe_unused]] EcmaHandleScope handleScope(thread);
218    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
219    JSHandle<JSTaggedValue> key = GetCallArg(argv, 1);
220    return ReflectHasInternal(thread, target, key);
221}
222
223JSTaggedValue BuiltinsReflect::ReflectHasInternal(JSThread *thread, JSHandle<JSTaggedValue> target,
224                                                  JSHandle<JSTaggedValue> key)
225{
226    [[maybe_unused]] EcmaHandleScope handleScope(thread);
227    // 1. If Type(target) is not Object, throw a TypeError exception.
228    if (!target->IsECMAObject()) {
229        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.has target is not object", JSTaggedValue::Exception());
230    }
231    // 2. Let key be ? ToPropertyKey(propertyKey).
232    JSHandle<JSTaggedValue> propertyKey = JSTaggedValue::ToPropertyKey(thread, key);
233    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
234    // 3. Return ? target.[[HasProperty]](key).
235    return GetTaggedBoolean(JSTaggedValue::HasProperty(thread, target, propertyKey));
236}
237
238// ecma 26.1.9  Reflect.isExtensible (target)
239JSTaggedValue BuiltinsReflect::ReflectIsExtensible(EcmaRuntimeCallInfo *argv)
240{
241    ASSERT(argv);
242    JSThread *thread = argv->GetThread();
243    [[maybe_unused]] EcmaHandleScope handleScope(thread);
244    // 1. If Type(target) is not Object, throw a TypeError exception.
245    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
246    if (!target->IsECMAObject()) {
247        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.isExtensible target is not object", JSTaggedValue::Exception());
248    }
249    // 2. Return ? target.[[IsExtensible]]().
250    return GetTaggedBoolean(target->IsExtensible(thread));
251}
252
253// ecma 26.1.10 Reflect.ownKeys (target)
254JSTaggedValue BuiltinsReflect::ReflectOwnKeys(EcmaRuntimeCallInfo *argv)
255{
256    ASSERT(argv);
257    BUILTINS_API_TRACE(argv->GetThread(), Reflect, OwnKeys);
258    JSThread *thread = argv->GetThread();
259    [[maybe_unused]] EcmaHandleScope handleScope(thread);
260    // 1. If Type(target) is not Object, throw a TypeError exception.
261    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
262    if (!target->IsECMAObject()) {
263        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.ownKeys target is not object", JSTaggedValue::Exception());
264    }
265    // 2. Let keys be ? target.[[OwnPropertyKeys]]().
266    JSHandle<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, target);
267    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
268    // 3. Return CreateArrayFromList(keys).
269    JSHandle<JSArray> result = JSArray::CreateArrayFromList(thread, keys);
270    return result.GetTaggedValue();
271}
272
273// ecma 26.1.11 Reflect.preventExtensions (target)
274JSTaggedValue BuiltinsReflect::ReflectPreventExtensions(EcmaRuntimeCallInfo *argv)
275{
276    ASSERT(argv);
277    BUILTINS_API_TRACE(argv->GetThread(), Reflect, PreventExtensions);
278    JSThread *thread = argv->GetThread();
279    [[maybe_unused]] EcmaHandleScope handleScope(thread);
280    // 1. If Type(target) is not Object, throw a TypeError exception.
281    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
282    if (!target->IsECMAObject()) {
283        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.preventExtensions target is not object",
284                                    JSTaggedValue::Exception());
285    }
286    // 2. Return ? target.[[PreventExtensions]]().
287    return GetTaggedBoolean(JSTaggedValue::PreventExtensions(thread, target));
288}
289
290// ecma 26.1.12 Reflect.set (target, propertyKey, V [ , receiver])
291JSTaggedValue BuiltinsReflect::ReflectSet(EcmaRuntimeCallInfo *argv)
292{
293    ASSERT(argv);
294    BUILTINS_API_TRACE(argv->GetThread(), Reflect, Set);
295    JSThread *thread = argv->GetThread();
296    [[maybe_unused]] EcmaHandleScope handleScope(thread);
297    // 1. If Type(target) is not Object, throw a TypeError exception.
298    JSHandle<JSTaggedValue> targetVal = GetCallArg(argv, 0);
299    if (!targetVal->IsECMAObject()) {
300        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.set target is not object", JSTaggedValue::Exception());
301    }
302    // 2. Let key be ? ToPropertyKey(propertyKey).
303    JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
304    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
305    JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
306    // 3. If receiver is not present, then
307    //     a. Set receiver to target.
308    // 4. Return ? target.[[Set]](key, receiver).
309    if (argv->GetArgsNumber() <= BuiltinsBase::ArgsPosition::FOURTH) {
310        return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value));
311    }
312    JSHandle<JSTaggedValue> receiver = GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
313    return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value, receiver));
314}
315
316// ecma 26.1.13  Reflect.setPrototypeOf (target, proto)
317JSTaggedValue BuiltinsReflect::ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv)
318{
319    ASSERT(argv);
320    BUILTINS_API_TRACE(argv->GetThread(), Reflect, SetPrototypeOf);
321    JSThread *thread = argv->GetThread();
322    [[maybe_unused]] EcmaHandleScope handleScope(thread);
323    // 1. If Type(target) is not Object, throw a TypeError exception.
324    JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
325    if (!target->IsECMAObject()) {
326        THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.setPrototypeOf target is not object", JSTaggedValue::Exception());
327    }
328    // 2. If Type(proto) is not Object and proto is not null, throw a TypeError exception.
329    JSHandle<JSTaggedValue> proto = GetCallArg(argv, 1);
330    if (!proto->IsECMAObject() && !proto->IsNull()) {
331        THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: proto is neither Object nor Null",
332                                    JSTaggedValue::Exception());
333    }
334    // 3. Return ? target.[[SetPrototypeOf]](proto).
335    return GetTaggedBoolean(JSTaggedValue::SetPrototype(thread, target, proto));
336}
337}  // namespace panda::ecmascript::builtins
338