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 
21 namespace panda::ecmascript::builtins {
22 // ecma 26.1.1 Reflect.apply (target, thisArgument, argumentsList)
ReflectApply(EcmaRuntimeCallInfo *argv)23 JSTaggedValue 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 
ReflectApplyInternal(JSThread *thread, JSHandle<JSTaggedValue> target, JSHandle<JSTaggedValue> thisArgument, JSHandle<JSTaggedValue> argumentsList)35 JSTaggedValue 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])
ReflectConstruct(EcmaRuntimeCallInfo *argv)61 JSTaggedValue 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 
ReflectConstructInternal(JSThread *thread, JSHandle<JSTaggedValue> target, JSHandle<TaggedArray> args, JSHandle<JSTaggedValue> newTarget)88 JSTaggedValue 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)
ReflectDefineProperty(EcmaRuntimeCallInfo *argv)102 JSTaggedValue 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)
ReflectDeleteProperty(EcmaRuntimeCallInfo *argv)126 JSTaggedValue 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])
ReflectGet(EcmaRuntimeCallInfo *argv)145 JSTaggedValue 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 )
ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo *argv)170 JSTaggedValue 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)
ReflectGetPrototypeOf(EcmaRuntimeCallInfo *argv)196 JSTaggedValue 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)
ReflectHas(EcmaRuntimeCallInfo *argv)212 JSTaggedValue 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 
ReflectHasInternal(JSThread *thread, JSHandle<JSTaggedValue> target, JSHandle<JSTaggedValue> key)223 JSTaggedValue 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)
ReflectIsExtensible(EcmaRuntimeCallInfo *argv)239 JSTaggedValue 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)
ReflectOwnKeys(EcmaRuntimeCallInfo *argv)254 JSTaggedValue 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)
ReflectPreventExtensions(EcmaRuntimeCallInfo *argv)274 JSTaggedValue 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])
ReflectSet(EcmaRuntimeCallInfo *argv)291 JSTaggedValue 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)
ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv)317 JSTaggedValue 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