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