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