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