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