1 /*
2 * Copyright (c) 2021 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_tagged_value-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 // 1. If IsCallable(target) is false, throw a TypeError exception.
30 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
31 if (!target->IsCallable()) {
32 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.apply target is not callable", JSTaggedValue::Exception());
33 }
34 // 2. Let args be ? CreateListFromArrayLike(argumentsList).
35 JSHandle<JSTaggedValue> thisArgument = GetCallArg(argv, 1);
36 JSHandle<JSTaggedValue> argumentsList = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
37 JSHandle<JSTaggedValue> argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList);
38 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
39 JSHandle<TaggedArray> args = JSHandle<TaggedArray>::Cast(argOrAbrupt);
40
41 // 3. Perform PrepareForTailCall().
42 // 4. Return ? Call(target, thisArgument, args).
43 const uint32_t argsLength = args->GetLength();
44 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
45 EcmaRuntimeCallInfo *info =
46 EcmaInterpreter::NewRuntimeCallInfo(thread, target, thisArgument, undefined, argsLength);
47 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
48 info->SetCallArg(argsLength, args);
49 return JSFunction::Call(info);
50 }
51
52 // ecma 26.1.2 Reflect.construct (target, argumentsList [ , newTarget])
ReflectConstruct(EcmaRuntimeCallInfo * argv)53 JSTaggedValue BuiltinsReflect::ReflectConstruct(EcmaRuntimeCallInfo *argv)
54 {
55 ASSERT(argv);
56 BUILTINS_API_TRACE(argv->GetThread(), Reflect, Constructor);
57 JSThread *thread = argv->GetThread();
58 [[maybe_unused]] EcmaHandleScope handleScope(thread);
59 // 1. If IsConstructor(target) is false, throw a TypeError exception.
60 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
61 if (!target->IsConstructor()) {
62 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct target is not constructor", JSTaggedValue::Exception());
63 }
64 // 2. If newTarget is not present, set newTarget to target.
65 JSHandle<JSTaggedValue> newTarget =
66 argv->GetArgsNumber() > 2 ? GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD) : target; // 2: num args
67 // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception.
68 if (!newTarget->IsConstructor()) {
69 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct newTarget is present, but not constructor",
70 JSTaggedValue::Exception());
71 }
72 // 4. Let args be ? CreateListFromArrayLike(argumentsList).
73 JSHandle<JSTaggedValue> argumentsList = GetCallArg(argv, 1);
74 JSHandle<JSTaggedValue> argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList);
75 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
76 JSHandle<TaggedArray> args = JSHandle<TaggedArray>::Cast(argOrAbrupt);
77 // 5. Return ? Construct(target, args, newTarget).
78 const uint32_t argsLength = args->GetLength();
79 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
80 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, target, undefined, newTarget, argsLength);
81 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
82 info->SetCallArg(argsLength, args);
83 return JSFunction::Construct(info);
84 }
85
86 // ecma 26.1.3 Reflect.defineProperty (target, propertyKey, attributes)
ReflectDefineProperty(EcmaRuntimeCallInfo * argv)87 JSTaggedValue BuiltinsReflect::ReflectDefineProperty(EcmaRuntimeCallInfo *argv)
88 {
89 ASSERT(argv);
90 BUILTINS_API_TRACE(argv->GetThread(), Reflect, DefineProperty);
91 JSThread *thread = argv->GetThread();
92 [[maybe_unused]] EcmaHandleScope handleScope(thread);
93 // 1. If Type(target) is not Object, throw a TypeError exception.
94 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
95 if (!target->IsECMAObject()) {
96 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.defineProperty target is not object", JSTaggedValue::Exception());
97 }
98 // 2. Let key be ? ToPropertyKey(propertyKey).
99 JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
100 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
101 // 3. Let desc be ? ToPropertyDescriptor(attributes).
102 JSHandle<JSTaggedValue> attributes = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
103 PropertyDescriptor desc(thread);
104 JSObject::ToPropertyDescriptor(thread, attributes, desc);
105 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
106 // 4. Return ? target.[[DefineOwnProperty]](key, desc).
107 return GetTaggedBoolean(JSTaggedValue::DefineOwnProperty(thread, target, key, desc));
108 }
109
110 // ecma 21.1.4 Reflect.deleteProperty (target, propertyKey)
ReflectDeleteProperty(EcmaRuntimeCallInfo * argv)111 JSTaggedValue BuiltinsReflect::ReflectDeleteProperty(EcmaRuntimeCallInfo *argv)
112 {
113 ASSERT(argv);
114 BUILTINS_API_TRACE(argv->GetThread(), Reflect, DeleteProperty);
115 JSThread *thread = argv->GetThread();
116 [[maybe_unused]] EcmaHandleScope handleScope(thread);
117 // 1. If Type(target) is not Object, throw a TypeError exception.
118 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
119 if (!target->IsECMAObject()) {
120 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.deleteProperty target is not object", JSTaggedValue::Exception());
121 }
122 // 2. Let key be ? ToPropertyKey(propertyKey).
123 JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
124 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
125 // 3. Return ? target.[[Delete]](key).
126 return GetTaggedBoolean(JSTaggedValue::DeleteProperty(thread, target, key));
127 }
128
129 // ecma 26.1.5 Reflect.get (target, propertyKey [ , receiver])
ReflectGet(EcmaRuntimeCallInfo * argv)130 JSTaggedValue BuiltinsReflect::ReflectGet(EcmaRuntimeCallInfo *argv)
131 {
132 ASSERT(argv);
133 BUILTINS_API_TRACE(argv->GetThread(), Reflect, Get);
134 JSThread *thread = argv->GetThread();
135 [[maybe_unused]] EcmaHandleScope handleScope(thread);
136 // 1. If Type(target) is not Object, throw a TypeError exception.
137 JSHandle<JSTaggedValue> val = GetCallArg(argv, 0);
138 if (!val->IsECMAObject()) {
139 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception());
140 }
141 JSHandle<JSObject> target = JSHandle<JSObject>::Cast(val);
142 // 2. Let key be ? ToPropertyKey(propertyKey).
143 JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
144 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
145 // 3. If receiver is not present, then
146 // a. Set receiver to target.
147 // 4. Return ? target.[[Get]](key, receiver).
148 if (argv->GetArgsNumber() == 2) { // 2: 2 means that there are 2 args in total
149 return JSObject::GetProperty(thread, target, key).GetValue().GetTaggedValue();
150 }
151 JSHandle<JSTaggedValue> receiver = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
152 return JSObject::GetProperty(thread, val, key, receiver).GetValue().GetTaggedValue();
153 }
154
155 // ecma 26.1.6 Reflect.getOwnPropertyDescriptor ( target, propertyKey )
ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo * argv)156 JSTaggedValue BuiltinsReflect::ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo *argv)
157 {
158 ASSERT(argv);
159 BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetOwnPropertyDescriptor);
160 JSThread *thread = argv->GetThread();
161 [[maybe_unused]] EcmaHandleScope handleScope(thread);
162 // 1. If Type(target) is not Object, throw a TypeError exception.
163 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
164 if (!target->IsECMAObject()) {
165 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getOwnPropertyDescriptor target is not object",
166 JSTaggedValue::Exception());
167 }
168 // 2. Let key be ? ToPropertyKey(propertyKey).
169 JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
170 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
171 // 3. Let desc be ? target.[[GetOwnProperty]](key).
172 PropertyDescriptor desc(thread);
173 if (!JSTaggedValue::GetOwnProperty(thread, target, key, desc)) {
174 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
175 }
176 // 4. Return FromPropertyDescriptor(desc).
177 JSHandle<JSTaggedValue> res = JSObject::FromPropertyDescriptor(thread, desc);
178 return res.GetTaggedValue();
179 }
180
181 // ecma 21.1.7 Reflect.getPrototypeOf (target)
ReflectGetPrototypeOf(EcmaRuntimeCallInfo * argv)182 JSTaggedValue BuiltinsReflect::ReflectGetPrototypeOf(EcmaRuntimeCallInfo *argv)
183 {
184 ASSERT(argv);
185 BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetPrototypeOf);
186 JSThread *thread = argv->GetThread();
187 [[maybe_unused]] EcmaHandleScope handleScope(thread);
188 // 1. If Type(target) is not Object, throw a TypeError exception.
189 JSHandle<JSTaggedValue> val = GetCallArg(argv, 0);
190 if (!val->IsECMAObject()) {
191 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getPrototypeOf target is not object", JSTaggedValue::Exception());
192 }
193 // 2. Return ? target.[[GetPrototypeOf]]().
194 return JSTaggedValue::GetPrototype(thread, val);
195 }
196
197 // ecma 26.1.8 Reflect.has (target, propertyKey)
ReflectHas(EcmaRuntimeCallInfo * argv)198 JSTaggedValue BuiltinsReflect::ReflectHas(EcmaRuntimeCallInfo *argv)
199 {
200 ASSERT(argv);
201 BUILTINS_API_TRACE(argv->GetThread(), Reflect, Has);
202 JSThread *thread = argv->GetThread();
203 [[maybe_unused]] EcmaHandleScope handleScope(thread);
204 // 1. If Type(target) is not Object, throw a TypeError exception.
205 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
206 if (!target->IsECMAObject()) {
207 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.has target is not object", JSTaggedValue::Exception());
208 }
209 // 2. Let key be ? ToPropertyKey(propertyKey).
210 JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
211 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
212 // 3. Return ? target.[[HasProperty]](key).
213 return GetTaggedBoolean(JSTaggedValue::HasProperty(thread, target, key));
214 }
215
216 // ecma 26.1.9 Reflect.isExtensible (target)
ReflectIsExtensible(EcmaRuntimeCallInfo * argv)217 JSTaggedValue BuiltinsReflect::ReflectIsExtensible(EcmaRuntimeCallInfo *argv)
218 {
219 ASSERT(argv);
220 JSThread *thread = argv->GetThread();
221 [[maybe_unused]] EcmaHandleScope handleScope(thread);
222 // 1. If Type(target) is not Object, throw a TypeError exception.
223 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
224 if (!target->IsECMAObject()) {
225 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.isExtensible target is not object", JSTaggedValue::Exception());
226 }
227 // 2. Return ? target.[[IsExtensible]]().
228 return GetTaggedBoolean(target->IsExtensible(thread));
229 }
230
231 // ecma 26.1.10 Reflect.ownKeys (target)
ReflectOwnKeys(EcmaRuntimeCallInfo * argv)232 JSTaggedValue BuiltinsReflect::ReflectOwnKeys(EcmaRuntimeCallInfo *argv)
233 {
234 ASSERT(argv);
235 BUILTINS_API_TRACE(argv->GetThread(), Reflect, OwnKeys);
236 JSThread *thread = argv->GetThread();
237 [[maybe_unused]] EcmaHandleScope handleScope(thread);
238 // 1. If Type(target) is not Object, throw a TypeError exception.
239 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
240 if (!target->IsECMAObject()) {
241 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.ownKeys target is not object", JSTaggedValue::Exception());
242 }
243 // 2. Let keys be ? target.[[OwnPropertyKeys]]().
244 JSHandle<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, target);
245 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
246 // 3. Return CreateArrayFromList(keys).
247 JSHandle<JSArray> result = JSArray::CreateArrayFromList(thread, keys);
248 return result.GetTaggedValue();
249 }
250
251 // ecma 26.1.11 Reflect.preventExtensions (target)
ReflectPreventExtensions(EcmaRuntimeCallInfo * argv)252 JSTaggedValue BuiltinsReflect::ReflectPreventExtensions(EcmaRuntimeCallInfo *argv)
253 {
254 ASSERT(argv);
255 BUILTINS_API_TRACE(argv->GetThread(), Reflect, PreventExtensions);
256 JSThread *thread = argv->GetThread();
257 [[maybe_unused]] EcmaHandleScope handleScope(thread);
258 // 1. If Type(target) is not Object, throw a TypeError exception.
259 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
260 if (!target->IsECMAObject()) {
261 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.preventExtensions target is not object",
262 JSTaggedValue::Exception());
263 }
264 // 2. Return ? target.[[PreventExtensions]]().
265 return GetTaggedBoolean(JSTaggedValue::PreventExtensions(thread, target));
266 }
267
268 // ecma 26.1.12 Reflect.set (target, propertyKey, V [ , receiver])
ReflectSet(EcmaRuntimeCallInfo * argv)269 JSTaggedValue BuiltinsReflect::ReflectSet(EcmaRuntimeCallInfo *argv)
270 {
271 ASSERT(argv);
272 BUILTINS_API_TRACE(argv->GetThread(), Reflect, Set);
273 JSThread *thread = argv->GetThread();
274 [[maybe_unused]] EcmaHandleScope handleScope(thread);
275 // 1. If Type(target) is not Object, throw a TypeError exception.
276 JSHandle<JSTaggedValue> targetVal = GetCallArg(argv, 0);
277 if (!targetVal->IsECMAObject()) {
278 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception());
279 }
280 // 2. Let key be ? ToPropertyKey(propertyKey).
281 JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
282 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
283 JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
284 // 3. If receiver is not present, then
285 // a. Set receiver to target.
286 // 4. Return ? target.[[Set]](key, receiver).
287 if (argv->GetArgsNumber() == 3) { // 3: 3 means that there are three args in total
288 return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value));
289 }
290 JSHandle<JSTaggedValue> receiver = GetCallArg(argv, 3); // 3: 3 means the third arg
291 return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value, receiver));
292 }
293
294 // ecma 26.1.13 Reflect.setPrototypeOf (target, proto)
ReflectSetPrototypeOf(EcmaRuntimeCallInfo * argv)295 JSTaggedValue BuiltinsReflect::ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv)
296 {
297 ASSERT(argv);
298 BUILTINS_API_TRACE(argv->GetThread(), Reflect, SetPrototypeOf);
299 JSThread *thread = argv->GetThread();
300 [[maybe_unused]] EcmaHandleScope handleScope(thread);
301 // 1. If Type(target) is not Object, throw a TypeError exception.
302 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
303 if (!target->IsECMAObject()) {
304 THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.setPrototypeOf target is not object", JSTaggedValue::Exception());
305 }
306 // 2. If Type(proto) is not Object and proto is not null, throw a TypeError exception.
307 JSHandle<JSTaggedValue> proto = GetCallArg(argv, 1);
308 if (!proto->IsECMAObject() && !proto->IsNull()) {
309 THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: proto is neither Object nor Null",
310 JSTaggedValue::Exception());
311 }
312 // 3. Return ? target.[[SetPrototypeOf]](proto).
313 return GetTaggedBoolean(JSTaggedValue::SetPrototype(thread, target, proto));
314 }
315 } // namespace panda::ecmascript::builtins
316