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/js_proxy.h"
17
18 #include "ecmascript/global_env.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/js_function.h"
21 #include "ecmascript/js_object-inl.h"
22
23 namespace panda::ecmascript {
24 // ES6 9.5.15 ProxyCreate(target, handler)
ProxyCreate(JSThread * thread,const JSHandle<JSTaggedValue> & target,const JSHandle<JSTaggedValue> & handler)25 JSHandle<JSProxy> JSProxy::ProxyCreate(JSThread *thread, const JSHandle<JSTaggedValue> &target,
26 const JSHandle<JSTaggedValue> &handler)
27 {
28 // 1. If Type(target) is not Object, throw a TypeError exception.
29 if (!target->IsECMAObject()) {
30 THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: target is not Object",
31 JSHandle<JSProxy>(thread, JSTaggedValue::Exception()));
32 }
33
34 // 2. If Type(handler) is not Object, throw a TypeError exception.
35 if (!handler->IsECMAObject()) {
36 THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: handler is not Object",
37 JSHandle<JSProxy>(thread, JSTaggedValue::Exception()));
38 }
39 // 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).
40 // 6. If IsCallable(target) is true, then P.[[Call]] as specified in 9.5.12.
41
42 // 8. Set the [[ProxyTarget]] internal slot of P to target.
43 // 9. Set the [[ProxyHandler]] internal slot of P to handler.
44 return thread->GetEcmaVM()->GetFactory()->NewJSProxy(target, handler);
45 }
46
47 // ES6 9.5.1 [[GetPrototypeOf]] ( )
GetPrototype(JSThread * thread,const JSHandle<JSProxy> & proxy)48 JSTaggedValue JSProxy::GetPrototype(JSThread *thread, const JSHandle<JSProxy> &proxy)
49 {
50 STACK_LIMIT_CHECK(thread, JSTaggedValue::Exception());
51 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
52 // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
53 JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
54 // 2. If handler is null, throw a TypeError exception.
55 if (handler->IsNull()) {
56 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: handler is null", JSTaggedValue::Exception());
57 }
58 // 3. Assert: Type(handler) is Object.
59 ASSERT(handler->IsECMAObject());
60 // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
61 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
62 // 5. Let trap be GetMethod(handler, "getPrototypeOf").
63 JSHandle<JSTaggedValue> name(globalConst->GetHandledGetPrototypeOfString());
64 JSHandle<JSTaggedValue> trap = JSObject::GetMethod(thread, handler, name);
65 // 6. ReturnIfAbrupt(trap).
66 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
67
68 // 7. If trap is undefined, then Return target.[[GetPrototypeOf]]().
69 if (trap->IsUndefined()) {
70 return JSTaggedValue::GetPrototype(thread, targetHandle);
71 }
72 // 8. Let handlerProto be Call(trap, handler, «target»).
73 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
74 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, 1);
75 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
76 info->SetCallArg(targetHandle.GetTaggedValue());
77 JSHandle<JSTaggedValue> handlerProto(thread, JSFunction::Call(info));
78
79 // 9. ReturnIfAbrupt(handlerProto).
80 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
81 // 10. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception.
82 if (!handlerProto->IsECMAObject() && !handlerProto->IsNull()) {
83 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: Type(handlerProto) is neither Object nor Null",
84 JSTaggedValue::Exception());
85 }
86 // 11. Let extensibleTarget be IsExtensible(target).
87 // 12. ReturnIfAbrupt(extensibleTarget).
88 // 13. If extensibleTarget is true, return handlerProto.
89 if (targetHandle->IsExtensible(thread)) {
90 return handlerProto.GetTaggedValue();
91 }
92 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
93
94 // 14. Let targetProto be target.[[GetPrototypeOf]]().
95 JSTaggedValue targetProto = JSTaggedValue::GetPrototype(thread, targetHandle);
96 // 15. ReturnIfAbrupt(targetProto).
97 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
98 // 16. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception.
99 if (!JSTaggedValue::SameValue(thread, handlerProto.GetTaggedValue(), targetProto)) {
100 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: SameValue(handlerProto, targetProto) is false",
101 JSTaggedValue::Exception());
102 }
103 // 17. Return handlerProto.
104 return handlerProto.GetTaggedValue();
105 }
106
107 // ES6 9.5.2 [[SetPrototypeOf]] (V)
SetPrototype(JSThread * thread,const JSHandle<JSProxy> & proxy,const JSHandle<JSTaggedValue> & proto)108 bool JSProxy::SetPrototype(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &proto)
109 {
110 STACK_LIMIT_CHECK(thread, false);
111 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
112 // 1. Assert: Either Type(V) is Object or Type(V) is Null.
113 ASSERT(proto->IsECMAObject() || proto->IsNull());
114 // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
115 JSTaggedValue handler = proxy->GetHandler(thread);
116 // 3. If handler is null, throw a TypeError exception.
117 if (handler.IsNull()) {
118 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: handler is null", false);
119 }
120 // 4. Assert: Type(handler) is Object.
121 ASSERT(handler.IsECMAObject());
122 // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
123 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
124 // 6. Let trap be GetMethod(handler, "setPrototypeOf").
125 JSHandle<JSTaggedValue> name = globalConst->GetHandledSetPrototypeOfString();
126 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
127 // 7. ReturnIfAbrupt(trap).
128 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
129
130 // 7. If trap is undefined, then Return target.[[SetPrototypeOf]](V).
131 if (trap->IsUndefined()) {
132 return JSTaggedValue::SetPrototype(thread, targetHandle, proto);
133 }
134 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
135 const uint32_t argsLength = 2; // 2: target and proto
136 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
137 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength);
138 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
139 info->SetCallArg(targetHandle.GetTaggedValue(), proto.GetTaggedValue());
140 JSTaggedValue trapResult = JSFunction::Call(info);
141 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
142 // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, V»)).
143 // If booleanTrapResult is false, return false
144 bool booleanTrapResult = trapResult.ToBoolean();
145 if (!booleanTrapResult) {
146 return false;
147 }
148 // 10. ReturnIfAbrupt(booleanTrapResult).
149 // 11. Let extensibleTarget be IsExtensible(target).
150 // 12. ReturnIfAbrupt(extensibleTarget).
151 // 13. If extensibleTarget is true, return booleanTrapResult
152 if (targetHandle->IsExtensible(thread)) {
153 return booleanTrapResult;
154 }
155 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
156
157 // 14. Let targetProto be target.[[GetPrototypeOf]]().
158 JSTaggedValue targetProto = JSTaggedValue::GetPrototype(thread, targetHandle);
159 // 15. ReturnIfAbrupt(targetProto).
160 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
161
162 // 16. If booleanTrapResult is true and SameValue(V, targetProto) is false, throw a TypeError exception.
163 if (booleanTrapResult && !JSTaggedValue::SameValue(thread, proto.GetTaggedValue(), targetProto)) {
164 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: TypeError of targetProto and Result", false);
165 }
166 // 17. Return handlerProto.
167 return booleanTrapResult;
168 }
169
170 // ES6 9.5.3 [[IsExtensible]] ( )
IsExtensible(JSThread * thread,const JSHandle<JSProxy> & proxy)171 bool JSProxy::IsExtensible(JSThread *thread, const JSHandle<JSProxy> &proxy)
172 {
173 STACK_LIMIT_CHECK(thread, false);
174 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
175 // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
176 JSTaggedValue handler = proxy->GetHandler(thread);
177 // 2. If handler is null, throw a TypeError exception.
178 if (handler.IsNull()) {
179 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: handler is null", false);
180 }
181 // 3. Assert: Type(handler) is Object.
182 ASSERT(handler.IsECMAObject());
183 // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
184 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
185 // 5. Let trap be GetMethod(handler, "isExtensible").
186 JSHandle<JSTaggedValue> name = globalConst->GetHandledIsExtensibleString();
187 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
188 // 6. ReturnIfAbrupt(trap).
189 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
190
191 // 7. If trap is undefined, then Return target.[[IsExtensible]]().
192 if (trap->IsUndefined()) {
193 return targetHandle->IsExtensible(thread);
194 }
195 // 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)).
196 JSHandle<JSTaggedValue> newTgt(thread, JSTaggedValue::Undefined());
197 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
198 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
199 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, 1);
200 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
201 info->SetCallArg(targetHandle.GetTaggedValue());
202 JSTaggedValue trapResult = JSFunction::Call(info);
203 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
204 bool booleanTrapResult = trapResult.ToBoolean();
205 // 9. ReturnIfAbrupt(booleanTrapResult).
206 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
207
208 // 10. Let targetResult be target.[[IsExtensible]]().
209 // 11. ReturnIfAbrupt(targetResult).
210 // 12. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception.
211 // 13. Return booleanTrapResult.
212 if (targetHandle->IsExtensible(thread) != booleanTrapResult) {
213 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: TypeError of targetResult", false);
214 }
215
216 return booleanTrapResult;
217 }
218
219 // ES6 9.5.4 [[PreventExtensions]] ( )
PreventExtensions(JSThread * thread,const JSHandle<JSProxy> & proxy)220 bool JSProxy::PreventExtensions(JSThread *thread, const JSHandle<JSProxy> &proxy)
221 {
222 STACK_LIMIT_CHECK(thread, false);
223 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
224 // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
225 // 2. If handler is null, throw a TypeError exception.
226 // 3. Assert: Type(handler) is Object.
227 // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
228 // 5. Let trap be GetMethod(handler, "preventExtensions").
229 // 6. ReturnIfAbrupt(trap).
230 // 7. If trap is undefined, then
231 // a. Return target.[[PreventExtensions]]().
232 // 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)).
233 // 9. ReturnIfAbrupt(booleanTrapResult).
234 JSTaggedValue handler = proxy->GetHandler(thread);
235 if (handler.IsNull()) {
236 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: handler is null", false);
237 }
238 ASSERT(handler.IsECMAObject());
239 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
240 JSHandle<JSTaggedValue> name = globalConst->GetHandledPreventExtensionsString();
241 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
242 // 6. ReturnIfAbrupt(trap).
243 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
244
245 if (trap->IsUndefined()) {
246 return JSTaggedValue::PreventExtensions(thread, targetHandle);
247 }
248 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
249 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
250 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, 1);
251 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
252 info->SetCallArg(targetHandle.GetTaggedValue());
253 JSTaggedValue trapResult = JSFunction::Call(info);
254 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
255 bool booleanTrapResult = trapResult.ToBoolean();
256 // 9. ReturnIfAbrupt(booleanTrapResult).
257 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
258 // 10. If booleanTrapResult is true, then
259 // a. Let targetIsExtensible be target.[[IsExtensible]]().
260 // b. ReturnIfAbrupt(targetIsExtensible).
261 // c. If targetIsExtensible is true, throw a TypeError exception.
262 if (booleanTrapResult && targetHandle->IsExtensible(thread)) {
263 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: targetIsExtensible is true", false);
264 }
265 // 11. Return booleanTrapResult.
266 return booleanTrapResult;
267 }
268
269 // ES6 9.5.5 [[GetOwnProperty]] (P)
GetOwnProperty(JSThread * thread,const JSHandle<JSProxy> & proxy,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)270 bool JSProxy::GetOwnProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key,
271 PropertyDescriptor &desc)
272 {
273 STACK_LIMIT_CHECK(thread, false);
274 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
275 // 1. Assert: IsPropertyKey(P) is true.
276 ASSERT(JSTaggedValue::IsPropertyKey(key));
277 // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
278 // 3. If handler is null, throw a TypeError exception.
279 // 4. Assert: Type(handler) is Object.
280 // 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
281 // 6. Let trap be GetMethod(handler, "getOwnPropertyDescriptor").
282 // 7. ReturnIfAbrupt(trap).
283 // 8. If trap is undefined, then
284 // a. Return target.[[GetOwnProperty]](P).
285 // 9. Let trapResultObj be Call(trap, handler, «target, P»).
286 // 10. ReturnIfAbrupt(trapResultObj).
287 // 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception
288 JSTaggedValue handler = proxy->GetHandler(thread);
289 if (handler.IsNull()) {
290 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: handler is null", false);
291 }
292 ASSERT(handler.IsECMAObject());
293 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
294 JSHandle<JSTaggedValue> name = globalConst->GetHandledGetOwnPropertyDescriptorString();
295 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
296 // 7. ReturnIfAbrupt(trap).
297 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
298
299 if (trap->IsUndefined()) {
300 return JSTaggedValue::GetOwnProperty(thread, targetHandle, key, desc);
301 }
302 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
303 const uint32_t argsLength = 2; // 2: target and key
304 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
305 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength);
306 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
307 info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue());
308 JSTaggedValue trapResultObj = JSFunction::Call(info);
309 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
310
311 JSHandle<JSTaggedValue> resultHandle(thread, trapResultObj);
312
313 // 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
314 if (!trapResultObj.IsECMAObject() && !trapResultObj.IsUndefined()) {
315 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of trapResultObj", false);
316 }
317 // 12. Let targetDesc be target.[[GetOwnProperty]](P).
318 PropertyDescriptor targetDesc(thread);
319 bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
320 // 13. ReturnIfAbrupt(targetDesc).
321 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
322
323 // 14. If trapResultObj is undefined, then
324 if (resultHandle->IsUndefined()) {
325 // a. If targetDesc is undefined, return undefined.
326 if (!found) {
327 return false;
328 }
329 // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
330 if (!targetDesc.IsConfigurable()) {
331 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: targetDesc.[[Configurable]] is false", false);
332 }
333 // c. Let extensibleTarget be IsExtensible(target).
334 // d. ReturnIfAbrupt(extensibleTarget).
335 // e. Assert: Type(extensibleTarget) is Boolean.
336 // f. If extensibleTarget is false, throw a TypeError exception.
337 if (!targetHandle->IsExtensible(thread)) {
338 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: extensibleTarget is false", false);
339 }
340 // g. Return undefined.
341 return false;
342 }
343 // 15. Let extensibleTarget be IsExtensible(target).
344 // 16. ReturnIfAbrupt(extensibleTarget).
345 // 17. Let resultDesc be ToPropertyDescriptor(trapResultObj).
346 PropertyDescriptor &resultDesc = desc;
347 JSObject::ToPropertyDescriptor(thread, resultHandle, resultDesc);
348 // 18. ReturnIfAbrupt(resultDesc)
349 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
350 // 19. Call CompletePropertyDescriptor(resultDesc).
351 PropertyDescriptor::CompletePropertyDescriptor(thread, resultDesc);
352 // 20. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget, resultDesc, targetDesc).
353 bool valid = JSObject::IsCompatiblePropertyDescriptor(targetHandle->IsExtensible(thread), resultDesc, targetDesc);
354 if (!valid) {
355 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of valid", false);
356 }
357 // 22. If resultDesc.[[Configurable]] is false, then
358 if (!resultDesc.IsConfigurable()) {
359 // a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
360 if (!found || targetDesc.IsConfigurable()) {
361 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc configurable", false);
362 }
363 // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then
364 // If targetDesc.[[Writable]] is true, throw a TypeError exception.
365 if (resultDesc.HasWritable() && !resultDesc.IsWritable() && targetDesc.IsWritable()) {
366 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc writable", false);
367 }
368 }
369 // 23. Return resultDesc.
370 return true;
371 }
372
373 // ES6 9.5.6 [[DefineOwnProperty]] (P, Desc)
DefineOwnProperty(JSThread * thread,const JSHandle<JSProxy> & proxy,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)374 bool JSProxy::DefineOwnProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key,
375 const PropertyDescriptor &desc)
376 {
377 STACK_LIMIT_CHECK(thread, false);
378 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
379 // step 1 ~ 10 are almost same as GetOwnProperty
380 ASSERT(JSTaggedValue::IsPropertyKey(key));
381 JSTaggedValue handler = proxy->GetHandler(thread);
382 if (handler.IsNull()) {
383 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: handler is Null", false);
384 }
385 ASSERT(handler.IsECMAObject());
386 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
387 JSHandle<JSTaggedValue> name = globalConst->GetHandledDefinePropertyString();
388 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
389 // 7. ReturnIfAbrupt(trap).
390 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
391 if (trap->IsUndefined()) {
392 return JSTaggedValue::DefineOwnProperty(thread, targetHandle, key, desc);
393 }
394
395 // 9. Let descObj be FromPropertyDescriptor(Desc).
396 JSHandle<JSTaggedValue> descObj = JSObject::FromPropertyDescriptor(thread, desc);
397 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
398 const uint32_t argsLength = 3; // 3: target, key and desc
399 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
400 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength);
401 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
402 info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue(), descObj.GetTaggedValue());
403 JSTaggedValue trapResult = JSFunction::Call(info);
404 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
405 bool booleanTrapResult = trapResult.ToBoolean();
406 // 11. ReturnIfAbrupt(booleanTrapResult).
407 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
408 if (!booleanTrapResult) {
409 return false;
410 }
411 // 13. Let targetDesc be target.[[GetOwnProperty]](P).
412 PropertyDescriptor targetDesc(thread);
413 bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
414 // 14. ReturnIfAbrupt(targetDesc).
415 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
416
417 // 15. Let extensibleTarget be IsExtensible(target).
418 // 16. ReturnIfAbrupt(extensibleTarget).
419 // 17. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then Let settingConfigFalse be
420 // true.
421 // 18. Else let settingConfigFalse be false.
422 bool settingConfigFalse = false;
423 if (desc.HasConfigurable() && !desc.IsConfigurable()) {
424 settingConfigFalse = true;
425 }
426 // 19. If targetDesc is undefined, then
427 if (!found) {
428 // a. If extensibleTarget is false, throw a TypeError exception.
429 if (!targetHandle->IsExtensible(thread)) {
430 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: extensibleTarget is false", false);
431 }
432 // b. If settingConfigFalse is true, throw a TypeError exception.
433 if (settingConfigFalse) {
434 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: settingConfigFalse is true", false);
435 }
436 } else {
437 // a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc , targetDesc) is false, throw a TypeError
438 // exception.
439 if (!JSObject::IsCompatiblePropertyDescriptor(targetHandle->IsExtensible(thread), desc, targetDesc)) {
440 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: CompatiblePropertyDescriptor err", false);
441 }
442 // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
443 if (settingConfigFalse && targetDesc.IsConfigurable()) {
444 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of settingConfigFalse", false);
445 }
446 // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]]
447 // is true, then If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.
448 if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && targetDesc.IsWritable() &&
449 desc.HasWritable() && !desc.IsWritable()) {
450 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of DataDescriptor", false);
451 }
452 }
453 // 21. Return true.
454 return true;
455 }
456
457 // ES6 9.5.7 [[HasProperty]] (P)
HasProperty(JSThread * thread,const JSHandle<JSProxy> & proxy,const JSHandle<JSTaggedValue> & key)458 bool JSProxy::HasProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key)
459 {
460 STACK_LIMIT_CHECK(thread, false);
461 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
462 // step 1 ~ 10 are almost same as GetOwnProperty
463 ASSERT(JSTaggedValue::IsPropertyKey(key));
464 JSTaggedValue handler = proxy->GetHandler(thread);
465 if (handler.IsNull()) {
466 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: handler is Null", false);
467 }
468 ASSERT(handler.IsECMAObject());
469 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
470 JSHandle<JSTaggedValue> name = globalConst->GetHandledHasString();
471 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
472 // 7. ReturnIfAbrupt(trap).
473 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
474 if (trap->IsUndefined()) {
475 return JSTaggedValue::HasProperty(thread, targetHandle, key);
476 }
477
478 // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)).
479 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
480
481 const uint32_t argsLength = 2; // 2: target and key
482 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
483 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength);
484 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
485 info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue());
486 JSTaggedValue trapResult = JSFunction::Call(info);
487 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
488 bool booleanTrapResult = trapResult.ToBoolean();
489 // 10. ReturnIfAbrupt(booleanTrapResult).
490 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
491
492 // 11. If booleanTrapResult is false, then
493 if (!booleanTrapResult) {
494 // a. Let targetDesc be target.[[GetOwnProperty]](P).
495 PropertyDescriptor targetDesc(thread);
496 bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
497 // b. ReturnIfAbrupt(targetDesc).
498 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
499 // c. If targetDesc is not undefined, then
500 if (found) {
501 // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
502 if (!targetDesc.IsConfigurable()) {
503 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: TypeError of targetDesc", false);
504 }
505 // ii. Let extensibleTarget be IsExtensible(target).
506 // iii. ReturnIfAbrupt(extensibleTarget).
507 // iv. If extensibleTarget is false, throw a TypeError exception.
508 if (!targetHandle->IsExtensible(thread)) {
509 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: extensibleTarget is false", false);
510 }
511 }
512 }
513 return booleanTrapResult;
514 }
515
CheckGetTrapResult(JSThread * thread,const JSHandle<JSTaggedValue> & targetHandle,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & resultHandle)516 OperationResult JSProxy::CheckGetTrapResult(JSThread *thread, const JSHandle<JSTaggedValue> &targetHandle,
517 const JSHandle<JSTaggedValue> &key,
518 const JSHandle<JSTaggedValue> &resultHandle)
519 {
520 JSHandle<JSTaggedValue> exceptionHandle(thread, JSTaggedValue::Exception());
521 // 11. Let targetDesc be target.[[GetOwnProperty]](P).
522 PropertyDescriptor targetDesc(thread);
523 bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
524 // 12. ReturnIfAbrupt(targetDesc).
525 RETURN_VALUE_IF_ABRUPT_COMPLETION(
526 thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
527
528 // 13. If targetDesc is not undefined, then
529 if (found) {
530 // a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is
531 // false, then
532 if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) {
533 // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
534 if (!JSTaggedValue::SameValue(thread, resultHandle.GetTaggedValue(),
535 targetDesc.GetValue().GetTaggedValue())) {
536 THROW_TYPE_ERROR_AND_RETURN(
537 thread, "JSProxy::GetProperty: TypeError of trapResult",
538 OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
539 }
540 }
541 // b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Get]] is
542 // undefined, then
543 if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() &&
544 targetDesc.GetGetter()->IsUndefined()) {
545 // i. If trapResult is not undefined, throw a TypeError exception.
546 if (!resultHandle.GetTaggedValue().IsUndefined()) {
547 THROW_TYPE_ERROR_AND_RETURN(
548 thread, "JSProxy::GetProperty: trapResult is not undefined",
549 OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
550 }
551 }
552 }
553 // 14. Return trapResult.
554 return OperationResult(thread, resultHandle.GetTaggedValue(), PropertyMetaData(true));
555 }
556
557 // ES6 9.5.8 [[Get]] (P, Receiver)
GetProperty(JSThread * thread,const JSHandle<JSProxy> & proxy,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & receiver)558 OperationResult JSProxy::GetProperty(JSThread *thread, const JSHandle<JSProxy> &proxy,
559 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &receiver)
560 {
561 STACK_LIMIT_CHECK(thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
562 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
563 // step 1 ~ 10 are almost same as GetOwnProperty
564 ASSERT(JSTaggedValue::IsPropertyKey(key));
565 JSTaggedValue handler = proxy->GetHandler(thread);
566 JSHandle<JSTaggedValue> exceptionHandle(thread, JSTaggedValue::Exception());
567 if (handler.IsNull()) {
568 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetProperty: handler is Null",
569 OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
570 }
571 ASSERT(handler.IsECMAObject());
572 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
573 JSHandle<JSTaggedValue> name = globalConst->GetHandledGetString();
574 JSHandle<JSTaggedValue> trap(JSObject::FastGetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
575 // 7. ReturnIfAbrupt(trap).
576 RETURN_VALUE_IF_ABRUPT_COMPLETION(
577 thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
578
579 if (trap->IsUndefined()) {
580 return JSTaggedValue::GetProperty(thread, targetHandle, key, receiver);
581 }
582 // 9. Let trapResult be Call(trap, handler, «target, P, Receiver»).
583 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
584 const uint32_t argsLength = 3; // 3: «target, P, Receiver»
585 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
586 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength);
587 RETURN_VALUE_IF_ABRUPT_COMPLETION(
588 thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
589 info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue(), receiver.GetTaggedValue());
590 JSTaggedValue trapResult = JSFunction::Call(info);
591 JSHandle<JSTaggedValue> resultHandle(thread, trapResult);
592
593 // 10. ReturnIfAbrupt(trapResult).
594 RETURN_VALUE_IF_ABRUPT_COMPLETION(
595 thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
596
597 return CheckGetTrapResult(thread, targetHandle, key, resultHandle);
598 }
599
CheckSetTrapResult(JSThread * thread,const JSHandle<JSTaggedValue> & targetHandle,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)600 bool JSProxy::CheckSetTrapResult(JSThread *thread, const JSHandle<JSTaggedValue> &targetHandle,
601 const JSHandle<JSTaggedValue> &key,
602 const JSHandle<JSTaggedValue> &value)
603 {
604 // 13. Let targetDesc be target.[[GetOwnProperty]](P).
605 PropertyDescriptor targetDesc(thread);
606 bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
607 // 14. If targetDesc is not undefined, then
608 if (found) {
609 // a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is
610 // false, then
611 if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) {
612 // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
613 if (!JSTaggedValue::SameValue(thread, value, targetDesc.GetValue())) {
614 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of trapResult", false);
615 }
616 }
617 // b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false, then
618 // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
619 if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() &&
620 targetDesc.GetSetter()->IsUndefined()) {
621 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of AccessorDescriptor", false);
622 }
623 }
624 return true;
625 }
626
627 // ES6 9.5.9 [[Set]] ( P, V, Receiver)
SetProperty(JSThread * thread,const JSHandle<JSProxy> & proxy,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & receiver,bool mayThrow)628 bool JSProxy::SetProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key,
629 const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &receiver, bool mayThrow)
630 {
631 STACK_LIMIT_CHECK(thread, false);
632 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
633 // step 1 ~ 10 are almost same as GetOwnProperty
634 ASSERT(JSTaggedValue::IsPropertyKey(key));
635 JSTaggedValue handler = proxy->GetHandler(thread);
636 if (handler.IsNull()) {
637 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: handler is Null", false);
638 }
639 ASSERT(handler.IsECMAObject());
640 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
641 JSHandle<JSTaggedValue> name = globalConst->GetHandledSetString();
642 JSHandle<JSTaggedValue> trap(JSObject::FastGetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
643 // 7. ReturnIfAbrupt(trap).
644 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
645 if (trap->IsUndefined()) {
646 return JSTaggedValue::SetProperty(thread, targetHandle, key, value, receiver, mayThrow);
647 }
648
649 // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, V, Receiver»))
650 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
651 const uint32_t argsLength = 4; // 4: «target, P, V, Receiver»
652 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
653 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength);
654 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
655 info->SetCallArg(
656 targetHandle.GetTaggedValue(), key.GetTaggedValue(), value.GetTaggedValue(), receiver.GetTaggedValue());
657 JSTaggedValue trapResult = JSFunction::Call(info);
658 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
659 bool booleanTrapResult = trapResult.ToBoolean();
660 // 11. ReturnIfAbrupt(booleanTrapResult).
661 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
662 if (!booleanTrapResult) {
663 if (mayThrow) {
664 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: 'set' return false", false);
665 }
666 return false;
667 }
668 return CheckSetTrapResult(thread, targetHandle, key, value);
669 }
670
671 // ES6 9.5.10 [[Delete]] (P)
DeleteProperty(JSThread * thread,const JSHandle<JSProxy> & proxy,const JSHandle<JSTaggedValue> & key)672 bool JSProxy::DeleteProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key)
673 {
674 STACK_LIMIT_CHECK(thread, false);
675 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
676 // step 1 ~ 13 are almost same as GetOwnProperty
677 ASSERT(JSTaggedValue::IsPropertyKey(key));
678 JSTaggedValue handler = proxy->GetHandler(thread);
679 if (handler.IsNull()) {
680 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: handler is Null", false);
681 }
682 ASSERT(handler.IsECMAObject());
683 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
684 JSHandle<JSTaggedValue> name = globalConst->GetHandledDeletePropertyString();
685 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
686 // 7. ReturnIfAbrupt(trap).
687 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
688 if (trap->IsUndefined()) {
689 return JSTaggedValue::DeleteProperty(thread, targetHandle, key);
690 }
691
692 // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)).
693 JSHandle<JSTaggedValue> newTgt(thread, JSTaggedValue::Undefined());
694 JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler(thread));
695 const uint32_t argsLength = 2; // 2: target and key
696 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
697 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength);
698 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
699 info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue());
700 JSTaggedValue trapResult = JSFunction::Call(info);
701 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
702 bool booleanTrapResult = trapResult.ToBoolean();
703 // 11. ReturnIfAbrupt(booleanTrapResult).
704 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
705 if (!booleanTrapResult) {
706 return false;
707 }
708 // 13. Let targetDesc be target.[[GetOwnProperty]](P).
709 PropertyDescriptor targetDesc(thread);
710 bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
711 // 14. If targetDesc is undefined, return true.
712 if (!found) {
713 return true;
714 }
715 // 15. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
716 if (!targetDesc.IsConfigurable()) {
717 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: targetDesc is not Configurable", false);
718 }
719 if (!targetHandle->IsExtensible(thread)) {
720 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: targetHandle is not Extensible", false);
721 }
722 // 16. Return true.
723 return true;
724 }
725
726 // ES6 9.5.12 [[OwnPropertyKeys]] ()
OwnPropertyKeys(JSThread * thread,const JSHandle<JSProxy> & proxy)727 JSHandle<TaggedArray> JSProxy::OwnPropertyKeys(JSThread *thread, const JSHandle<JSProxy> &proxy)
728 {
729 STACK_LIMIT_CHECK(thread, JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
730 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
731 // step 1 ~ 4 get ProxyHandler and ProxyTarget
732 JSTaggedValue handler = proxy->GetHandler(thread);
733 if (handler.IsNull()) {
734 THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: handler is null",
735 JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
736 }
737
738 ASSERT(handler.IsECMAObject());
739 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
740
741 // 5.Let trap be GetMethod(handler, "ownKeys").
742 JSHandle<JSTaggedValue> key = globalConst->GetHandledOwnKeysString();
743 JSHandle<JSTaggedValue> handlerHandle(thread, handler);
744 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handlerHandle, key));
745
746 // 6.ReturnIfAbrupt(trap).
747 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
748
749 // 7.If trap is undefined, then
750 // a.Return target.[[OwnPropertyKeys]]().
751 if (trap->IsUndefined()) {
752 return JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle);
753 }
754
755 // 8.Let trapResultArray be Call(trap, handler, «target»).
756 JSHandle<JSFunction> tagFunc(targetHandle);
757 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
758 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerHandle, undefined, 1);
759 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
760 info->SetCallArg(targetHandle.GetTaggedValue());
761 JSTaggedValue res = JSFunction::Call(info);
762 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
763 JSHandle<JSTaggedValue> trapResArr(thread, res);
764
765 // 9.Let trapResult be CreateListFromArrayLike(trapResultArray, «String, Symbol»).
766 // 10.ReturnIfAbrupt(trapResult)
767 // If trapResult contains any duplicate entries, throw a TypeError exception.
768 JSHandle<TaggedArray> trapRes(
769 JSObject::CreateListFromArrayLike<ElementTypes::STRING_AND_SYMBOL>(thread, trapResArr));
770 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
771
772 if (trapRes->HasDuplicateEntry(thread)) {
773 THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: contains duplicate entries",
774 JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
775 }
776
777 // 11.Let extensibleTarget be IsExtensible(target).
778 bool extensibleTarget = targetHandle->IsExtensible(thread);
779
780 // 12.ReturnIfAbrupt(extensibleTarget).
781 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
782
783 // 13.Let targetKeys be target.[[OwnPropertyKeys]]().
784 JSHandle<TaggedArray> targetKeys = JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle);
785
786 // 14.ReturnIfAbrupt(targetKeys).
787 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
788
789 // 15.Assert: targetKeys is a List containing only String and Symbol values.
790 // 16.Let targetConfigurableKeys be an empty List.
791 // 17.Let targetNonconfigurableKeys be an empty List.
792 // 18.Repeat, for each element key of targetKeys,
793 // a.Let desc be target.[[GetOwnProperty]](key).
794 // b.ReturnIfAbrupt(desc).
795 // c.If desc is not undefined and desc.[[Configurable]] is false, then
796 // i.Append key as an element of targetNonconfigurableKeys.
797 // d.Else,
798 // i.Append key as an element of targetConfigurableKeys.
799 uint32_t length = targetKeys->GetLength();
800 JSHandle<TaggedArray> tgtCfigKeys = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length);
801 JSHandle<TaggedArray> tgtNoCfigKeys = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length);
802
803 uint32_t cfigLength = 0;
804 uint32_t noCfigLength = 0;
805 for (uint32_t i = 0; i < length; i++) {
806 JSHandle<JSTaggedValue> targetKey(thread, targetKeys->Get(thread, i));
807 ASSERT(targetKey->IsStringOrSymbol());
808
809 PropertyDescriptor desc(thread);
810 JSTaggedValue::GetOwnProperty(thread, targetHandle, targetKey, desc);
811 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
812
813 if (!desc.IsEmpty() && !desc.IsConfigurable()) {
814 tgtNoCfigKeys->Set(thread, noCfigLength, targetKey);
815 noCfigLength++;
816 } else {
817 tgtCfigKeys->Set(thread, cfigLength, targetKey);
818 cfigLength++;
819 }
820 }
821
822 // 19.If extensibleTarget is true and targetNonconfigurableKeys is empty, then
823 // a.Return trapResult.
824 if (extensibleTarget && (cfigLength == 0)) {
825 return trapRes;
826 }
827
828 // 20.Let uncheckedResultKeys be a new List which is a copy of trapResult.
829 JSHandle<TaggedArray> uncheckFesKeys =
830 thread->GetEcmaVM()->GetFactory()->CopyArray(trapRes, trapRes->GetLength(), trapRes->GetLength());
831 uint32_t uncheckLength = uncheckFesKeys->GetLength();
832
833 // 21.Repeat, for each key that is an element of targetNonconfigurableKeys,
834 // a.If key is not an element of uncheckedResultKeys, throw a TypeError exception.
835 // b.Remove key from uncheckedResultKeys
836 for (uint32_t i = 0; i < noCfigLength; i++) {
837 uint32_t idx = uncheckFesKeys->GetIdx(thread, tgtNoCfigKeys->Get(thread, i));
838 if (idx == TaggedArray::MAX_ARRAY_INDEX) {
839 THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: key is not an element of uncheckedResultKeys",
840 JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
841 }
842 uncheckFesKeys->Set(thread, idx, JSTaggedValue::Hole());
843 uncheckLength--;
844 }
845
846 // 22.If extensibleTarget is true, return trapResult.
847 if (extensibleTarget) {
848 return trapRes;
849 }
850
851 // 23.Repeat, for each key that is an element of targetConfigurableKeys,
852 // a.If key is not an element of uncheckedResultKeys, throw a TypeError exception.
853 // b.Remove key from uncheckedResultKeys
854 for (uint32_t i = 0; i < cfigLength; i++) {
855 uint32_t idx = uncheckFesKeys->GetIdx(thread, tgtCfigKeys->Get(thread, i));
856 if (idx == TaggedArray::MAX_ARRAY_INDEX) {
857 THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: key is not an element of uncheckedResultKeys",
858 JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
859 }
860 uncheckFesKeys->Set(thread, idx, JSTaggedValue::Hole());
861 uncheckLength--;
862 }
863
864 // 24.If uncheckedResultKeys is not empty, throw a TypeError exception.
865 if (uncheckLength != 0) {
866 THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: uncheckedResultKeys is not empty",
867 JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
868 }
869
870 // 25.Return trapResult.
871 return trapRes;
872 }
873
GetAllPropertyKeys(JSThread * thread,const JSHandle<JSProxy> & proxy,uint32_t filter)874 JSHandle<TaggedArray> JSProxy::GetAllPropertyKeys(JSThread *thread, const JSHandle<JSProxy> &proxy, uint32_t filter)
875 {
876 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
877
878 JSTaggedValue handler = proxy->GetHandler(thread);
879 if (handler.IsNull()) {
880 THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: handler is null",
881 JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
882 }
883
884 ASSERT(handler.IsECMAObject());
885 JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
886
887 JSHandle<JSTaggedValue> key = globalConst->GetHandledOwnKeysString();
888 JSHandle<JSTaggedValue> handlerHandle(thread, handler);
889 JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handlerHandle, key));
890
891 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
892
893 if (trap->IsUndefined()) {
894 return JSTaggedValue::GetAllPropertyKeys(thread, targetHandle, filter);
895 }
896
897 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
898 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerHandle, undefined, 1);
899 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
900 info->SetCallArg(targetHandle.GetTaggedValue());
901 JSTaggedValue res = JSFunction::Call(info);
902 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
903 JSHandle<JSTaggedValue> trapResArr(thread, res);
904
905 JSHandle<TaggedArray> trapRes(
906 JSObject::CreateListFromArrayLike<ElementTypes::STRING_AND_SYMBOL>(thread, trapResArr));
907 JSHandle<TaggedArray> ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle);
908 JSHandle<TaggedArray> reciveArray = JSTaggedValue::GetAllPropertyKeys(thread, targetHandle, filter);
909
910 uint32_t trapResLength = trapRes->GetLength();
911 uint32_t ownKeysLength = ownKeys->GetLength();
912 uint32_t reciveArrayLength = reciveArray->GetLength();
913 uint32_t newArrayLength = reciveArrayLength + trapResLength - ownKeysLength;
914
915 JSHandle<TaggedArray> resArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(newArrayLength);
916
917 uint32_t elementIndex = 0;
918 if (filter & NATIVE_KEY_SKIP_SYMBOLS) {
919 for (uint32_t index = 0; index < reciveArrayLength; index++) {
920 if (!ownKeys->Get(thread, index).IsSymbol()) {
921 resArray->Set(thread, elementIndex, reciveArray->Get(thread, index));
922 elementIndex++;
923 }
924 }
925 return resArray;
926 }
927
928 for (uint32_t i = 0; i < trapResLength; i++) {
929 resArray->Set(thread, i, trapRes->Get(thread, i));
930 }
931
932 uint32_t index = ownKeysLength;
933 for (uint32_t j = 0; j < reciveArrayLength - ownKeysLength; j++) {
934 resArray->Set(thread, trapResLength + j, reciveArray->Get(thread, index));
935 index++;
936 }
937 return resArray;
938 }
939
940 // ES6 9.5.13 [[Call]] (thisArgument, argumentsList)
CallInternal(EcmaRuntimeCallInfo * info)941 JSTaggedValue JSProxy::CallInternal(EcmaRuntimeCallInfo *info)
942 {
943 if (info == nullptr) {
944 return JSTaggedValue::Exception();
945 }
946
947 JSThread *thread = info->GetThread();
948 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
949 JSHandle<JSProxy> proxy(info->GetFunction());
950 // step 1 ~ 4 get ProxyHandler and ProxyTarget
951 JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
952 if (handler->IsNull()) {
953 THROW_TYPE_ERROR_AND_RETURN(thread, "Call: handler is null", JSTaggedValue::Exception());
954 }
955 ASSERT(handler->IsECMAObject());
956 JSHandle<JSTaggedValue> target(thread, proxy->GetTarget(thread));
957
958 // 5.Let trap be GetMethod(handler, "apply").
959 JSHandle<JSTaggedValue> key(globalConst->GetHandledApplyString());
960 JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, handler, key);
961
962 // 6.ReturnIfAbrupt(trap).
963 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
964 uint32_t argc = info->GetArgsNumber();
965 JSHandle<JSTaggedValue> thisArg = info->GetThis();
966 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
967 // 7.If trap is undefined, then
968 // a.Return Call(target, thisArgument, argumentsList).
969 if (method->IsUndefined()) {
970 EcmaRuntimeCallInfo *runtimeInfo =
971 EcmaInterpreter::NewRuntimeCallInfo(thread, target, thisArg, undefined, argc);
972 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
973 runtimeInfo->SetCallArg(argc, 0, info, 0);
974 return JSFunction::Call(runtimeInfo);
975 }
976 // 8.Let argArray be CreateArrayFromList(argumentsList).
977 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
978 JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(argc);
979 for (uint32_t index = 0; index < argc; ++index) {
980 taggedArray->Set(thread, index, info->GetCallArg(index));
981 }
982 JSHandle<JSArray> arrHandle = JSArray::CreateArrayFromList(thread, taggedArray);
983
984 // 9.Return Call(trap, handler, «target, thisArgument, argArray»).
985 const uint32_t argsLength = 3; // 3: «target, thisArgument, argArray»
986 EcmaRuntimeCallInfo *runtimeInfo =
987 EcmaInterpreter::NewRuntimeCallInfo(thread, method, handler, undefined, argsLength);
988 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
989 runtimeInfo->SetCallArg(target.GetTaggedValue(), thisArg.GetTaggedValue(), arrHandle.GetTaggedValue());
990 return JSFunction::Call(runtimeInfo);
991 }
992
993 // ES6 9.5.14 [[Construct]] ( argumentsList, newTarget)
ConstructInternal(EcmaRuntimeCallInfo * info)994 JSTaggedValue JSProxy::ConstructInternal(EcmaRuntimeCallInfo *info)
995 {
996 if (info == nullptr) {
997 return JSTaggedValue::Exception();
998 }
999 JSThread *thread = info->GetThread();
1000 // check stack overflow because infinite recursion may occur
1001 if (thread->DoStackLimitCheck()) {
1002 return JSTaggedValue::Exception();
1003 }
1004
1005 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1006 // step 1 ~ 4 get ProxyHandler and ProxyTarget
1007 JSHandle<JSProxy> proxy(info->GetFunction());
1008 JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
1009 if (handler->IsNull()) {
1010 THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor: handler is null", JSTaggedValue::Exception());
1011 }
1012 ASSERT(handler->IsECMAObject());
1013 JSHandle<JSTaggedValue> target(thread, proxy->GetTarget(thread));
1014
1015 // 5.Let trap be GetMethod(handler, "construct").
1016 JSHandle<JSTaggedValue> key(globalConst->GetHandledProxyConstructString());
1017 JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, handler, key);
1018
1019 // 6.ReturnIfAbrupt(trap).
1020 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1021 // 7.If trap is undefined, then
1022 // a.Assert: target has a [[Construct]] internal method.
1023 // b.Return Construct(target, argumentsList, newTarget).
1024 if (method->IsUndefined()) {
1025 if (!target->IsConstructor()) {
1026 THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception());
1027 }
1028 ASSERT(target->IsConstructor());
1029 info->SetFunction(target.GetTaggedValue());
1030 return JSFunction::Construct(info);
1031 }
1032
1033 // 8.Let argArray be CreateArrayFromList(argumentsList).
1034 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1035 uint32_t argc = info->GetArgsNumber();
1036 JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(argc);
1037 for (uint32_t index = 0; index < argc; ++index) {
1038 taggedArray->Set(thread, index, info->GetCallArg(index));
1039 }
1040 JSHandle<JSArray> arrHandle = JSArray::CreateArrayFromList(thread, taggedArray);
1041
1042 // step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »).
1043 JSHandle<JSTaggedValue> newTarget(thread, info->GetNewTargetValue());
1044 const uint32_t argsLength = 3; // 3: «target, argArray, newTarget »
1045 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1046
1047 JSTaggedType *currentSp = reinterpret_cast<JSTaggedType *>(info);
1048 InterpretedEntryFrame *currentEntryState = InterpretedEntryFrame::GetFrameFromSp(currentSp);
1049 JSTaggedType *prevSp = currentEntryState->base.prev;
1050 thread->SetCurrentSPFrame(prevSp);
1051
1052 EcmaRuntimeCallInfo *runtimeInfo =
1053 EcmaInterpreter::NewRuntimeCallInfo(thread, method, handler, undefined, argsLength);
1054 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1055 runtimeInfo->SetCallArg(target.GetTaggedValue(), arrHandle.GetTaggedValue(), newTarget.GetTaggedValue());
1056 JSTaggedValue newObj = JSFunction::Call(runtimeInfo);
1057
1058 // 10.ReturnIfAbrupt(newObj).
1059 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1060 // 11.If Type(newObj) is not Object, throw a TypeError exception.
1061 if (!newObj.IsECMAObject()) {
1062 THROW_TYPE_ERROR_AND_RETURN(thread, "new object is not object", JSTaggedValue::Exception());
1063 }
1064 // 12.Return newObj.
1065 return newObj;
1066 }
1067
IsArray(JSThread * thread) const1068 bool JSProxy::IsArray(JSThread *thread) const
1069 {
1070 // check stack overflow because infinite recursion may occur
1071 if (thread->DoStackLimitCheck()) {
1072 return false;
1073 }
1074 if (GetHandler(thread).IsNull()) {
1075 THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsArray: handler is null", false);
1076 }
1077 return GetTarget(thread).IsArray(thread);
1078 }
1079
GetSourceTarget(JSThread * thread) const1080 JSHandle<JSTaggedValue> JSProxy::GetSourceTarget(JSThread *thread) const
1081 {
1082 JSMutableHandle<JSProxy> proxy(thread, JSTaggedValue(this));
1083 JSMutableHandle<JSTaggedValue> target(thread, proxy->GetTarget(thread));
1084 while (target->IsJSProxy()) {
1085 proxy.Update(target.GetTaggedValue());
1086 target.Update(proxy->GetTarget(thread));
1087 }
1088 return target;
1089 }
1090 } // namespace panda::ecmascript
1091