• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_tagged_value.h"
17 #include "ecmascript/ecma_macros.h"
18 #include "ecmascript/ecma_vm.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/internal_call_params.h"
21 #include "ecmascript/js_array.h"
22 #include "ecmascript/js_api_arraylist.h"
23 #include "ecmascript/js_handle.h"
24 #include "ecmascript/js_primitive_ref.h"
25 #include "ecmascript/js_proxy.h"
26 #include "ecmascript/js_tagged_value-inl.h"
27 #include "ecmascript/js_thread.h"
28 #include "ecmascript/js_typed_array.h"
29 #include "ecmascript/tagged_array.h"
30 #include "js_object-inl.h"
31 #include "object_factory.h"
32 
33 namespace panda::ecmascript {
GetTypeString(JSThread * thread,PreferredPrimitiveType type)34 JSHandle<EcmaString> GetTypeString(JSThread *thread, PreferredPrimitiveType type)
35 {
36     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
37     if (type == NO_PREFERENCE) {
38         return factory->NewFromCanBeCompressString("default");
39     }
40     if (type == PREFER_NUMBER) {
41         return factory->NewFromCanBeCompressString("number");
42     }
43     return factory->NewFromCanBeCompressString("string");
44 }
45 
ToPropertyKey(JSThread * thread,const JSHandle<JSTaggedValue> & tagged)46 JSHandle<JSTaggedValue> JSTaggedValue::ToPropertyKey(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
47 {
48     if (tagged->IsStringOrSymbol() || tagged->IsNumber()) {
49         return tagged;
50     }
51     JSHandle<JSTaggedValue> key(thread, ToPrimitive(thread, tagged, PREFER_STRING));
52     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
53     if (key->IsSymbol()) {
54         return key;
55     }
56     JSHandle<EcmaString> string = ToString(thread, key);
57     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
58     return JSHandle<JSTaggedValue>::Cast(string);
59 }
60 
IsInteger() const61 bool JSTaggedValue::IsInteger() const
62 {
63     if (!IsNumber()) {
64         return false;
65     }
66 
67     if (IsInt()) {
68         return true;
69     }
70 
71     double thisValue = GetDouble();
72     // If argument is NaN, +∞, or -∞, return false.
73     if (!std::isfinite(thisValue)) {
74         return false;
75     }
76 
77     // If floor(abs(argument)) ≠ abs(argument), return false.
78     if (std::floor(std::abs(thisValue)) != std::abs(thisValue)) {
79         return false;
80     }
81 
82     return true;
83 }
84 
WithinInt32() const85 bool JSTaggedValue::WithinInt32() const
86 {
87     if (!IsNumber()) {
88         return false;
89     }
90 
91     double doubleValue = GetNumber();
92     if (bit_cast<int64_t>(doubleValue) == bit_cast<int64_t>(-0.0)) {
93         return false;
94     }
95 
96     int32_t intvalue = base::NumberHelper::DoubleToInt(doubleValue, base::INT32_BITS);
97     return doubleValue == static_cast<double>(intvalue);
98 }
99 
IsZero() const100 bool JSTaggedValue::IsZero() const
101 {
102     if (GetRawData() == VALUE_ZERO) {
103         return true;
104     }
105     if (IsDouble()) {
106         const double limit = 1e-8;
107         return (std::abs(GetDouble() - 0.0) <= limit);
108     }
109     return false;
110 }
111 
Equal(JSThread * thread,const JSHandle<JSTaggedValue> & x,const JSHandle<JSTaggedValue> & y)112 bool JSTaggedValue::Equal(JSThread *thread, const JSHandle<JSTaggedValue> &x, const JSHandle<JSTaggedValue> &y)
113 {
114     if (x->IsNumber()) {
115         if (y->IsNumber()) {
116             return StrictNumberEquals(x->ExtractNumber(), y->ExtractNumber());
117         }
118         if (y->IsString()) {
119             JSTaggedNumber yNumber = ToNumber(thread, y);
120             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
121             return StrictNumberEquals(x->ExtractNumber(), yNumber.GetNumber());
122         }
123         if (y->IsBoolean()) {
124             JSTaggedNumber yNumber = ToNumber(thread, y);
125             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
126             return StrictNumberEquals(x->ExtractNumber(), yNumber.GetNumber());
127         }
128         if (y->IsBigInt()) {
129             return Equal(thread, y, x);
130         }
131         if (y->IsHeapObject() && !y->IsSymbol()) {
132             JSHandle<JSTaggedValue> yPrimitive(thread, ToPrimitive(thread, y));
133             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
134             return Equal(thread, x, yPrimitive);
135         }
136         return false;
137     }
138 
139     if (x->IsString()) {
140         if (y->IsString()) {
141             return EcmaString::StringsAreEqual(static_cast<EcmaString *>(x->GetTaggedObject()),
142                                                static_cast<EcmaString *>(y->GetTaggedObject()));
143         }
144         if (y->IsNumber()) {
145             JSTaggedNumber xNumber = ToNumber(thread, x);
146             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
147             return StrictNumberEquals(xNumber.GetNumber(), y->ExtractNumber());
148         }
149         if (y->IsBoolean()) {
150             JSTaggedNumber xNumber = ToNumber(thread, x);
151             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
152             JSTaggedNumber yNumber = ToNumber(thread, y);
153             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
154             return StrictNumberEquals(xNumber.GetNumber(), yNumber.GetNumber());
155         }
156         if (y->IsBigInt()) {
157             return Equal(thread, y, x);
158         }
159         if (y->IsHeapObject() && !y->IsSymbol()) {
160             JSHandle<JSTaggedValue> yPrimitive(thread, ToPrimitive(thread, y));
161             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
162             return Equal(thread, x, yPrimitive);
163         }
164         return false;
165     }
166 
167     if (x->IsBoolean()) {
168         JSTaggedNumber xNumber = ToNumber(thread, x);
169         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
170         return Equal(thread, JSHandle<JSTaggedValue>(thread, xNumber), y);
171     }
172 
173     if (x->IsSymbol()) {
174         if (y->IsSymbol()) {
175             return x.GetTaggedValue() == y.GetTaggedValue();
176         }
177         if (y->IsBigInt()) {
178             return Equal(thread, y, x);
179         }
180         if (y->IsHeapObject()) {
181             JSHandle<JSTaggedValue> yPrimitive(thread, ToPrimitive(thread, y));
182             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
183             return Equal(thread, x, yPrimitive);
184         }
185         return false;
186     }
187 
188     if (x->IsBigInt()) {
189         if (y->IsBigInt()) {
190             return BigInt::Equal(x.GetTaggedValue(), y.GetTaggedValue());
191         }
192         if (y->IsString()) {
193             JSHandle<JSTaggedValue> yNumber(thread, base::NumberHelper::StringToBigInt(thread, y));
194             if (!yNumber->IsBigInt()) {
195                 return false;
196             }
197             return BigInt::Equal(x.GetTaggedValue(), yNumber.GetTaggedValue());
198         }
199         if (y->IsBoolean()) {
200             JSHandle<JSTaggedValue> yNumber(thread, ToBigInt(thread, y));
201             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
202             return BigInt::Equal(x.GetTaggedValue(), yNumber.GetTaggedValue());
203         }
204         if (y->IsNumber()) {
205             JSHandle<BigInt> bigint = JSHandle<BigInt>::Cast(x);
206             return BigInt::CompareWithNumber(thread, bigint, y) == ComparisonResult::EQUAL;
207         }
208         if (y->IsHeapObject() && !y->IsSymbol()) {
209             JSHandle<JSTaggedValue> yPrimitive(thread, ToPrimitive(thread, y));
210             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
211             return Equal(thread, x, yPrimitive);
212         }
213         return false;
214     }
215 
216     if (x->IsHeapObject()) {
217         if (y->IsHeapObject()) {
218             // if same type, must call Type::StrictEqual()
219             JSType xType = x.GetTaggedValue().GetTaggedObject()->GetClass()->GetObjectType();
220             JSType yType = y.GetTaggedValue().GetTaggedObject()->GetClass()->GetObjectType();
221             if (xType == yType) {
222                 return StrictEqual(thread, x, y);
223             }
224         }
225         if (y->IsNumber() || y->IsStringOrSymbol() || y->IsBoolean() || y->IsBigInt()) {
226             JSHandle<JSTaggedValue> x_primitive(thread, ToPrimitive(thread, x));
227             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
228             return Equal(thread, x_primitive, y);
229         }
230         return false;
231     }
232 
233     if (x->IsNull() && y->IsNull()) {
234         return true;
235     }
236 
237     if (x->IsUndefined() && y->IsUndefined()) {
238         return true;
239     }
240 
241     if (x->IsNull() && y->IsUndefined()) {
242         return true;
243     }
244 
245     if (x->IsUndefined() && y->IsNull()) {
246         return true;
247     }
248 
249     return false;
250 }
251 
Compare(JSThread * thread,const JSHandle<JSTaggedValue> & x,const JSHandle<JSTaggedValue> & y)252 ComparisonResult JSTaggedValue::Compare(JSThread *thread, const JSHandle<JSTaggedValue> &x,
253                                         const JSHandle<JSTaggedValue> &y)
254 {
255     JSHandle<JSTaggedValue> primX(thread, ToPrimitive(thread, x));
256     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
257     JSHandle<JSTaggedValue> primY(thread, ToPrimitive(thread, y));
258     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
259     if (primX->IsString() && primY->IsString()) {
260         auto xString = static_cast<EcmaString *>(primX->GetTaggedObject());
261         auto yString = static_cast<EcmaString *>(primY->GetTaggedObject());
262         int result = xString->Compare(yString);
263         if (result < 0) {
264             return ComparisonResult::LESS;
265         }
266         if (result == 0) {
267             return ComparisonResult::EQUAL;
268         }
269         return ComparisonResult::GREAT;
270     }
271     if (primX->IsBigInt()) {
272         if (primY->IsNumber()) {
273             JSHandle<BigInt> bigint = JSHandle<BigInt>::Cast(primX);
274             return BigInt::CompareWithNumber(thread, bigint, primY);
275         } else if (primY->IsString()) {
276             JSHandle<JSTaggedValue> bigY(thread, base::NumberHelper::StringToBigInt(thread, primY));
277             if (!bigY->IsBigInt()) {
278                 return ComparisonResult::UNDEFINED;
279             }
280             return BigInt::Compare(thread, primX.GetTaggedValue(), bigY.GetTaggedValue());
281         } else {
282             JSHandle<JSTaggedValue> bigY(thread, ToBigInt(thread, primY));
283             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
284             return BigInt::Compare(thread, primX.GetTaggedValue(), bigY.GetTaggedValue());
285         }
286     }
287     if (primY->IsBigInt()) {
288         ComparisonResult res = Compare(thread, primY, primX);
289         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
290         if (res == ComparisonResult::GREAT) {
291             return ComparisonResult::LESS;
292         } else if (res == ComparisonResult::LESS) {
293             return ComparisonResult::GREAT;
294         }
295         return res;
296     }
297     JSTaggedNumber xNumber = ToNumber(thread, x);
298     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
299     JSTaggedNumber yNumber = ToNumber(thread, y);
300     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED);
301     return StrictNumberCompare(xNumber.GetNumber(), yNumber.GetNumber());
302 }
303 
IsSameTypeOrHClass(JSTaggedValue x,JSTaggedValue y)304 bool JSTaggedValue::IsSameTypeOrHClass(JSTaggedValue x, JSTaggedValue y)
305 {
306     if (x.IsNumber() && y.IsNumber()) {
307         return true;
308     }
309     if (x.IsBoolean() && y.IsBoolean()) {
310         return true;
311     }
312     if (x.IsString() && y.IsString()) {
313         return true;
314     }
315     if (x.IsHeapObject() && y.IsHeapObject()) {
316         return x.GetTaggedObject()->GetClass() == y.GetTaggedObject()->GetClass();
317     }
318 
319     return false;
320 }
321 
ToPrimitive(JSThread * thread,const JSHandle<JSTaggedValue> & tagged,PreferredPrimitiveType type)322 JSTaggedValue JSTaggedValue::ToPrimitive(JSThread *thread, const JSHandle<JSTaggedValue> &tagged,
323                                          PreferredPrimitiveType type)
324 {
325     if (tagged->IsECMAObject()) {
326         EcmaVM *vm = thread->GetEcmaVM();
327         JSHandle<JSTaggedValue> keyString = vm->GetGlobalEnv()->GetToPrimitiveSymbol();
328 
329         JSHandle<JSTaggedValue> exoticToprim = JSObject::GetMethod(thread, tagged, keyString);
330         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
331         if (!exoticToprim->IsUndefined()) {
332             JSTaggedValue value = GetTypeString(thread, type).GetTaggedValue();
333             InternalCallParams *arguments = thread->GetInternalCallParams();
334             arguments->MakeArgv(value);
335             JSTaggedValue valueResult = JSFunction::Call(thread, exoticToprim, tagged, 1, arguments->GetArgv());
336             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
337             if (!valueResult.IsECMAObject()) {
338                 return valueResult;
339             }
340             THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Exception());
341         } else {
342             type = (type == NO_PREFERENCE) ? PREFER_NUMBER : type;
343             return OrdinaryToPrimitive(thread, tagged, type);
344         }
345     }
346     return tagged.GetTaggedValue();
347 }
348 
OrdinaryToPrimitive(JSThread * thread,const JSHandle<JSTaggedValue> & tagged,PreferredPrimitiveType type)349 JSTaggedValue JSTaggedValue::OrdinaryToPrimitive(JSThread *thread, const JSHandle<JSTaggedValue> &tagged,
350                                                  PreferredPrimitiveType type)
351 {
352     static_assert(PREFER_NUMBER == 0 && PREFER_STRING == 1);
353     ASSERT(tagged->IsECMAObject());
354     auto globalConst = thread->GlobalConstants();
355     for (uint8_t i = 0; i < 2; i++) {  // 2: 2 means value has 2 target types, string or value.
356         JSHandle<JSTaggedValue> keyString;
357         if ((type ^ i) != 0) {
358             keyString = globalConst->GetHandledToStringString();
359         } else {
360             keyString = globalConst->GetHandledValueOfString();
361         }
362         JSHandle<JSTaggedValue> entryfunc = GetProperty(thread, tagged, keyString).GetValue();
363         if (entryfunc->IsCallable()) {
364             JSTaggedValue valueResult = JSFunction::Call(thread, entryfunc, tagged, 0, nullptr);
365             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
366             if (!valueResult.IsECMAObject()) {
367                 return valueResult;
368             }
369         }
370     }
371     THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a illegal value to a Primitive", JSTaggedValue::Undefined());
372 }
373 
ToString(JSThread * thread,const JSHandle<JSTaggedValue> & tagged)374 JSHandle<EcmaString> JSTaggedValue::ToString(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
375 {
376     if (tagged->IsString()) {
377         return JSHandle<EcmaString>(tagged);
378     }
379     auto globalConst = thread->GlobalConstants();
380     if (tagged->IsSpecial()) {
381         switch (tagged->GetRawData()) {
382             case VALUE_UNDEFINED: {
383                 return JSHandle<EcmaString>(globalConst->GetHandledUndefinedString());
384             }
385             case VALUE_NULL: {
386                 return JSHandle<EcmaString>(globalConst->GetHandledNullString());
387             }
388             case VALUE_TRUE: {
389                 return JSHandle<EcmaString>(globalConst->GetHandledTrueString());
390             }
391             case VALUE_FALSE: {
392                 return JSHandle<EcmaString>(globalConst->GetHandledFalseString());
393             }
394             case VALUE_HOLE: {
395                 return JSHandle<EcmaString>(globalConst->GetHandledEmptyString());
396             }
397             default:
398                 break;
399         }
400     }
401 
402     if (tagged->IsNumber()) {
403         return base::NumberHelper::NumberToString(thread, tagged.GetTaggedValue());
404     }
405 
406     if (tagged->IsBigInt()) {
407         JSHandle<BigInt> taggedValue(tagged);
408         return BigInt::ToString(thread, taggedValue);
409     }
410 
411     auto emptyStr = globalConst->GetHandledEmptyString();
412     if (tagged->IsECMAObject()) {
413         JSHandle<JSTaggedValue> primValue(thread, ToPrimitive(thread, tagged, PREFER_STRING));
414         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<EcmaString>(emptyStr));
415         return ToString(thread, primValue);
416     }
417     // Already Include Symbol
418     THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a illegal value to a String", JSHandle<EcmaString>(emptyStr));
419 }
420 
CanonicalNumericIndexString(JSThread * thread,const JSHandle<JSTaggedValue> & tagged)421 JSTaggedValue JSTaggedValue::CanonicalNumericIndexString(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
422 {
423     JSHandle<EcmaString> str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-0");
424     if (tagged->IsString()) {
425         if (EcmaString::StringsAreEqual(static_cast<EcmaString *>(tagged->GetTaggedObject()), *str)) {
426             return JSTaggedValue(-0.0);
427         }
428         JSHandle<JSTaggedValue> tmp(thread, ToNumber(thread, tagged));
429         if (SameValue(ToString(thread, tmp).GetTaggedValue(), tagged.GetTaggedValue())) {
430             return tmp.GetTaggedValue();
431         }
432     }
433     return JSTaggedValue::Undefined();
434 }
435 
ToObject(JSThread * thread,const JSHandle<JSTaggedValue> & tagged)436 JSHandle<JSObject> JSTaggedValue::ToObject(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
437 {
438     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
439     if (tagged->IsInt() || tagged->IsDouble()) {
440         return JSHandle<JSObject>::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_NUMBER, tagged));
441     }
442 
443     switch (tagged->GetRawData()) {
444         case JSTaggedValue::VALUE_UNDEFINED: {
445             THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a UNDEFINED value to a JSObject",
446                                         JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
447         }
448         case JSTaggedValue::VALUE_HOLE: {
449             THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a HOLE value to a JSObject",
450                                         JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
451         }
452         case JSTaggedValue::VALUE_NULL: {
453             THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a NULL value to a JSObject",
454                                         JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
455         }
456         case JSTaggedValue::VALUE_TRUE:
457         case JSTaggedValue::VALUE_FALSE: {
458             return JSHandle<JSObject>::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_BOOLEAN, tagged));
459         }
460         default: {
461             break;
462         }
463     }
464 
465     if (tagged->IsECMAObject()) {
466         return JSHandle<JSObject>::Cast(tagged);
467     }
468     if (tagged->IsSymbol()) {
469         return JSHandle<JSObject>::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_SYMBOL, tagged));
470     }
471     if (tagged->IsString()) {
472         return JSHandle<JSObject>::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_STRING, tagged));
473     }
474     if (tagged->IsBigInt()) {
475         return JSHandle<JSObject>::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_BIGINT, tagged));
476     }
477     THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Unknown object value to a JSObject",
478                                 JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
479 }
480 
481 // 7.3.1 Get ( O, P )
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)482 OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
483                                            const JSHandle<JSTaggedValue> &key)
484 {
485     if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) {
486         THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object",
487                                     OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
488     }
489     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
490 
491     if (obj->IsJSProxy()) {
492         return JSProxy::GetProperty(thread, JSHandle<JSProxy>(obj), key);
493     }
494     if (obj->IsTypedArray()) {
495         return JSTypedArray::GetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key));
496     }
497 
498     return JSObject::GetProperty(thread, obj, key);
499 }
500 
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t key)501 OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t key)
502 {
503     if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) {
504         THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object",
505                                     OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
506     }
507 
508     if (obj->IsJSProxy()) {
509         JSHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue(key));
510         return JSProxy::GetProperty(thread, JSHandle<JSProxy>(obj), keyHandle);
511     }
512 
513     if (obj->IsTypedArray()) {
514         return JSTypedArray::GetProperty(thread, obj, key);
515     }
516 
517     return JSObject::GetProperty(thread, obj, key);
518 }
519 
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & receiver)520 OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
521                                            const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &receiver)
522 {
523     if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) {
524         THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object",
525                                     OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
526     }
527     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
528 
529     if (obj->IsJSProxy()) {
530         return JSProxy::GetProperty(thread, JSHandle<JSProxy>(obj), key, receiver);
531     }
532     if (obj->IsTypedArray()) {
533         return JSTypedArray::GetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), receiver);
534     }
535 
536     return JSObject::GetProperty(thread, obj, key, receiver);
537 }
538 
539 // 7.3.3 Set (O, P, V, Throw)
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,bool mayThrow)540 bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
541                                 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value, bool mayThrow)
542 {
543     if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) {
544         THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false);
545     }
546 
547     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
548 
549     // 4. Let success be O.[[Set]](P, V, O).
550     bool success = false;
551     if (obj->IsJSProxy()) {
552         success = JSProxy::SetProperty(thread, JSHandle<JSProxy>(obj), key, value, mayThrow);
553     } else if (obj->IsTypedArray()) {
554         success = JSTypedArray::SetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), value, mayThrow);
555     } else {
556         success = JSObject::SetProperty(thread, obj, key, value, mayThrow);
557     }
558     // 5. ReturnIfAbrupt(success).
559     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success);
560     // 6. If success is false and Throw is true, throw a TypeError exception.
561     // have done in JSObject::SetPropert.
562     return success;
563 }
564 
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t key,const JSHandle<JSTaggedValue> & value,bool mayThrow)565 bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t key,
566                                 const JSHandle<JSTaggedValue> &value, bool mayThrow)
567 {
568     if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) {
569         THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false);
570     }
571 
572     // 4. Let success be O.[[Set]](P, V, O).
573     bool success = false;
574     if (obj->IsJSProxy()) {
575         JSHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue(key));
576         success = JSProxy::SetProperty(thread, JSHandle<JSProxy>(obj), keyHandle, value, mayThrow);
577     } else if (obj->IsTypedArray()) {
578         JSHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue(key));
579         success = JSTypedArray::SetProperty(
580             thread, obj, JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, keyHandle)), value, mayThrow);
581     } else {
582         success = JSObject::SetProperty(thread, obj, key, value, mayThrow);
583     }
584     // 5. ReturnIfAbrupt(success).
585     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success);
586     // 6. If success is false and Throw is true, throw a TypeError exception.
587     // have done in JSObject::SetPropert.
588     return success;
589 }
590 
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & receiver,bool mayThrow)591 bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
592                                 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
593                                 const JSHandle<JSTaggedValue> &receiver, bool mayThrow)
594 {
595     if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) {
596         THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false);
597     }
598 
599     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
600 
601     // 4. Let success be O.[[Set]](P, V, O).
602     bool success = false;
603     if (obj->IsJSProxy()) {
604         success = JSProxy::SetProperty(thread, JSHandle<JSProxy>(obj), key, value, receiver, mayThrow);
605     } else if (obj->IsTypedArray()) {
606         success =
607             JSTypedArray::SetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), value, receiver, mayThrow);
608     } else {
609         success = JSObject::SetProperty(thread, obj, key, value, receiver, mayThrow);
610     }
611     // 5. ReturnIfAbrupt(success).
612     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success);
613     // 6. If success is false and Throw is true, throw a TypeError exception.
614     // have done in JSObject::SetPropert.
615     return success;
616 }
617 
DeleteProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)618 bool JSTaggedValue::DeleteProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
619                                    const JSHandle<JSTaggedValue> &key)
620 {
621     if (obj->IsJSProxy()) {
622         return JSProxy::DeleteProperty(thread, JSHandle<JSProxy>(obj), key);
623     }
624 
625     if (obj->IsSpecialContainer()) {
626         THROW_TYPE_ERROR_AND_RETURN(thread, "Can not delete property in Container Object", false);
627     }
628 
629     return JSObject::DeleteProperty(thread, JSHandle<JSObject>(obj), key);
630 }
631 
632 // 7.3.8 DeletePropertyOrThrow (O, P)
DeletePropertyOrThrow(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)633 bool JSTaggedValue::DeletePropertyOrThrow(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
634                                           const JSHandle<JSTaggedValue> &key)
635 {
636     if (!obj->IsECMAObject()) {
637         THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", false);
638     }
639     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
640 
641     // 3. Let success be O.[[Delete]](P).
642     bool success = DeleteProperty(thread, obj, key);
643 
644     // 4. ReturnIfAbrupt(success).
645     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success);
646     // 5. If success is false, throw a TypeError exception
647     if (!success) {
648         THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot delete property", false);
649     }
650     return success;
651 }
652 
653 // 7.3.7 DefinePropertyOrThrow (O, P, desc)
DefinePropertyOrThrow(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)654 bool JSTaggedValue::DefinePropertyOrThrow(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
655                                           const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc)
656 {
657     // 1. Assert: Type(O) is Object.
658     // 2. Assert: IsPropertyKey(P) is true.
659     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
660     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
661     // 3. Let success be ? O.[[DefineOwnProperty]](P, desc).
662     bool success = DefineOwnProperty(thread, obj, key, desc);
663     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
664     // 4. If success is false, throw a TypeError exception.
665     if (!success) {
666         THROW_TYPE_ERROR_AND_RETURN(thread, "", false);
667     }
668     return success;
669 }
670 
DefineOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)671 bool JSTaggedValue::DefineOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
672                                       const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc)
673 {
674     if (obj->IsJSArray()) {
675         return JSArray::DefineOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
676     }
677 
678     if (obj->IsJSProxy()) {
679         return JSProxy::DefineOwnProperty(thread, JSHandle<JSProxy>(obj), key, desc);
680     }
681 
682     if (obj->IsTypedArray()) {
683         return JSTypedArray::DefineOwnProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), desc);
684     }
685 
686     if (obj->IsSpecialContainer()) {
687         THROW_TYPE_ERROR_AND_RETURN(thread, "Can not defineProperty on Container Object", false);
688     }
689 
690     return JSObject::DefineOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
691 }
692 
GetOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)693 bool JSTaggedValue::GetOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
694                                    const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
695 {
696     if (obj->IsJSProxy()) {
697         return JSProxy::GetOwnProperty(thread, JSHandle<JSProxy>(obj), key, desc);
698     }
699     if (obj->IsTypedArray()) {
700         return JSTypedArray::GetOwnProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), desc);
701     }
702     if (obj->IsSpecialContainer()) {
703         return GetContainerProperty(thread, obj, key, desc);
704     }
705     return JSObject::GetOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
706 }
707 
SetPrototype(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & proto)708 bool JSTaggedValue::SetPrototype(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
709                                  const JSHandle<JSTaggedValue> &proto)
710 {
711     if (obj->IsJSProxy()) {
712         return JSProxy::SetPrototype(thread, JSHandle<JSProxy>(obj), proto);
713     }
714     if (obj->IsSpecialContainer()) {
715         THROW_TYPE_ERROR_AND_RETURN(thread, "Can not set Prototype on Container Object", false);
716     }
717 
718     return JSObject::SetPrototype(thread, JSHandle<JSObject>(obj), proto);
719 }
720 
PreventExtensions(JSThread * thread,const JSHandle<JSTaggedValue> & obj)721 bool JSTaggedValue::PreventExtensions(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
722 {
723     if (obj->IsJSProxy()) {
724         return JSProxy::PreventExtensions(thread, JSHandle<JSProxy>(obj));
725     }
726     return JSObject::PreventExtensions(thread, JSHandle<JSObject>(obj));
727 }
728 
GetOwnPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & obj)729 JSHandle<TaggedArray> JSTaggedValue::GetOwnPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
730 {
731     if (obj->IsJSProxy()) {
732         return JSProxy::OwnPropertyKeys(thread, JSHandle<JSProxy>(obj));
733     }
734     if (obj->IsTypedArray()) {
735         return JSTypedArray::OwnPropertyKeys(thread, obj);
736     }
737     if (obj->IsSpecialContainer()) {
738         return GetOwnContainerPropertyKeys(thread, obj);
739     }
740     return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>(obj));
741 }
742 
743 // 7.3.10 HasProperty (O, P)
HasProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)744 bool JSTaggedValue::HasProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
745                                 const JSHandle<JSTaggedValue> &key)
746 {
747     if (obj->IsJSProxy()) {
748         return JSProxy::HasProperty(thread, JSHandle<JSProxy>(obj), key);
749     }
750     if (obj->IsTypedArray()) {
751         return JSTypedArray::HasProperty(thread, obj, JSTypedArray::ToPropKey(thread, key));
752     }
753     if (obj->IsSpecialContainer()) {
754         return HasContainerProperty(thread, obj, key);
755     }
756     return JSObject::HasProperty(thread, JSHandle<JSObject>(obj), key);
757 }
758 
HasProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t key)759 bool JSTaggedValue::HasProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t key)
760 {
761     if (obj->IsJSProxy()) {
762         JSHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue(key));
763         return JSProxy::HasProperty(thread, JSHandle<JSProxy>(obj), keyHandle);
764     }
765     if (obj->IsTypedArray()) {
766         JSHandle<JSTaggedValue> key_handle(thread, JSTaggedValue(key));
767         return JSTypedArray::HasProperty(thread, obj, JSHandle<JSTaggedValue>(ToString(thread, key_handle)));
768     }
769     if (obj->IsSpecialContainer()) {
770         return HasContainerProperty(thread, obj, JSHandle<JSTaggedValue>(thread, JSTaggedValue(key)));
771     }
772     return JSObject::HasProperty(thread, JSHandle<JSObject>(obj), key);
773 }
774 
775 // 7.3.11 HasOwnProperty (O, P)
HasOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)776 bool JSTaggedValue::HasOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
777                                    const JSHandle<JSTaggedValue> &key)
778 {
779     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
780 
781     PropertyDescriptor desc(thread);
782     return JSTaggedValue::GetOwnProperty(thread, obj, key, desc);
783 }
784 
GlobalHasOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & key)785 bool JSTaggedValue::GlobalHasOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &key)
786 {
787     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
788 
789     PropertyDescriptor desc(thread);
790     return JSObject::GlobalGetOwnProperty(thread, key, desc);
791 }
792 
ToIndex(JSThread * thread,const JSHandle<JSTaggedValue> & tagged)793 JSTaggedNumber JSTaggedValue::ToIndex(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
794 {
795     if (tagged->IsInt() && tagged->GetInt() >= 0) {
796         return JSTaggedNumber(tagged.GetTaggedValue());
797     }
798     if (tagged->IsUndefined()) {
799         return JSTaggedNumber(0);
800     }
801     JSTaggedNumber integerIndex = ToNumber(thread, tagged);
802     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception());
803     if (integerIndex.IsInt() && integerIndex.GetInt() >= 0) {
804         return integerIndex;
805     }
806     double len = base::NumberHelper::TruncateDouble(integerIndex.GetNumber());
807     if (len < 0.0 || len > SAFE_NUMBER) {
808         THROW_RANGE_ERROR_AND_RETURN(thread, "integerIndex < 0 or integerIndex > SAFE_NUMBER",
809                                      JSTaggedNumber::Exception());
810     }
811     return JSTaggedNumber(len);
812 }
813 
ToPrototypeOrObj(JSThread * thread,const JSHandle<JSTaggedValue> & obj)814 JSHandle<JSTaggedValue> JSTaggedValue::ToPrototypeOrObj(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
815 {
816     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
817 
818     if (obj->IsNumber()) {
819         return JSHandle<JSTaggedValue>(thread,
820                                        env->GetNumberFunction().GetObject<JSFunction>()->GetFunctionPrototype());
821     }
822     if (obj->IsBoolean()) {
823         return JSHandle<JSTaggedValue>(thread,
824                                        env->GetBooleanFunction().GetObject<JSFunction>()->GetFunctionPrototype());
825     }
826     if (obj->IsString()) {
827         return JSHandle<JSTaggedValue>(thread,
828                                        env->GetStringFunction().GetObject<JSFunction>()->GetFunctionPrototype());
829     }
830     if (obj->IsSymbol()) {
831         return JSHandle<JSTaggedValue>(thread,
832                                        env->GetSymbolFunction().GetObject<JSFunction>()->GetFunctionPrototype());
833     }
834     if (obj->IsBigInt()) {
835         return JSHandle<JSTaggedValue>(thread,
836                                        env->GetBigIntFunction().GetObject<JSFunction>()->GetFunctionPrototype());
837     }
838     return obj;
839 }
840 
GetSuperBase(JSThread * thread,const JSHandle<JSTaggedValue> & obj)841 JSTaggedValue JSTaggedValue::GetSuperBase(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
842 {
843     if (obj->IsUndefined()) {
844         return JSTaggedValue::Undefined();
845     }
846 
847     ASSERT(obj->IsECMAObject());
848     return JSObject::Cast(obj.GetTaggedValue())->GetPrototype(thread);
849 }
850 
HasContainerProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)851 bool JSTaggedValue::HasContainerProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
852                                          const JSHandle<JSTaggedValue> &key)
853 {
854     auto *hclass = obj->GetTaggedObject()->GetClass();
855     JSType jsType = hclass->GetObjectType();
856     switch (jsType) {
857         case JSType::JS_API_ARRAY_LIST: {
858             return JSHandle<JSAPIArrayList>::Cast(obj)->Has(key.GetTaggedValue());
859         }
860         case JSType::JS_QUEUE:
861             break;
862         case JSType::JS_API_TREE_MAP:
863         case JSType::JS_API_TREE_SET: {
864             return JSObject::HasProperty(thread, JSHandle<JSObject>(obj), key);
865         }
866         default: {
867             UNREACHABLE();
868         }
869     }
870     return false;
871 }
872 
GetOwnContainerPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & obj)873 JSHandle<TaggedArray> JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
874 {
875     auto *hclass = obj->GetTaggedObject()->GetClass();
876     JSType jsType = hclass->GetObjectType();
877     switch (jsType) {
878         case JSType::JS_API_ARRAY_LIST: {
879             return JSAPIArrayList::OwnKeys(thread, JSHandle<JSAPIArrayList>::Cast(obj));
880         }
881         case JSType::JS_QUEUE:
882             break;
883         case JSType::JS_API_TREE_MAP:
884         case JSType::JS_API_TREE_SET: {
885             return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>(obj));
886         }
887         default: {
888             UNREACHABLE();
889         }
890     }
891     return thread->GetEcmaVM()->GetFactory()->EmptyArray();
892 }
893 
GetContainerProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)894 bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
895                                          const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
896 {
897     auto *hclass = obj->GetTaggedObject()->GetClass();
898     JSType jsType = hclass->GetObjectType();
899     switch (jsType) {
900         case JSType::JS_API_ARRAY_LIST: {
901             return JSAPIArrayList::GetOwnProperty(thread, JSHandle<JSAPIArrayList>::Cast(obj), key, desc);
902         }
903         case JSType::JS_QUEUE:
904             break;
905         case JSType::JS_API_TREE_MAP:
906         case JSType::JS_API_TREE_SET: {
907             return JSObject::GetOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
908         }
909         default: {
910             UNREACHABLE();
911         }
912     }
913     return false;
914 }
ToNumeric(JSThread * thread,const JSHandle<JSTaggedValue> & tagged)915 JSTaggedValue JSTaggedValue::ToNumeric(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
916 {
917     // 1. Let primValue be ? ToPrimitive(value, number)
918     JSHandle<JSTaggedValue> primValue(thread, ToPrimitive(thread, tagged, PREFER_NUMBER));
919     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
920     // 2. If Type(primValue) is BigInt, return primValue.
921     if (primValue->IsBigInt()) {
922         return primValue.GetTaggedValue();
923     }
924     // 3. Return ? ToNumber(primValue).
925     JSTaggedNumber number = ToNumber(thread, primValue);
926     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
927     return number;
928 }
929 }  // namespace panda::ecmascript
930