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