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_object-inl.h"
17
18 #include "ecmascript/accessor_data.h"
19 #include "ecmascript/ecma_macros.h"
20 #include "ecmascript/ecma_vm.h"
21 #include "ecmascript/global_dictionary-inl.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/js_for_in_iterator.h"
24 #include "ecmascript/js_hclass.h"
25 #include "ecmascript/js_iterator.h"
26 #include "ecmascript/js_primitive_ref.h"
27 #include "ecmascript/js_thread.h"
28 #include "ecmascript/object_factory.h"
29 #include "ecmascript/object_fast_operator-inl.h"
30 #include "ecmascript/property_attributes.h"
31 #include "ecmascript/tagged_array-inl.h"
32
33 namespace panda::ecmascript {
PropertyAttributes(const PropertyDescriptor & desc)34 PropertyAttributes::PropertyAttributes(const PropertyDescriptor &desc)
35 {
36 DISALLOW_GARBAGE_COLLECTION;
37 if (desc.HasWritable()) {
38 SetWritable(desc.IsWritable());
39 }
40
41 if (desc.HasEnumerable()) {
42 SetEnumerable(desc.IsEnumerable());
43 }
44
45 if (desc.HasConfigurable()) {
46 SetConfigurable(desc.IsConfigurable());
47 }
48
49 if (desc.IsAccessorDescriptor()) {
50 SetIsAccessor(true);
51 }
52 // internal accessor
53 if (desc.HasValue() && desc.GetValue()->IsAccessor()) {
54 SetIsAccessor(true);
55 }
56 }
57
GetCallTarget() const58 Method *ECMAObject::GetCallTarget() const
59 {
60 const TaggedObject *obj = this;
61 ASSERT(JSTaggedValue(obj).IsJSFunctionBase() || JSTaggedValue(obj).IsJSProxy());
62
63 JSTaggedValue value;
64 if (JSTaggedValue(obj).IsJSFunctionBase()) {
65 value = JSFunctionBase::ConstCast(obj)->GetMethod();
66 } else {
67 value = JSProxy::ConstCast(obj)->GetMethod();
68 }
69 return Method::Cast(value.GetTaggedObject());
70 }
71
GrowElementsCapacity(const JSThread * thread,const JSHandle<JSObject> & obj,uint32_t capacity)72 JSHandle<TaggedArray> JSObject::GrowElementsCapacity(const JSThread *thread, const JSHandle<JSObject> &obj,
73 uint32_t capacity)
74 {
75 uint32_t newCapacity = ComputeElementCapacity(capacity);
76 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
77 JSHandle<TaggedArray> oldElements(thread, obj->GetElements());
78 uint32_t oldLength = oldElements->GetLength();
79 JSHandle<TaggedArray> newElements = factory->CopyArray(oldElements, oldLength, newCapacity);
80
81 obj->SetElements(thread, newElements);
82 return newElements;
83 }
84
IterableToList(JSThread * thread,const JSHandle<JSTaggedValue> & items,JSTaggedValue method)85 JSHandle<JSTaggedValue> JSObject::IterableToList(JSThread *thread, const JSHandle<JSTaggedValue> &items,
86 JSTaggedValue method)
87 {
88 // 1. If method is present, then
89 // a. Let iteratorRecord be ? GetIterator(items, sync, method).
90 // 2. Else,
91 // a. Let iteratorRecord be ? GetIterator(items, sync).
92 JSHandle<JSTaggedValue> iteratorRecord;
93 JSHandle<JSTaggedValue> methodHandle(thread, method);
94 if (!methodHandle->IsUndefined()) {
95 iteratorRecord = JSIterator::GetIterator(thread, items, methodHandle);
96 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
97 } else {
98 iteratorRecord = JSIterator::GetIterator(thread, items);
99 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
100 }
101 // 3. Let values be a new empty List.
102 // 4. Let next be true.
103 JSHandle<JSArray> array = JSHandle<JSArray>::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
104 JSHandle<JSTaggedValue> valuesList = JSHandle<JSTaggedValue>::Cast(array);
105 JSMutableHandle<JSTaggedValue> next(thread, JSTaggedValue::True());
106 // 5. Repeat, while next is not false,
107 // a. Set next to ? IteratorStep(iteratorRecord).
108 // b. If next is not false, then
109 // i. Let nextValue be ? IteratorValue(next).
110 // ii. Append nextValue to the end of the List values.
111 uint32_t k = 0;
112 while (!next->IsFalse()) {
113 next.Update(JSIterator::IteratorStep(thread, iteratorRecord).GetTaggedValue());
114 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
115 if (!next->IsFalse()) {
116 JSHandle<JSTaggedValue> nextValue(JSIterator::IteratorValue(thread, next));
117 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
118 JSArray::FastSetPropertyByValue(thread, valuesList, k, nextValue);
119 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
120 k++;
121 }
122 }
123 // 6. Return values.
124 return valuesList;
125 }
126
IsRegExp(JSThread * thread,const JSHandle<JSTaggedValue> & argument)127 bool JSObject::IsRegExp(JSThread *thread, const JSHandle<JSTaggedValue> &argument)
128 {
129 if (!argument->IsECMAObject()) {
130 return false;
131 }
132 JSHandle<JSTaggedValue> matchSymbol = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol();
133 JSHandle<JSTaggedValue> isRegexp = JSObject::GetProperty(thread, argument, matchSymbol).GetValue();
134 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
135 if (!isRegexp->IsUndefined()) {
136 return isRegexp->ToBoolean();
137 }
138 JSHandle<JSObject> argumentObj = JSHandle<JSObject>::Cast(argument);
139 return argumentObj->IsJSRegExp();
140 }
141
TransitionToDictionary(const JSThread * thread,const JSHandle<JSObject> & receiver)142 JSHandle<NameDictionary> JSObject::TransitionToDictionary(const JSThread *thread, const JSHandle<JSObject> &receiver)
143 {
144 JSHandle<TaggedArray> array(thread, receiver->GetProperties());
145 JSHandle<JSHClass> jshclass(thread, receiver->GetJSHClass());
146 ASSERT(!jshclass->IsDictionaryMode());
147 uint32_t propNumber = jshclass->NumberOfProps();
148
149 ASSERT(!jshclass->GetLayout().IsNull());
150 JSHandle<LayoutInfo> layoutInfoHandle(thread, jshclass->GetLayout());
151 ASSERT(layoutInfoHandle->GetLength() != 0);
152 JSMutableHandle<NameDictionary> dict(
153 thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propNumber)));
154 JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
155 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
156 uint32_t numberInlinedProps = jshclass->GetInlinedProperties();
157 for (uint32_t i = 0; i < propNumber; i++) {
158 JSTaggedValue key = layoutInfoHandle->GetKey(i);
159 PropertyAttributes attr = layoutInfoHandle->GetAttr(i);
160 ASSERT(i == attr.GetOffset());
161 JSTaggedValue value;
162
163 if (i < numberInlinedProps) {
164 value = receiver->GetPropertyInlinedProps(i);
165 } else {
166 value = array->Get(i - numberInlinedProps);
167 }
168
169 attr.SetBoxType(PropertyBoxType::UNDEFINED);
170 valueHandle.Update(value);
171 keyHandle.Update(key);
172 JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr);
173 dict.Update(newDict);
174 }
175
176 receiver->SetProperties(thread, dict);
177 // change HClass
178 JSHClass::TransitionToDictionary(thread, receiver);
179
180 // trim in-obj properties space
181 if (numberInlinedProps > 0) {
182 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
183 uint32_t newSize = receiver->GetClass()->GetObjectSize();
184 size_t trimBytes = numberInlinedProps * JSTaggedValue::TaggedTypeSize();
185 factory->FillFreeObject(ToUintPtr(*receiver) + newSize, trimBytes, RemoveSlots::YES, ToUintPtr(*receiver));
186 }
187
188 return dict;
189 }
190
ElementsToDictionary(const JSThread * thread,JSHandle<JSObject> obj)191 void JSObject::ElementsToDictionary(const JSThread *thread, JSHandle<JSObject> obj)
192 {
193 JSHandle<TaggedArray> elements(thread, obj->GetElements());
194 ASSERT(!obj->GetJSHClass()->IsDictionaryElement());
195 uint32_t length = elements->GetLength();
196 JSMutableHandle<NumberDictionary> dict(thread, NumberDictionary::Create(thread));
197 auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes());
198 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
199 JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue ::Undefined());
200 for (uint32_t i = 0; i < length; i++) {
201 JSTaggedValue value = elements->Get(i);
202 if (value.IsHole()) {
203 continue;
204 }
205 key.Update(JSTaggedValue(i));
206 valueHandle.Update(value);
207 JSHandle<NumberDictionary> newDict = NumberDictionary::PutIfAbsent(thread, dict, key, valueHandle, attr);
208 dict.Update(newDict);
209 }
210 obj->SetElements(thread, dict);
211
212 JSHClass::TransitionElementsToDictionary(thread, obj);
213 }
214
IsArrayLengthWritable(JSThread * thread,const JSHandle<JSObject> & receiver)215 bool JSObject::IsArrayLengthWritable(JSThread *thread, const JSHandle<JSObject> &receiver)
216 {
217 auto *hclass = receiver->GetJSHClass();
218 if (!hclass->IsDictionaryMode()) {
219 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
220 PropertyAttributes attr(layoutInfo->GetAttr(JSArray::LENGTH_INLINE_PROPERTY_INDEX));
221 return attr.IsWritable();
222 }
223 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
224 ObjectOperator op(thread, receiver, lengthKey, OperatorType::OWN);
225 return op.GetAttr().IsWritable();
226 }
227
AddElementInternal(JSThread * thread,const JSHandle<JSObject> & receiver,uint32_t index,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr)228 bool JSObject::AddElementInternal(JSThread *thread, const JSHandle<JSObject> &receiver, uint32_t index,
229 const JSHandle<JSTaggedValue> &value, PropertyAttributes attr)
230 {
231 bool isDictionary = receiver->GetJSHClass()->IsDictionaryElement();
232 if (receiver->IsJSArray()) {
233 DISALLOW_GARBAGE_COLLECTION;
234 JSArray *arr = JSArray::Cast(*receiver);
235 uint32_t oldLength = arr->GetArrayLength();
236 if (index >= oldLength) {
237 if (!IsArrayLengthWritable(thread, receiver)) {
238 return false;
239 }
240 arr->SetArrayLength(thread, index + 1);
241 }
242 }
243 thread->NotifyStableArrayElementsGuardians(receiver);
244
245 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
246 if (isDictionary) {
247 ASSERT(elements->IsDictionaryMode());
248 JSHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue(static_cast<int32_t>(index)));
249 JSHandle<NumberDictionary> newDict =
250 NumberDictionary::Put(thread, JSHandle<NumberDictionary>(thread, elements), keyHandle, value, attr);
251 receiver->SetElements(thread, newDict);
252 return true;
253 }
254
255 uint32_t capacity = elements->GetLength();
256 if (index >= capacity || !attr.IsDefaultAttributes()) {
257 if (ShouldTransToDict(capacity, index) || !attr.IsDefaultAttributes()) {
258 JSObject::ElementsToDictionary(thread, receiver);
259 JSHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue(static_cast<int32_t>(index)));
260 JSHandle<NumberDictionary> dict(thread, receiver->GetElements());
261 JSHandle<NumberDictionary> newKey = NumberDictionary::Put(thread, dict, keyHandle, value, attr);
262 receiver->SetElements(thread, newKey);
263 return true;
264 }
265 elements = *JSObject::GrowElementsCapacity(thread, receiver, index + 1);
266 }
267 elements->Set(thread, index, value);
268 receiver->GetJSHClass()->UpdateRepresentation(value.GetTaggedValue());
269 return true;
270 }
271
DeletePropertyInternal(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,uint32_t index)272 void JSObject::DeletePropertyInternal(JSThread *thread, const JSHandle<JSObject> &obj,
273 const JSHandle<JSTaggedValue> &key, uint32_t index)
274 {
275 JSHandle<TaggedArray> array(thread, obj->GetProperties());
276
277 if (obj->IsJSGlobalObject()) {
278 JSHandle<GlobalDictionary> dictHandle(thread, obj->GetProperties());
279 JSHandle<GlobalDictionary> newDict = GlobalDictionary::Remove(thread, dictHandle, index);
280 obj->SetProperties(thread, newDict);
281 return;
282 }
283
284 if (!array->IsDictionaryMode()) {
285 if (obj->GetJSHClass()->IsTS()) {
286 obj->SetPropertyInlinedProps(thread, index, JSTaggedValue::Hole());
287 return;
288 }
289 JSHandle<NameDictionary> dictHandle(TransitionToDictionary(thread, obj));
290 int entry = dictHandle->FindEntry(key.GetTaggedValue());
291 ASSERT(entry != -1);
292 JSHandle<NameDictionary> newDict = NameDictionary::Remove(thread, dictHandle, entry);
293 obj->SetProperties(thread, newDict);
294 return;
295 }
296
297 JSHandle<NameDictionary> dictHandle(array);
298 JSHandle<NameDictionary> newDict = NameDictionary::Remove(thread, dictHandle, index);
299 obj->SetProperties(thread, newDict);
300 }
301
GetAllKeys(const JSThread * thread,const JSHandle<JSObject> & obj,int offset,const JSHandle<TaggedArray> & keyArray)302 void JSObject::GetAllKeys(const JSThread *thread, const JSHandle<JSObject> &obj, int offset,
303 const JSHandle<TaggedArray> &keyArray)
304
305 {
306 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
307 if (!array->IsDictionaryMode()) {
308 int end = static_cast<int>(obj->GetJSHClass()->NumberOfProps());
309 if (end > 0) {
310 LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())
311 ->GetAllKeys(thread, end, offset, *keyArray, obj);
312 }
313 return;
314 }
315
316 if (obj->IsJSGlobalObject()) {
317 GlobalDictionary *dict = GlobalDictionary::Cast(array);
318 return dict->GetAllKeys(thread, offset, *keyArray);
319 }
320
321 NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject());
322 dict->GetAllKeys(thread, offset, *keyArray);
323 }
324
325 // For Serialization use. Does not support JSGlobalObject
GetAllKeys(const JSHandle<JSObject> & obj,std::vector<JSTaggedValue> & keyVector)326 void JSObject::GetAllKeys(const JSHandle<JSObject> &obj, std::vector<JSTaggedValue> &keyVector)
327 {
328 DISALLOW_GARBAGE_COLLECTION;
329 ASSERT_PRINT(!obj->IsJSGlobalObject(), "Do not support get key of JSGlobal Object");
330 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
331 if (!array->IsDictionaryMode()) {
332 int end = static_cast<int>(obj->GetJSHClass()->NumberOfProps());
333 if (end > 0) {
334 LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())->GetAllKeys(end, keyVector, obj);
335 }
336 } else {
337 NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject());
338 dict->GetAllKeysIntoVector(keyVector);
339 }
340 }
341
GetAllEnumKeys(const JSThread * thread,const JSHandle<JSObject> & obj,int offset,uint32_t numOfKeys,uint32_t * keys)342 JSHandle<TaggedArray> JSObject::GetAllEnumKeys(const JSThread *thread, const JSHandle<JSObject> &obj, int offset,
343 uint32_t numOfKeys, uint32_t *keys)
344 {
345 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
346 if (obj->IsJSGlobalObject()) {
347 JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(numOfKeys);
348 GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject());
349 dict->GetEnumAllKeys(thread, offset, *keyArray, keys);
350 return keyArray;
351 }
352
353 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
354 if (!array->IsDictionaryMode()) {
355 JSHClass *jsHclass = obj->GetJSHClass();
356 JSTaggedValue enumCache = jsHclass->GetEnumCache();
357 if (!enumCache.IsNull()) {
358 auto keyArray = JSHandle<TaggedArray>(thread, enumCache);
359 *keys = keyArray->GetLength();
360 return keyArray;
361 }
362 JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(numOfKeys);
363 int end = static_cast<int>(jsHclass->NumberOfProps());
364 if (end > 0) {
365 LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject())
366 ->GetAllEnumKeys(thread, end, offset, *keyArray, keys, obj);
367 if (*keys == keyArray->GetLength()) {
368 jsHclass->SetEnumCache(thread, keyArray.GetTaggedValue());
369 }
370 }
371 return keyArray;
372 }
373
374 JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(numOfKeys);
375 NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject());
376 dict->GetAllEnumKeys(thread, offset, *keyArray, keys);
377 return keyArray;
378 }
379
GetAllElementKeys(JSThread * thread,const JSHandle<JSObject> & obj,int offset,const JSHandle<TaggedArray> & keyArray)380 void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
381 const JSHandle<TaggedArray> &keyArray)
382 {
383 uint32_t elementIndex = 0;
384
385 if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) {
386 elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + static_cast<uint32_t>(offset);
387 for (uint32_t i = static_cast<uint32_t>(offset); i < elementIndex; ++i) {
388 auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i));
389 keyArray->Set(thread, i, key);
390 }
391 }
392
393 JSHandle<TaggedArray> elements(thread, obj->GetElements());
394 if (!elements->IsDictionaryMode()) {
395 uint32_t elementsLen = elements->GetLength();
396 for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) {
397 if (!elements->Get(i).IsHole()) {
398 auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i));
399 keyArray->Set(thread, j++, key);
400 }
401 }
402 } else {
403 NumberDictionary::GetAllKeys(thread, JSHandle<NumberDictionary>(elements), elementIndex, keyArray);
404 }
405 }
406
GetALLElementKeysIntoVector(const JSThread * thread,const JSHandle<JSObject> & obj,std::vector<JSTaggedValue> & keyVector)407 void JSObject::GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle<JSObject> &obj,
408 std::vector<JSTaggedValue> &keyVector)
409 {
410 JSHandle<TaggedArray> elements(thread, obj->GetElements());
411 if (!elements->IsDictionaryMode()) {
412 uint32_t elementsLen = elements->GetLength();
413 for (uint32_t i = 0; i < elementsLen; ++i) {
414 if (!elements->Get(i).IsHole()) {
415 keyVector.emplace_back(JSTaggedValue(i));
416 }
417 }
418 } else {
419 JSHandle<NumberDictionary> dict = JSHandle<NumberDictionary>::Cast(elements);
420 dict->GetAllKeysIntoVector(keyVector);
421 }
422 }
423
GetEnumElementKeys(JSThread * thread,const JSHandle<JSObject> & obj,int offset,uint32_t numOfElements,uint32_t * keys)424 JSHandle<TaggedArray> JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
425 uint32_t numOfElements, uint32_t *keys)
426 {
427 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
428 JSHandle<TaggedArray> elementArray = factory->NewTaggedArray(numOfElements);
429 uint32_t elementIndex = 0;
430 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
431
432 if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) {
433 elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength();
434 *keys += elementIndex;
435 elementIndex += static_cast<uint32_t>(offset);
436 for (uint32_t i = static_cast<uint32_t>(offset); i < elementIndex; ++i) {
437 keyHandle.Update(JSTaggedValue(i));
438 auto key = JSTaggedValue::ToString(thread, keyHandle);
439 elementArray->Set(thread, i, key);
440 }
441 }
442
443 JSHandle<TaggedArray> arr(thread, obj->GetElements());
444 if (!arr->IsDictionaryMode()) {
445 uint32_t elementsLen = arr->GetLength();
446 uint32_t preElementIndex = elementIndex;
447 for (uint32_t i = 0; i < elementsLen; ++i) {
448 if (!arr->Get(i).IsHole()) {
449 keyHandle.Update(factory->NewFromASCII(ToCString(i)).GetTaggedValue());
450 elementArray->Set(thread, elementIndex++, keyHandle);
451 }
452 }
453 *keys += (elementIndex - preElementIndex);
454 } else {
455 NumberDictionary::GetAllEnumKeys(thread, JSHandle<NumberDictionary>(arr), elementIndex, elementArray, keys);
456 }
457 return elementArray;
458 }
459
GetNumberOfKeys()460 uint32_t JSObject::GetNumberOfKeys()
461 {
462 DISALLOW_GARBAGE_COLLECTION;
463 TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject());
464
465 if (!array->IsDictionaryMode()) {
466 return GetJSHClass()->NumberOfProps();
467 }
468
469 return NameDictionary::Cast(array)->EntriesCount();
470 }
471
GlobalSetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,bool mayThrow)472 bool JSObject::GlobalSetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &key,
473 const JSHandle<JSTaggedValue> &value, bool mayThrow)
474 {
475 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
476
477 ObjectOperator op(thread, key);
478 if (!op.IsFound()) {
479 PropertyAttributes attr = PropertyAttributes::Default(true, true, false);
480 op.SetAttr(attr);
481 }
482 return SetProperty(&op, value, mayThrow);
483 }
484
GetNumberOfElements()485 uint32_t JSObject::GetNumberOfElements()
486 {
487 DISALLOW_GARBAGE_COLLECTION;
488 uint32_t numOfElements = 0;
489 if (IsJSPrimitiveRef() && JSPrimitiveRef::Cast(this)->IsString()) {
490 numOfElements = JSPrimitiveRef::Cast(this)->GetStringLength();
491 }
492
493 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
494 if (!elements->IsDictionaryMode()) {
495 uint32_t elementsLen = elements->GetLength();
496 for (uint32_t i = 0; i < elementsLen; ++i) {
497 if (!elements->Get(i).IsHole()) {
498 numOfElements++;
499 }
500 }
501 } else {
502 numOfElements += static_cast<uint32_t>(NumberDictionary::Cast(elements)->EntriesCount());
503 }
504
505 return numOfElements;
506 }
507
508 // 9.1.9 [[Set]] ( P, V, Receiver)
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & receiver,bool mayThrow)509 bool JSObject::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
510 const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &receiver, bool mayThrow)
511 {
512 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
513 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
514
515 // 2 ~ 4 findProperty in Receiver, Obj and its parents
516 ObjectOperator op(thread, obj, receiver, key);
517 return SetProperty(&op, value, mayThrow);
518 }
519
SetProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,bool mayThrow)520 bool JSObject::SetProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
521 const JSHandle<JSTaggedValue> &value, bool mayThrow)
522 {
523 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject");
524 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
525
526 ObjectOperator op(thread, obj, key);
527 return SetProperty(&op, value, mayThrow);
528 }
529
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,bool mayThrow)530 bool JSObject::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
531 const JSHandle<JSTaggedValue> &value, bool mayThrow)
532 {
533 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
534 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
535
536 // 2 ~ 4 findProperty in Receiver, Obj and its parents
537 ObjectOperator op(thread, obj, key);
538 return SetProperty(&op, value, mayThrow);
539 }
540
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value,bool mayThrow)541 bool JSObject::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index,
542 const JSHandle<JSTaggedValue> &value, bool mayThrow)
543 {
544 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
545
546 ObjectOperator op(thread, obj, index);
547 return SetProperty(&op, value, mayThrow);
548 }
549
SetProperty(ObjectOperator * op,const JSHandle<JSTaggedValue> & value,bool mayThrow)550 bool JSObject::SetProperty(ObjectOperator *op, const JSHandle<JSTaggedValue> &value, bool mayThrow)
551 {
552 JSThread *thread = op->GetThread();
553
554 JSHandle<JSTaggedValue> receiver = op->GetReceiver();
555 JSHandle<JSTaggedValue> holder = op->GetHolder();
556 if (holder->IsJSProxy()) {
557 if (op->IsElement()) {
558 JSHandle<JSTaggedValue> key(thread, JSTaggedValue(op->GetElementIndex()));
559 return JSProxy::SetProperty(thread, JSHandle<JSProxy>::Cast(holder), key, value, receiver, mayThrow);
560 }
561 return JSProxy::SetProperty(thread, JSHandle<JSProxy>::Cast(holder), op->GetKey(), value, receiver, mayThrow);
562 }
563
564 // When op is not found and is not set extra attributes
565 if (!op->IsFound() && op->IsPrimitiveAttr()) {
566 op->SetAsDefaultAttr();
567 }
568
569 bool isInternalAccessor = false;
570 if (op->IsAccessorDescriptor()) {
571 JSTaggedValue ret = ShouldGetValueFromBox(op);
572 isInternalAccessor = AccessorData::Cast(ret.GetTaggedObject())->IsInternal();
573 }
574
575 // 5. If IsDataDescriptor(ownDesc) is true, then
576 if (!op->IsAccessorDescriptor() || isInternalAccessor) {
577 if (!op->IsWritable()) {
578 if (mayThrow) {
579 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false);
580 }
581 return false;
582 }
583
584 if (!receiver->IsECMAObject()) {
585 if (mayThrow) {
586 THROW_TYPE_ERROR_AND_RETURN(thread, "Receiver is not a JSObject", false);
587 }
588 return false;
589 }
590
591 if (receiver->IsJSProxy()) {
592 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
593 if (op->IsElement()) {
594 key.Update(JSTaggedValue(op->GetElementIndex()));
595 } else {
596 key.Update(op->GetKey().GetTaggedValue());
597 }
598
599 PropertyDescriptor existDesc(thread);
600 JSProxy::GetOwnProperty(thread, JSHandle<JSProxy>::Cast(receiver), key, existDesc);
601 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
602 if (!existDesc.IsEmpty()) {
603 if (existDesc.IsAccessorDescriptor()) {
604 return false;
605 }
606
607 if (!existDesc.IsWritable()) {
608 return false;
609 }
610
611 PropertyDescriptor valueDesc(thread, value);
612 return JSProxy::DefineOwnProperty(thread, JSHandle<JSProxy>::Cast(receiver), key, valueDesc);
613 }
614 return CreateDataProperty(thread, JSHandle<JSObject>(receiver), key, value);
615 }
616
617 // 5e. If existingDescriptor is not undefined, then
618 bool hasReceiver = false;
619 if (op->HasReceiver()) {
620 op->ReLookupPropertyInReceiver();
621 hasReceiver = true;
622 }
623 bool isSuccess = true;
624 if (op->IsFound() && !op->IsOnPrototype()) {
625 // i. If IsAccessorDescriptor(existingDescriptor) is true, return false.
626 if (op->IsAccessorDescriptor() && !isInternalAccessor) {
627 return false;
628 }
629
630 // ii. If existingDescriptor.[[Writable]] is false, return false.
631 if (!op->IsWritable()) {
632 if (mayThrow) {
633 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false);
634 }
635 return false;
636 }
637 isSuccess = op->UpdateDataValue(JSHandle<JSObject>(receiver), value, isInternalAccessor, mayThrow);
638 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, isSuccess);
639 } else {
640 // 5f. Else if Receiver does not currently have a property P, Return CreateDataProperty(Receiver, P, V).
641 if (!receiver->IsExtensible(thread)) {
642 if (mayThrow) {
643 THROW_TYPE_ERROR_AND_RETURN(thread, "receiver is not Extensible", false);
644 }
645 return false;
646 }
647 if (hasReceiver || isInternalAccessor) {
648 return op->AddProperty(JSHandle<JSObject>(receiver), value, PropertyAttributes::Default());
649 } else {
650 return op->AddProperty(JSHandle<JSObject>(receiver), value, op->GetAttr());
651 }
652 }
653 return isSuccess;
654 }
655 // 6. Assert: IsAccessorDescriptor(ownDesc) is true.
656 ASSERT(op->IsAccessorDescriptor());
657 // 8. If setter is undefined, return false.
658 JSTaggedValue ret = ShouldGetValueFromBox(op);
659 AccessorData *accessor = AccessorData::Cast(ret.GetTaggedObject());
660 return CallSetter(thread, *accessor, receiver, value, mayThrow);
661 }
662
CallSetter(JSThread * thread,const AccessorData & accessor,const JSHandle<JSTaggedValue> & receiver,const JSHandle<JSTaggedValue> & value,bool mayThrow)663 bool JSObject::CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle<JSTaggedValue> &receiver,
664 const JSHandle<JSTaggedValue> &value, bool mayThrow)
665 {
666 if (UNLIKELY(accessor.IsInternal())) {
667 return accessor.CallInternalSet(thread, JSHandle<JSObject>::Cast(receiver), value, mayThrow);
668 }
669 JSTaggedValue setter = accessor.GetSetter();
670 // 8. If setter is undefined, return false.
671 if (setter.IsUndefined()) {
672 if (mayThrow) {
673 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property when setter is undefined", false);
674 }
675 return false;
676 }
677
678 JSHandle<JSTaggedValue> func(thread, setter);
679 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
680 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 1);
681 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
682 info->SetCallArg(value.GetTaggedValue());
683 JSFunction::Call(info);
684
685 // 10. ReturnIfAbrupt(setterResult).
686 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
687
688 return true;
689 }
690
CallGetter(JSThread * thread,const AccessorData * accessor,const JSHandle<JSTaggedValue> & receiver)691 JSTaggedValue JSObject::CallGetter(JSThread *thread, const AccessorData *accessor,
692 const JSHandle<JSTaggedValue> &receiver)
693 {
694 JSTaggedValue getter = accessor->GetGetter();
695 // 7. If getter is undefined, return undefined.
696 if (getter.IsUndefined()) {
697 return JSTaggedValue::Undefined();
698 }
699
700 JSHandle<JSTaggedValue> func(thread, getter);
701 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
702 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 0);
703 JSTaggedValue res = JSFunction::Call(info);
704 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
705 return res;
706 }
707
708 // 9.1.8 [[Get]] (P, Receiver)
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & receiver)709 OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
710 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &receiver)
711 {
712 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
713 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
714
715 ObjectOperator op(thread, obj, receiver, key);
716 return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
717 }
718
GetProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key)719 OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle<JSObject> &obj,
720 const JSHandle<JSTaggedValue> &key)
721 {
722 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject");
723 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
724
725 ObjectOperator op(thread, obj, key);
726 return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
727 }
728
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)729 OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
730 const JSHandle<JSTaggedValue> &key)
731 {
732 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
733 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
734
735 ObjectOperator op(thread, obj, key);
736 return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
737 }
738
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index)739 OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index)
740 {
741 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
742
743 ObjectOperator op(thread, obj, index);
744 return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
745 }
746
GetPropertyFromGlobal(JSThread * thread,const JSHandle<JSTaggedValue> & key)747 OperationResult JSObject::GetPropertyFromGlobal(JSThread *thread, const JSHandle<JSTaggedValue> &key)
748 {
749 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
750
751 ObjectOperator op(thread, key);
752 return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
753 }
754
GetProperty(JSThread * thread,ObjectOperator * op)755 JSTaggedValue JSObject::GetProperty(JSThread *thread, ObjectOperator *op)
756 {
757 JSHandle<JSTaggedValue> receiver = op->GetReceiver();
758 JSHandle<JSTaggedValue> holder = op->GetHolder();
759 if (holder->IsJSProxy()) {
760 if (op->IsElement()) {
761 return JSProxy::GetProperty(thread, JSHandle<JSProxy>::Cast(holder), op->GetKey(), receiver)
762 .GetValue()
763 .GetTaggedValue();
764 }
765 return JSProxy::GetProperty(thread, JSHandle<JSProxy>::Cast(holder), op->GetKey(), receiver)
766 .GetValue()
767 .GetTaggedValue();
768 }
769
770 // 4. If desc is undefined, then
771 if (!op->IsFound()) {
772 // 4c. If obj and parent is null, return undefined.
773 return JSTaggedValue::Undefined();
774 }
775 // 5. If IsDataDescriptor(desc) is true, return desc.[[Value]]
776 JSTaggedValue ret = ShouldGetValueFromBox(op);
777 if (!op->IsAccessorDescriptor()) {
778 return ret;
779 }
780
781 // 6. Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]].
782 AccessorData *accessor = AccessorData::Cast(ret.GetTaggedObject());
783 // 8. Return Call(getter, Receiver).
784 if (UNLIKELY(accessor->IsInternal())) {
785 return accessor->CallInternalGet(thread, JSHandle<JSObject>::Cast(holder));
786 }
787 return CallGetter(thread, accessor, receiver);
788 }
789
DeleteProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key)790 bool JSObject::DeleteProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key)
791 {
792 // 1. Assert: IsPropertyKey(P) is true.
793 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
794 // 2. Let desc be O.[[GetOwnProperty]](P).
795
796 ObjectOperator op(thread, JSHandle<JSTaggedValue>(obj), key, OperatorType::OWN);
797
798 // 4. If desc is undefined, return true.
799 if (!op.IsFound()) {
800 return true;
801 }
802 // 5. If desc.[[Configurable]] is true, then
803 // a. Remove the own property with name P from O.
804 // b. Return true.
805 // 6. Return false.
806 if (op.IsConfigurable()) {
807 op.DeletePropertyInHolder();
808 return true;
809 }
810 return false;
811 }
812
GetOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)813 bool JSObject::GetOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
814 PropertyDescriptor &desc)
815 {
816 return OrdinaryGetOwnProperty(thread, obj, key, desc);
817 }
818
GlobalGetOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)819 bool JSObject::GlobalGetOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
820 {
821 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
822 ObjectOperator op(thread, key, OperatorType::OWN);
823
824 if (!op.IsFound()) {
825 return false;
826 }
827
828 op.ToPropertyDescriptor(desc);
829
830 if (desc.HasValue()) {
831 PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject());
832 JSHandle<JSTaggedValue> valueHandle(thread, cell->GetValue());
833 desc.SetValue(valueHandle);
834 }
835 ASSERT(!desc.GetValue()->IsInternalAccessor());
836 return true;
837 }
838
OrdinaryGetOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)839 bool JSObject::OrdinaryGetOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj,
840 const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
841 {
842 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
843 ObjectOperator op(thread, JSHandle<JSTaggedValue>(obj), key, OperatorType::OWN);
844
845 if (!op.IsFound()) {
846 return false;
847 }
848
849 op.ToPropertyDescriptor(desc);
850
851 if (desc.HasValue() && obj->IsJSGlobalObject()) {
852 PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject());
853 JSHandle<JSTaggedValue> valueHandle(thread, cell->GetValue());
854 desc.SetValue(valueHandle);
855 }
856
857 return true;
858 }
859
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)860 bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
861 const PropertyDescriptor &desc)
862 {
863 return OrdinaryDefineOwnProperty(thread, obj, key, desc);
864 }
865
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const PropertyDescriptor & desc)866 bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
867 const PropertyDescriptor &desc)
868 {
869 return OrdinaryDefineOwnProperty(thread, obj, index, desc);
870 }
871
872 // 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc)
OrdinaryDefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)873 bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj,
874 const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc)
875 {
876 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
877 // 1. Let current be O.[[GetOwnProperty]](P).
878 JSHandle<JSTaggedValue> objValue(obj);
879 ObjectOperator op(thread, objValue, key, OperatorType::OWN);
880
881 bool extensible = obj->IsExtensible();
882 PropertyDescriptor current(thread);
883 op.ToPropertyDescriptor(current);
884 // 4. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).
885 return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current);
886 }
887
OrdinaryDefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const PropertyDescriptor & desc)888 bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
889 const PropertyDescriptor &desc)
890 {
891 JSHandle<JSTaggedValue> objValue(obj);
892 ObjectOperator op(thread, objValue, index, OperatorType::OWN);
893
894 bool extensible = obj->IsExtensible();
895 PropertyDescriptor current(thread);
896 op.ToPropertyDescriptor(current);
897 return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current);
898 }
899
900 // 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current)
ValidateAndApplyPropertyDescriptor(ObjectOperator * op,bool extensible,const PropertyDescriptor & desc,const PropertyDescriptor & current)901 bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc,
902 const PropertyDescriptor ¤t)
903 {
904 // 2. If current is undefined, then
905 if (current.IsEmpty()) {
906 // 2a. If extensible is false, return false.
907 if (!extensible) {
908 return false;
909 }
910 if (!op->HasHolder()) {
911 return true;
912 }
913
914 // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then
915 PropertyAttributes attr(desc);
916 bool success = false;
917 if (!desc.IsAccessorDescriptor()) {
918 success = op->AddPropertyInHolder(desc.GetValue(), attr);
919 } else { // is AccessorDescriptor
920 // may GC in NewAccessorData, so we need to handle getter and setter.
921 JSThread *thread = op->GetThread();
922 JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData();
923 if (desc.HasGetter()) {
924 accessor->SetGetter(thread, desc.GetGetter());
925 }
926
927 if (desc.HasSetter()) {
928 accessor->SetSetter(thread, desc.GetSetter());
929 }
930 success = op->AddPropertyInHolder(JSHandle<JSTaggedValue>::Cast(accessor), attr);
931 }
932
933 return success;
934 }
935
936 // 3. Return true, if every field in Desc is absent
937 // 4. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the
938 // same value as the corresponding field in current when compared using the SameValue algorithm.
939 if ((!desc.HasEnumerable() || desc.IsEnumerable() == current.IsEnumerable()) &&
940 (!desc.HasConfigurable() || desc.IsConfigurable() == current.IsConfigurable()) &&
941 (!desc.HasValue() || JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) &&
942 (!desc.HasWritable() || (current.IsWritable() == desc.IsWritable())) &&
943 (!desc.HasGetter() ||
944 (current.HasGetter() && JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter()))) &&
945 (!desc.HasSetter() ||
946 (current.HasSetter() && JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())))) {
947 return true;
948 }
949
950 // 5. If the [[Configurable]] field of current is false, then
951 if (!current.IsConfigurable()) {
952 // 5a. Return false, if the [[Configurable]] field of Desc is true.
953 if (desc.HasConfigurable() && desc.IsConfigurable()) {
954 return false;
955 }
956 // b. Return false, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current
957 // and Desc are the Boolean negation of each other.
958 if (desc.HasEnumerable() && (desc.IsEnumerable() != current.IsEnumerable())) {
959 return false;
960 }
961 }
962
963 // 6. If IsGenericDescriptor(Desc) is true, no further validation is required.
964 if (desc.IsGenericDescriptor()) {
965 // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
966 } else if (current.IsDataDescriptor() != desc.IsDataDescriptor()) {
967 // 7a. Return false, if the [[Configurable]] field of current is false.
968 if (!current.IsConfigurable()) {
969 return false;
970 }
971 // 7b. If IsDataDescriptor(current) is true, then
972 if (current.IsDataDescriptor()) {
973 // 7bi. If O is not undefined, convert the property named P of object O from a data property to an
974 // accessor property. Preserve the existing values of the converted property’s [[Configurable]] and
975 // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values.
976 } else {
977 // 7ci. If O is not undefined, convert the property named P of object O from an accessor property to a
978 // data property. Preserve the existing values of the converted property’s [[Configurable]] and
979 // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values.
980 }
981 // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
982 } else if (current.IsDataDescriptor() && desc.IsDataDescriptor()) {
983 // 8a. If the [[Configurable]] field of current is false, then
984 if (!current.IsConfigurable()) {
985 // 8a i. Return false, if the [[Writable]] field of current is false and the [[Writable]] field of Desc
986 // is true.
987 if (!current.IsWritable() && desc.HasWritable() && desc.IsWritable()) {
988 return false;
989 }
990 // 8a ii. If the [[Writable]] field of current is false, then
991 if (!current.IsWritable()) {
992 if (desc.HasValue() && !JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) {
993 return false;
994 }
995 }
996 }
997 // 8b. Else the [[Configurable]] field of current is true, so any change is acceptable.
998 } else { // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true,
999 // 9a. If the [[Configurable]] field of current is false, then
1000 if (!current.IsConfigurable()) {
1001 // i. Return false, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]])
1002 // is false.
1003 if (desc.HasSetter() && !JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())) {
1004 return false;
1005 }
1006 // ii. Return false, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]],
1007 // current.[[Get]]) is false.
1008 if (desc.HasGetter() && !JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter())) {
1009 return false;
1010 }
1011 }
1012 }
1013
1014 if (op->HasHolder()) {
1015 // 10. If O is not undefined, then
1016 // a. For each field of Desc that is present, set the corresponding attribute of the property named P of object
1017 // O to the value of the field.
1018 return op->WriteDataPropertyInHolder(desc);
1019 }
1020 return true;
1021 }
1022
1023 // 9.1.6.2 IsCompatiblePropertyDescriptor (Extensible, Desc, Current)
IsCompatiblePropertyDescriptor(bool extensible,const PropertyDescriptor & desc,const PropertyDescriptor & current)1024 bool JSObject::IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc,
1025 const PropertyDescriptor ¤t)
1026 {
1027 // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current).
1028 ObjectOperator op;
1029 return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current);
1030 }
1031
GetPrototype(const JSHandle<JSObject> & obj)1032 JSTaggedValue JSObject::GetPrototype(const JSHandle<JSObject> &obj)
1033 {
1034 JSHClass *hclass = obj->GetJSHClass();
1035 return hclass->GetPrototype();
1036 }
1037
SetPrototype(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & proto)1038 bool JSObject::SetPrototype(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &proto)
1039 {
1040 ASSERT_PRINT(proto->IsECMAObject() || proto->IsNull(), "proto must be object or null");
1041 JSTaggedValue current = JSObject::GetPrototype(obj);
1042 if (current == proto.GetTaggedValue()) {
1043 return true;
1044 }
1045 if (!obj->IsExtensible()) {
1046 return false;
1047 }
1048 bool done = false;
1049 JSMutableHandle<JSTaggedValue> tempProtoHandle(thread, proto.GetTaggedValue());
1050 while (!done) {
1051 if (tempProtoHandle->IsNull() || !tempProtoHandle->IsECMAObject()) {
1052 done = true;
1053 } else if (JSTaggedValue::SameValue(tempProtoHandle.GetTaggedValue(), obj.GetTaggedValue())) {
1054 return false;
1055 } else {
1056 if (tempProtoHandle->IsJSProxy()) {
1057 break;
1058 }
1059 tempProtoHandle.Update(JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>(tempProtoHandle)));
1060 }
1061 }
1062 // map transition
1063 JSHandle<JSHClass> hclass(thread, obj->GetJSHClass());
1064 JSHandle<JSHClass> newClass = JSHClass::TransitionProto(thread, hclass, proto);
1065 JSHClass::NotifyHclassChanged(thread, hclass, newClass);
1066 obj->SetClass(newClass);
1067 thread->NotifyStableArrayElementsGuardians(obj);
1068 return true;
1069 }
1070
HasProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key)1071 bool JSObject::HasProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key)
1072 {
1073 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1074 JSHandle<JSTaggedValue> objValue(obj);
1075 ObjectOperator op(thread, objValue, key);
1076
1077 JSHandle<JSTaggedValue> holder = op.GetHolder();
1078 if (holder->IsJSProxy()) {
1079 return JSProxy::HasProperty(thread, JSHandle<JSProxy>::Cast(holder), key);
1080 }
1081
1082 return op.IsFound();
1083 }
1084
HasProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index)1085 bool JSObject::HasProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index)
1086 {
1087 JSHandle<JSTaggedValue> objValue(obj);
1088 ObjectOperator op(thread, objValue, index);
1089
1090 JSHandle<JSTaggedValue> holder = op.GetHolder();
1091 if (holder->IsJSProxy()) {
1092 JSHandle<JSTaggedValue> key(thread, JSTaggedValue(index));
1093 return JSProxy::HasProperty(thread, JSHandle<JSProxy>::Cast(holder), key);
1094 }
1095
1096 return op.IsFound();
1097 }
1098
PreventExtensions(JSThread * thread,const JSHandle<JSObject> & obj)1099 bool JSObject::PreventExtensions(JSThread *thread, const JSHandle<JSObject> &obj)
1100 {
1101 if (obj->IsExtensible()) {
1102 JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
1103 JSHandle<JSHClass> newHclass = JSHClass::TransitionExtension(thread, jshclass);
1104 obj->SetClass(newHclass);
1105 }
1106
1107 return true;
1108 }
1109
1110 // 9.1.12 [[OwnPropertyKeys]] ( )
GetOwnPropertyKeys(JSThread * thread,const JSHandle<JSObject> & obj)1111 JSHandle<TaggedArray> JSObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj)
1112 {
1113 [[maybe_unused]] uint32_t elementIndex = 0;
1114 uint32_t numOfElements = obj->GetNumberOfElements();
1115 uint32_t keyLen = numOfElements + obj->GetNumberOfKeys();
1116
1117 JSHandle<TaggedArray> keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen);
1118
1119 if (numOfElements > 0) {
1120 GetAllElementKeys(thread, obj, 0, keyArray);
1121 }
1122 GetAllKeys(thread, obj, static_cast<int32_t>(numOfElements), keyArray);
1123 return keyArray;
1124 }
1125
ObjectCreate(JSThread * thread,const JSHandle<JSObject> & proto)1126 JSHandle<JSObject> JSObject::ObjectCreate(JSThread *thread, const JSHandle<JSObject> &proto)
1127 {
1128 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1129 JSHandle<JSFunction> constructor(env->GetObjectFunction());
1130 JSHandle<JSObject> objHandle = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(constructor);
1131 SetPrototype(thread, objHandle, JSHandle<JSTaggedValue>(proto));
1132 return objHandle;
1133 }
1134
1135 // 7.3.4 CreateDataProperty (O, P, V)
CreateDataProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)1136 bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
1137 const JSHandle<JSTaggedValue> &value)
1138 {
1139 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1140 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1141 auto result = ObjectFastOperator::SetPropertyByValue<true>(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
1142 value.GetTaggedValue());
1143 if (!result.IsHole()) {
1144 return result != JSTaggedValue::Exception();
1145 }
1146 PropertyDescriptor desc(thread, value, true, true, true);
1147 return JSTaggedValue::DefineOwnProperty(thread, JSHandle<JSTaggedValue>::Cast(obj), key, desc);
1148 }
1149
CreateDataProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value)1150 bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
1151 const JSHandle<JSTaggedValue> &value)
1152 {
1153 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1154 auto result =
1155 ObjectFastOperator::SetPropertyByIndex<true>(thread, obj.GetTaggedValue(), index, value.GetTaggedValue());
1156 if (!result.IsHole()) {
1157 return result != JSTaggedValue::Exception();
1158 }
1159 PropertyDescriptor desc(thread, value, true, true, true);
1160 return DefineOwnProperty(thread, obj, index, desc);
1161 }
1162
1163 // 7.3.5 CreateMethodProperty (O, P, V)
CreateDataPropertyOrThrow(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)1164 bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle<JSObject> &obj,
1165 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
1166 {
1167 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1168 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1169
1170 bool success = CreateDataProperty(thread, obj, key, value);
1171 if (!success) {
1172 THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success);
1173 }
1174 return success;
1175 }
1176
CreateDataPropertyOrThrow(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value)1177 bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
1178 const JSHandle<JSTaggedValue> &value)
1179 {
1180 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1181
1182 bool success = CreateDataProperty(thread, obj, index, value);
1183 if (!success) {
1184 THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success);
1185 }
1186 return success;
1187 }
1188 // 7.3.6 CreateDataPropertyOrThrow (O, P, V)
CreateMethodProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)1189 bool JSObject::CreateMethodProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
1190 const JSHandle<JSTaggedValue> &value)
1191 {
1192 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1193 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1194
1195 PropertyDescriptor desc(thread, value, true, false, true);
1196 return DefineOwnProperty(thread, obj, key, desc);
1197 }
1198
1199 // 7.3.9 GetMethod (O, P)
GetMethod(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)1200 JSHandle<JSTaggedValue> JSObject::GetMethod(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
1201 const JSHandle<JSTaggedValue> &key)
1202 {
1203 JSHandle<JSTaggedValue> func = JSTaggedValue::GetProperty(thread, obj, key).GetValue();
1204 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
1205 if (func->IsUndefined() || func->IsNull()) {
1206 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined());
1207 }
1208
1209 if (!func->IsCallable()) {
1210 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", func);
1211 }
1212 return func;
1213 }
1214
1215 // 7.3.14 SetIntegrityLevel (O, level)
SetIntegrityLevel(JSThread * thread,const JSHandle<JSObject> & obj,IntegrityLevel level)1216 bool JSObject::SetIntegrityLevel(JSThread *thread, const JSHandle<JSObject> &obj, IntegrityLevel level)
1217 {
1218 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1219 ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN),
1220 "level is not a valid IntegrityLevel");
1221
1222 bool status = PreventExtensions(thread, obj);
1223 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1224 if (!status) {
1225 return false;
1226 }
1227
1228 JSHandle<TaggedArray> jshandleKeys = GetOwnPropertyKeys(thread, obj);
1229 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1230 PropertyDescriptor descNoConf(thread);
1231 descNoConf.SetConfigurable(false);
1232 PropertyDescriptor descNoConfWrite(thread);
1233 descNoConfWrite.SetWritable(false);
1234 descNoConfWrite.SetConfigurable(false);
1235
1236 if (level == IntegrityLevel::SEALED) {
1237 uint32_t length = jshandleKeys->GetLength();
1238 if (length == 0) {
1239 return true;
1240 }
1241 auto key = jshandleKeys->Get(0);
1242 JSMutableHandle<JSTaggedValue> handleKey(thread, key);
1243 for (uint32_t i = 0; i < length; i++) {
1244 auto taggedKey = JSTaggedValue(jshandleKeys->Get(i));
1245 handleKey.Update(taggedKey);
1246 [[maybe_unused]] bool success =
1247 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(obj), handleKey, descNoConf);
1248 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1249 }
1250 } else {
1251 uint32_t length = jshandleKeys->GetLength();
1252 if (length == 0) {
1253 return true;
1254 }
1255 auto key = jshandleKeys->Get(0);
1256 JSMutableHandle<JSTaggedValue> handleKey(thread, key);
1257 for (uint32_t i = 0; i < length; i++) {
1258 auto taggedKey = JSTaggedValue(jshandleKeys->Get(i));
1259 handleKey.Update(taggedKey);
1260 PropertyDescriptor currentDesc(thread);
1261 bool curDescStatus = GetOwnProperty(thread, obj, handleKey, currentDesc);
1262 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1263 if (curDescStatus) {
1264 PropertyDescriptor desc = currentDesc.IsAccessorDescriptor() ? descNoConf : descNoConfWrite;
1265 [[maybe_unused]] bool success =
1266 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(obj), handleKey, desc);
1267 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1268 }
1269 }
1270 }
1271 return true;
1272 }
1273
1274 // 7.3.15 TestIntegrityLevel (O, level)
TestIntegrityLevel(JSThread * thread,const JSHandle<JSObject> & obj,IntegrityLevel level)1275 bool JSObject::TestIntegrityLevel(JSThread *thread, const JSHandle<JSObject> &obj, IntegrityLevel level)
1276 {
1277 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1278 ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN),
1279 "level is not a valid IntegrityLevel");
1280
1281 bool status = obj->IsExtensible();
1282 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1283 if (status) {
1284 return false;
1285 }
1286
1287 JSHandle<TaggedArray> jshandleKeys = GetOwnPropertyKeys(thread, obj);
1288 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1289 uint32_t length = jshandleKeys->GetLength();
1290 if (length == 0) {
1291 return true;
1292 }
1293 auto key = jshandleKeys->Get(0);
1294 JSMutableHandle<JSTaggedValue> handleKey(thread, key);
1295 for (uint32_t i = 0; i < length; i++) {
1296 auto taggedKey = JSTaggedValue(jshandleKeys->Get(i));
1297 handleKey.Update(taggedKey);
1298 PropertyDescriptor currentDesc(thread);
1299 bool curDescStatus = GetOwnProperty(thread, obj, handleKey, currentDesc);
1300 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1301 if (curDescStatus) {
1302 if (currentDesc.IsConfigurable()) {
1303 return false;
1304 }
1305 if (level == IntegrityLevel::FROZEN && currentDesc.IsDataDescriptor() && currentDesc.IsWritable()) {
1306 return false;
1307 }
1308 }
1309 }
1310 return true;
1311 }
1312
1313 // 7.3.21 EnumerableOwnNames (O)
EnumerableOwnNames(JSThread * thread,const JSHandle<JSObject> & obj)1314 JSHandle<TaggedArray> JSObject::EnumerableOwnNames(JSThread *thread, const JSHandle<JSObject> &obj)
1315 {
1316 ASSERT_PRINT(obj->IsECMAObject(), "obj is not object");
1317 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1318 JSHandle<JSTaggedValue> tagObj(obj);
1319 // fast mode
1320 if (tagObj->IsJSObject() && !tagObj->IsTypedArray() && !tagObj->IsModuleNamespace()) {
1321 uint32_t copyLengthOfKeys = 0;
1322 uint32_t copyLengthOfElements = 0;
1323 auto keyElementPair = GetOwnEnumerableNamesInFastMode(thread, obj, ©LengthOfKeys, ©LengthOfElements);
1324 JSHandle<TaggedArray> keyArray = keyElementPair.first;
1325 JSHandle<TaggedArray> elementArray = keyElementPair.second;
1326 JSHandle<TaggedArray> keys;
1327 if (copyLengthOfKeys != 0 && copyLengthOfElements != 0) {
1328 keys = TaggedArray::AppendSkipHole(thread, elementArray, keyArray, copyLengthOfKeys + copyLengthOfElements);
1329 } else if (copyLengthOfKeys != 0) {
1330 keys = factory->CopyArray(keyArray, copyLengthOfKeys, copyLengthOfKeys);
1331 } else if (copyLengthOfElements != 0) {
1332 keys = factory->CopyArray(elementArray, copyLengthOfElements, copyLengthOfElements);
1333 } else {
1334 keys = factory->EmptyArray();
1335 }
1336 return keys;
1337 }
1338
1339 uint32_t copyLength = 0;
1340 JSHandle<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj);
1341 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1342 uint32_t length = keys->GetLength();
1343
1344 JSHandle<TaggedArray> names = factory->NewTaggedArray(length);
1345 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
1346 for (uint32_t i = 0; i < length; i++) {
1347 keyHandle.Update(keys->Get(i));
1348 if (keyHandle->IsString()) {
1349 PropertyDescriptor desc(thread);
1350 bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj),
1351 keyHandle, desc);
1352 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1353
1354 if (status && desc.IsEnumerable()) {
1355 names->Set(thread, copyLength, keyHandle);
1356 copyLength++;
1357 }
1358 }
1359 }
1360
1361 return factory->CopyArray(names, length, copyLength);
1362 }
1363
EnumerableOwnPropertyNamesHelper(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<TaggedArray> & arr,JSHandle<TaggedArray> & prop,uint32_t & index,bool & fastMode,PropertyKind kind)1364 void JSObject::EnumerableOwnPropertyNamesHelper(JSThread *thread, const JSHandle<JSObject> &obj,
1365 const JSHandle<TaggedArray> &arr, JSHandle<TaggedArray> &prop, uint32_t &index, bool &fastMode, PropertyKind kind)
1366 {
1367 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1368 JSHandle<JSHClass> objClass(thread, obj->GetJSHClass());
1369 uint32_t length = arr->GetLength();
1370 for (uint32_t i = 0; i < length; i++) {
1371 key.Update(arr->Get(thread, i));
1372 if (!JSTaggedValue::IsPropertyKey(key)) {
1373 break;
1374 }
1375 JSTaggedValue value = JSTaggedValue::Hole();
1376 if (fastMode) {
1377 value = ObjectFastOperator::GetPropertyByValue<true>(thread, obj.GetTaggedValue(), key.GetTaggedValue());
1378 RETURN_IF_ABRUPT_COMPLETION(thread);
1379 }
1380 if (value.IsHole()) {
1381 PropertyDescriptor desc(thread);
1382 bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj), key, desc);
1383 RETURN_IF_ABRUPT_COMPLETION(thread);
1384 if (!status || !desc.IsEnumerable()) {
1385 continue;
1386 }
1387 if (desc.HasValue()) {
1388 value = desc.GetValue().GetTaggedValue();
1389 } else {
1390 OperationResult opResult = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(obj), key);
1391 RETURN_IF_ABRUPT_COMPLETION(thread);
1392 value = opResult.GetValue().GetTaggedValue();
1393 }
1394 }
1395 index = SetValuesOrEntries(thread, prop, index, key, JSHandle<JSTaggedValue>(thread, value), kind);
1396 fastMode = fastMode ? CheckHClassHit(obj, objClass) : fastMode;
1397 }
1398 }
1399
EnumerableOwnPropertyNames(JSThread * thread,const JSHandle<JSObject> & obj,PropertyKind kind)1400 JSHandle<TaggedArray> JSObject::EnumerableOwnPropertyNames(JSThread *thread, const JSHandle<JSObject> &obj,
1401 PropertyKind kind)
1402 {
1403 // 1. Assert: Type(O) is Object.
1404 ASSERT_PRINT(obj->IsECMAObject(), "obj is not object");
1405
1406 // 2. Let ownKeys be ? O.[[OwnPropertyKeys]]().
1407 JSHandle<JSTaggedValue> tagObj(obj);
1408 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1409 if (tagObj->IsJSObject() && !tagObj->IsJSProxy() && !tagObj->IsTypedArray() && !tagObj->IsModuleNamespace()) {
1410 uint32_t copyLengthOfKeys = 0;
1411 uint32_t copyLengthOfElements = 0;
1412 uint32_t index = 0;
1413 bool fastMode = true;
1414 auto keyElementPair = GetOwnEnumerableNamesInFastMode(thread, obj, ©LengthOfKeys, ©LengthOfElements);
1415 JSHandle<TaggedArray> keyArray = keyElementPair.first;
1416 JSHandle<TaggedArray> elementArray = keyElementPair.second;
1417 JSHandle<TaggedArray> properties = factory->NewTaggedArray(copyLengthOfKeys + copyLengthOfElements);
1418 if (copyLengthOfElements != 0) {
1419 EnumerableOwnPropertyNamesHelper(thread, obj, elementArray, properties, index, fastMode, kind);
1420 }
1421 if (copyLengthOfKeys != 0) {
1422 EnumerableOwnPropertyNamesHelper(thread, obj, keyArray, properties, index, fastMode, kind);
1423 }
1424 if (UNLIKELY(!fastMode && index < copyLengthOfKeys + copyLengthOfElements)) {
1425 properties->Trim(thread, index);
1426 }
1427 return properties;
1428 }
1429
1430 JSHandle<TaggedArray> ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj);
1431 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1432
1433 // 3. Let properties be a new empty List.
1434 uint32_t length = ownKeys->GetLength();
1435 JSHandle<TaggedArray> properties = factory->NewTaggedArray(length);
1436
1437 // 4. For each element key of ownKeys, do
1438 // a. If Type(key) is String, then
1439 // i. Let desc be ? O.[[GetOwnProperty]](key).
1440 // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
1441 // 1. If kind is key, append key to properties.
1442 // 2. Else,
1443 // a. Let value be ? Get(O, key).
1444 // b. If kind is value, append value to properties.
1445 // c. Else,
1446 // i. Assert: kind is key+value.
1447 // ii. Let entry be ! CreateArrayFromList(« key, value »).
1448 // iii. Append entry to properties.
1449 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1450 uint32_t index = 0;
1451 for (uint32_t i = 0; i < length; i++) {
1452 key.Update(ownKeys->Get(thread, i));
1453 if (key->IsString()) {
1454 PropertyDescriptor desc(thread);
1455 bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj),
1456 key, desc);
1457 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1458 if (status && desc.IsEnumerable()) {
1459 if (kind == PropertyKind::KEY) {
1460 properties->Set(thread, index++, key);
1461 } else {
1462 OperationResult result =
1463 JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(obj), key);
1464 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1465 JSHandle<JSTaggedValue> value = result.GetValue();
1466 index = SetValuesOrEntries(thread, properties, index, key, value, kind);
1467 }
1468 }
1469 }
1470 }
1471
1472 if (UNLIKELY(index < length)) {
1473 properties->Trim(thread, index);
1474 }
1475 // 5. Return properties.
1476 return properties;
1477 }
1478
GetFunctionRealm(JSThread * thread,const JSHandle<JSTaggedValue> & object)1479 JSHandle<GlobalEnv> JSObject::GetFunctionRealm(JSThread *thread, const JSHandle<JSTaggedValue> &object)
1480 {
1481 // 1. Assert: obj is a callable object.
1482 ASSERT(object->IsCallable());
1483 // 2. If obj has a [[Realm]] internal slot, then return obj’s [[Realm]] internal slot.
1484 // 3. If obj is a Bound Function exotic object, then
1485 if (object->IsBoundFunction()) {
1486 // a. Let target be obj’s [[BoundTargetFunction]] internal slot.
1487 JSHandle<JSTaggedValue> target(thread, JSHandle<JSBoundFunction>(object)->GetBoundTarget());
1488 // b. Return GetFunctionRealm(target).
1489 return GetFunctionRealm(thread, target);
1490 }
1491 // 4. If obj is a Proxy exotic object, then
1492 if (object->IsJSProxy()) {
1493 // a. If the value of the [[ProxyHandler]] internal slot of obj is null, throw a TypeError exception.
1494 if (JSHandle<JSProxy>(object)->GetHandler().IsNull()) {
1495 THROW_TYPE_ERROR_AND_RETURN(thread, "JSObject::GetFunctionRealm: handler is null",
1496 JSHandle<GlobalEnv>(thread, JSTaggedValue::Exception()));
1497 }
1498 // b. Let proxyTarget be the value of obj’s [[ProxyTarget]] internal slot.
1499 JSHandle<JSTaggedValue> proxyTarget(thread, JSHandle<JSProxy>(object)->GetTarget());
1500 return GetFunctionRealm(thread, proxyTarget);
1501 }
1502 JSTaggedValue maybeGlobalEnv = JSHandle<JSFunction>(object)->GetLexicalEnv();
1503 while (!maybeGlobalEnv.IsJSGlobalEnv()) {
1504 if (maybeGlobalEnv.IsUndefined()) {
1505 return thread->GetEcmaVM()->GetGlobalEnv();
1506 }
1507 maybeGlobalEnv = LexicalEnv::Cast(maybeGlobalEnv.GetTaggedObject())->GetParentEnv();
1508 }
1509 return JSHandle<GlobalEnv>(thread, maybeGlobalEnv);
1510 }
1511
InstanceOf(JSThread * thread,const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & target)1512 bool JSObject::InstanceOf(JSThread *thread, const JSHandle<JSTaggedValue> &object,
1513 const JSHandle<JSTaggedValue> &target)
1514 {
1515 // 1. If Type(target) is not Object, throw a TypeError exception.
1516 if (!target->IsECMAObject()) {
1517 THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when type of target is not Object", false);
1518 }
1519
1520 EcmaVM *vm = thread->GetEcmaVM();
1521 // 2. Let instOfHandler be GetMethod(target, @@hasInstance).
1522 JSHandle<JSTaggedValue> instOfHandler = GetMethod(thread, target, vm->GetGlobalEnv()->GetHasInstanceSymbol());
1523
1524 // 3. ReturnIfAbrupt(instOfHandler).
1525 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1526
1527 // 4. If instOfHandler is not undefined, then
1528 if (!instOfHandler->IsUndefined()) {
1529 // a. Return ! ToBoolean(? Call(instOfHandler, target, «object»)).
1530 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1531 EcmaRuntimeCallInfo *info =
1532 EcmaInterpreter::NewRuntimeCallInfo(thread, instOfHandler, target, undefined, 1);
1533 info->SetCallArg(object.GetTaggedValue());
1534 JSTaggedValue tagged = JSFunction::Call(info);
1535 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1536 return tagged.ToBoolean();
1537 }
1538
1539 // 5. If IsCallable(target) is false, throw a TypeError exception.
1540 if (!target->IsCallable()) {
1541 THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", false);
1542 }
1543
1544 // 6. Return ? OrdinaryHasInstance(target, object).
1545 return JSFunction::OrdinaryHasInstance(thread, target, object);
1546 }
1547
1548 // ecma6.0 6.2.4.4
FromPropertyDescriptor(JSThread * thread,const PropertyDescriptor & desc)1549 JSHandle<JSTaggedValue> JSObject::FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc)
1550 {
1551 // 1. If Desc is undefined, return undefined
1552 if (desc.IsEmpty()) {
1553 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined());
1554 }
1555
1556 // 2. Let obj be ObjectCreate(%ObjectPrototype%).
1557 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1558 JSHandle<JSFunction> objFunc(env->GetObjectFunction());
1559 JSHandle<JSObject> objHandle = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(objFunc);
1560
1561 auto globalConst = thread->GlobalConstants();
1562 // 4. If Desc has a [[Value]] field, then Perform CreateDataProperty(obj, "value", Desc.[[Value]]).
1563 if (desc.HasValue()) {
1564 JSHandle<JSTaggedValue> valueStr = globalConst->GetHandledValueString();
1565 bool success = CreateDataProperty(thread, objHandle, valueStr, desc.GetValue());
1566 RASSERT_PRINT(success, "CreateDataProperty must be success");
1567 }
1568 // 5. If Desc has a [[Writable]] field, then Perform CreateDataProperty(obj, "writable", Desc.[[Writable]]).
1569 if (desc.HasWritable()) {
1570 JSHandle<JSTaggedValue> writableStr = globalConst->GetHandledWritableString();
1571 JSHandle<JSTaggedValue> writable(thread, JSTaggedValue(desc.IsWritable()));
1572 [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, writableStr, writable);
1573 ASSERT_PRINT(success, "CreateDataProperty must be success");
1574 }
1575 // 6. If Desc has a [[Get]] field, then Perform CreateDataProperty(obj, "get", Desc.[[Get]]).
1576 if (desc.HasGetter()) {
1577 JSHandle<JSTaggedValue> getStr = globalConst->GetHandledGetString();
1578 bool success = CreateDataProperty(thread, objHandle, getStr, desc.GetGetter());
1579 RASSERT_PRINT(success, "CreateDataProperty must be success");
1580 }
1581 // 7. If Desc has a [[Set]] field, then Perform CreateDataProperty(obj, "set", Desc.[[Set]])
1582 if (desc.HasSetter()) {
1583 JSHandle<JSTaggedValue> setStr = globalConst->GetHandledSetString();
1584 bool success = CreateDataProperty(thread, objHandle, setStr, desc.GetSetter());
1585 RASSERT_PRINT(success, "CreateDataProperty must be success");
1586 }
1587 // 8. If Desc has an [[Enumerable]] field, then Perform CreateDataProperty(obj, "enumerable",
1588 // Desc.[[Enumerable]]).
1589 if (desc.HasEnumerable()) {
1590 JSHandle<JSTaggedValue> enumerableStr = globalConst->GetHandledEnumerableString();
1591 JSHandle<JSTaggedValue> enumerable(thread, JSTaggedValue(desc.IsEnumerable()));
1592 [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, enumerableStr, enumerable);
1593 ASSERT_PRINT(success, "CreateDataProperty must be success");
1594 }
1595 // 9. If Desc has a [[Configurable]] field, then Perform CreateDataProperty(obj , "configurable",
1596 // Desc.[[Configurable]]).
1597 if (desc.HasConfigurable()) {
1598 JSHandle<JSTaggedValue> configurableStr = globalConst->GetHandledConfigurableString();
1599 JSHandle<JSTaggedValue> configurable(thread, JSTaggedValue(desc.IsConfigurable()));
1600 [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, configurableStr, configurable);
1601 ASSERT_PRINT(success, "CreateDataProperty must be success");
1602 }
1603 return JSHandle<JSTaggedValue>(objHandle);
1604 }
1605
ToPropertyDescriptorFast(JSThread * thread,const JSHandle<JSTaggedValue> & obj,PropertyDescriptor & desc)1606 bool JSObject::ToPropertyDescriptorFast(JSThread *thread, const JSHandle<JSTaggedValue> &obj, PropertyDescriptor &desc)
1607 {
1608 auto *hclass = obj->GetTaggedObject()->GetClass();
1609 JSType jsType = hclass->GetObjectType();
1610 if (jsType != JSType::JS_OBJECT) {
1611 return false;
1612 }
1613 if (hclass->IsDictionaryMode()) {
1614 return false;
1615 }
1616 auto env = thread->GetEcmaVM()->GetGlobalEnv();
1617 auto globalConst = thread->GlobalConstants();
1618 if (hclass->GetPrototype() != env->GetObjectFunctionPrototype().GetTaggedValue()) {
1619 return false;
1620 }
1621 if (JSObject::Cast(hclass->GetPrototype().GetTaggedObject())->GetClass() !=
1622 env->GetObjectFunctionPrototypeClass().GetObject<JSHClass>()) {
1623 return false;
1624 }
1625 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1626 uint32_t propsNumber = hclass->NumberOfProps();
1627 for (uint32_t i = 0; i < propsNumber; i++) {
1628 auto attr = layoutInfo->GetAttr(i);
1629 if (attr.IsAccessor()) {
1630 return false;
1631 }
1632 auto key = layoutInfo->GetKey(i);
1633 auto value = JSObject::Cast(obj->GetTaggedObject())->GetProperty(hclass, attr);
1634 if (key == globalConst->GetEnumerableString()) {
1635 bool enumerable = value.ToBoolean();
1636 desc.SetEnumerable(enumerable);
1637 } else if (key == globalConst->GetConfigurableString()) {
1638 bool configurable = value.ToBoolean();
1639 desc.SetConfigurable(configurable);
1640 } else if (key == globalConst->GetValueString()) {
1641 auto handleValue = JSHandle<JSTaggedValue>(thread, value);
1642 desc.SetValue(handleValue);
1643 } else if (key == globalConst->GetWritableString()) {
1644 bool writable = value.ToBoolean();
1645 desc.SetWritable(writable);
1646 } else if (key == globalConst->GetGetString()) {
1647 if (!value.IsCallable()) {
1648 return false;
1649 }
1650 auto getter = JSHandle<JSTaggedValue>(thread, value);
1651 desc.SetGetter(getter);
1652 } else if (key == globalConst->GetSetString()) {
1653 if (!value.IsCallable()) {
1654 return false;
1655 }
1656 auto setter = JSHandle<JSTaggedValue>(thread, value);
1657 desc.SetSetter(setter);
1658 }
1659 }
1660
1661 if (desc.IsAccessorDescriptor()) {
1662 // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception.
1663 if (desc.HasValue() || desc.HasWritable()) {
1664 THROW_TYPE_ERROR_AND_RETURN(thread, "either Value or Writable is present", true);
1665 }
1666 }
1667 return true;
1668 }
1669
1670 // ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj )
ToPropertyDescriptor(JSThread * thread,const JSHandle<JSTaggedValue> & obj,PropertyDescriptor & desc)1671 void JSObject::ToPropertyDescriptor(JSThread *thread, const JSHandle<JSTaggedValue> &obj, PropertyDescriptor &desc)
1672 {
1673 if (!obj->IsECMAObject()) {
1674 // 2. If Type(Obj) is not Object, throw a TypeError exception.
1675 THROW_TYPE_ERROR(thread, "ToPropertyDescriptor error obj is not Object");
1676 }
1677
1678 if (ToPropertyDescriptorFast(thread, obj, desc)) {
1679 return;
1680 }
1681 auto globalConst = thread->GlobalConstants();
1682 // 3. Let desc be a new Property Descriptor that initially has no fields.
1683 // 4. Let hasEnumerable be HasProperty(Obj, "enumerable")
1684 JSHandle<JSTaggedValue> enumerableStr = globalConst->GetHandledEnumerableString();
1685 {
1686 ObjectOperator op(thread, obj.GetTaggedValue(), enumerableStr.GetTaggedValue());
1687 if (op.IsFound()) {
1688 auto value = op.FastGetValue();
1689 bool enumerable = value->IsException() ? false : value->ToBoolean();
1690 desc.SetEnumerable(enumerable);
1691 }
1692 }
1693 // 7. Let hasConfigurable be HasProperty(Obj, "configurable").
1694 JSHandle<JSTaggedValue> configurableStr = globalConst->GetHandledConfigurableString();
1695 {
1696 ObjectOperator op(thread, obj.GetTaggedValue(), configurableStr.GetTaggedValue());
1697 if (op.IsFound()) {
1698 auto value = op.FastGetValue();
1699 bool conf = value->IsException() ? false : value->ToBoolean();
1700 desc.SetConfigurable(conf);
1701 }
1702 }
1703 // 10. Let hasValue be HasProperty(Obj, "value").
1704 JSHandle<JSTaggedValue> valueStr = globalConst->GetHandledValueString();
1705 {
1706 ObjectOperator op(thread, obj.GetTaggedValue(), valueStr.GetTaggedValue());
1707 if (op.IsFound()) {
1708 JSHandle<JSTaggedValue> prop = op.FastGetValue();
1709 desc.SetValue(prop);
1710 }
1711 }
1712 // 13. Let hasWritable be HasProperty(Obj, "writable").
1713 JSHandle<JSTaggedValue> writableStr = globalConst->GetHandledWritableString();
1714 {
1715 ObjectOperator op(thread, obj.GetTaggedValue(), writableStr.GetTaggedValue());
1716 if (op.IsFound()) {
1717 auto value = op.FastGetValue();
1718 bool writable = value->IsException() ? false : value->ToBoolean();
1719 desc.SetWritable(writable);
1720 }
1721 }
1722 // 16. Let hasGet be HasProperty(Obj, "get").
1723 JSHandle<JSTaggedValue> getStr = globalConst->GetHandledGetString();
1724 {
1725 ObjectOperator op(thread, obj.GetTaggedValue(), getStr.GetTaggedValue());
1726 if (op.IsFound()) {
1727 JSHandle<JSTaggedValue> getter = op.FastGetValue();
1728 if (!getter->IsCallable() && !getter->IsUndefined()) {
1729 THROW_TYPE_ERROR(thread, "getter not callable or undefined");
1730 }
1731 desc.SetGetter(getter);
1732 }
1733 }
1734
1735 // 19. Let hasSet be HasProperty(Obj, "set").
1736 JSHandle<JSTaggedValue> setStr = globalConst->GetHandledSetString();
1737 {
1738 ObjectOperator op(thread, obj.GetTaggedValue(), setStr.GetTaggedValue());
1739 if (op.IsFound()) {
1740 JSHandle<JSTaggedValue> setter = op.FastGetValue();
1741 if (!setter->IsCallable() && !setter->IsUndefined()) {
1742 THROW_TYPE_ERROR(thread, "setter not callable or undefined");
1743 }
1744 desc.SetSetter(setter);
1745 }
1746 }
1747
1748 // 22. If either desc.[[Get]] or desc.[[Set]] is present, then
1749 if (desc.IsAccessorDescriptor()) {
1750 // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception.
1751 if (desc.HasValue() || desc.HasWritable()) {
1752 THROW_TYPE_ERROR(thread, "either desc.[[Value]] or desc.[[Writable]] is present");
1753 }
1754 }
1755 // 23. Return desc.
1756 }
1757
SpeciesConstructor(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & defaultConstructort)1758 JSHandle<JSTaggedValue> JSObject::SpeciesConstructor(JSThread *thread, const JSHandle<JSObject> &obj,
1759 const JSHandle<JSTaggedValue> &defaultConstructort)
1760 {
1761 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1762 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1763 // Assert: Type(O) is Object.
1764 ASSERT_PRINT(obj->IsECMAObject(), "obj must be js object");
1765
1766 // Let C be Get(O, "constructor").
1767 JSHandle<JSTaggedValue> contructorKey = globalConst->GetHandledConstructorString();
1768 JSHandle<JSTaggedValue> objConstructor(JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(obj),
1769 contructorKey).GetValue());
1770 // ReturnIfAbrupt(C).
1771 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
1772 // If C is undefined, return defaultConstructor.
1773 if (objConstructor->IsUndefined()) {
1774 return defaultConstructort;
1775 }
1776 // If Type(C) is not Object, throw a TypeError exception.
1777 if (!objConstructor->IsECMAObject()) {
1778 THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is not Object",
1779 JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
1780 }
1781 // Let S be Get(C, @@species).
1782 JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
1783 JSHandle<JSTaggedValue> speciesConstructor(GetProperty(thread, objConstructor, speciesSymbol).GetValue());
1784 // ReturnIfAbrupt(S).
1785 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
1786 // If S is either undefined or null, return defaultConstructor.
1787 if (speciesConstructor->IsUndefined() || speciesConstructor->IsNull()) {
1788 return defaultConstructort;
1789 }
1790 // If IsConstructor(S) is true, return S.
1791 if (speciesConstructor->IsConstructor()) {
1792 return speciesConstructor;
1793 }
1794 // Throw a TypeError exception.
1795 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Constructor",
1796 JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
1797 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception());
1798 }
1799
1800 // 6.2.4.6 CompletePropertyDescriptor ( Desc )
CompletePropertyDescriptor(const JSThread * thread,PropertyDescriptor & desc)1801 void PropertyDescriptor::CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc)
1802 {
1803 // 1. ReturnIfAbrupt(Desc).
1804 // 2. Assert: Desc is a Property Descriptor
1805 // 3. Let like be Record{[[Value]]: undefined, [[Writable]]: false, [[Get]]: undefined, [[Set]]: undefined,
1806 // [[Enumerable]]: false, [[Configurable]]: false}.
1807 // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then
1808 if (!desc.IsAccessorDescriptor()) {
1809 // a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to like.[[Value]].
1810 // b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] to like.[[Writable]].
1811 if (!desc.HasValue()) {
1812 desc.SetValue(JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
1813 }
1814 if (!desc.HasWritable()) {
1815 desc.SetWritable(false);
1816 }
1817 } else {
1818 // a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to like.[[Get]].
1819 // b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to like.[[Set]].
1820 // Default value of Get and Set is undefined.
1821 }
1822 // 6. If Desc does not have an [[Enumerable]] field, set Desc.[[Enumerable]] to like.[[Enumerable]].
1823 // 7. If Desc does not have a [[Configurable]] field, set Desc.[[Configurable]] to like.[[Configurable]].
1824 if (!desc.HasEnumerable()) {
1825 desc.SetEnumerable(false);
1826 }
1827 if (!desc.HasConfigurable()) {
1828 desc.SetConfigurable(false);
1829 }
1830 }
1831
1832 // 13.7.5.15 EnumerateObjectProperties ( O )
EnumerateObjectProperties(JSThread * thread,const JSHandle<JSTaggedValue> & obj)1833 JSHandle<JSForInIterator> JSObject::EnumerateObjectProperties(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
1834 {
1835 // 1. Return an Iterator object (25.1.1.2) whose next method iterates over all the String-valued keys of
1836 // enumerable properties of O. The Iterator object must inherit from %IteratorPrototype% (25.1.2). The
1837 // mechanics and order of enumerating the properties is not specified but must conform to the rules specified
1838 // below.
1839 JSHandle<JSTaggedValue> object;
1840 if (obj->IsString()) {
1841 object = JSHandle<JSTaggedValue>::Cast(JSPrimitiveRef::StringCreate(thread, obj));
1842 } else {
1843 object = JSTaggedValue::ToPrototypeOrObj(thread, obj);
1844 }
1845
1846 return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object);
1847 }
1848
DefinePropertyByLiteral(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,bool useForClass)1849 void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle<JSObject> &obj,
1850 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
1851 bool useForClass)
1852 {
1853 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1854 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1855 PropertyAttributes attr = useForClass ? PropertyAttributes::Default(true, false, true)
1856 : PropertyAttributes::Default();
1857
1858 if (value->IsAccessorData()) {
1859 attr.SetIsAccessor(true);
1860 }
1861
1862 uint32_t index = 0;
1863 if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) {
1864 AddElementInternal(thread, obj, index, value, attr);
1865 return;
1866 }
1867 UNREACHABLE();
1868 }
1869
DefineSetter(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)1870 void JSObject::DefineSetter(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
1871 const JSHandle<JSTaggedValue> &value)
1872 {
1873 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
1874 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1875 ObjectOperator op(thread, obj, key, OperatorType::OWN);
1876 ASSERT(op.IsFound());
1877 op.DefineSetter(value);
1878 }
1879
DefineGetter(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)1880 void JSObject::DefineGetter(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
1881 const JSHandle<JSTaggedValue> &value)
1882 {
1883 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
1884 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1885 ObjectOperator op(thread, obj, key, OperatorType::OWN);
1886 ASSERT(op.IsFound());
1887 op.DefineGetter(value);
1888 }
1889
CreateObjectFromProperties(const JSThread * thread,const JSHandle<TaggedArray> & properties)1890 JSHandle<JSObject> JSObject::CreateObjectFromProperties(const JSThread *thread, const JSHandle<TaggedArray> &properties)
1891 {
1892 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1893 size_t length = properties->GetLength();
1894 uint32_t propsLen = 0;
1895 for (size_t i = 0; i < length; i += 2) { // 2: skip a pair of key and value
1896 if (properties->Get(i).IsHole()) {
1897 break;
1898 }
1899 propsLen++;
1900 }
1901 if (propsLen <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES) {
1902 JSHandle<JSObject> obj = factory->NewOldSpaceObjLiteralByHClass(properties, propsLen);
1903 ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1904 for (size_t i = 0; i < propsLen; i++) {
1905 // 2: literal contains a pair of key-value
1906 obj->SetPropertyInlinedProps(thread, i, properties->Get(i * 2 + 1));
1907 }
1908 return obj;
1909 } else {
1910 JSHandle<JSObject> obj = factory->NewEmptyJSObject();
1911 JSHClass::TransitionToDictionary(thread, obj);
1912
1913 JSMutableHandle<NameDictionary> dict(
1914 thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propsLen)));
1915 JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
1916 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
1917 for (size_t i = 0; i < propsLen; i++) {
1918 PropertyAttributes attr = PropertyAttributes::Default();
1919 // 2: literal contains a pair of key-value
1920 valueHandle.Update(properties->Get(i * 2 + 1));
1921 // 2: literal contains a pair of key-value
1922 keyHandle.Update(properties->Get(i * 2));
1923 JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr);
1924 dict.Update(newDict);
1925 }
1926 obj->SetProperties(thread, dict);
1927 return obj;
1928 }
1929 }
1930
AddAccessor(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<AccessorData> & value,PropertyAttributes attr)1931 void JSObject::AddAccessor(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
1932 const JSHandle<AccessorData> &value, PropertyAttributes attr)
1933 {
1934 ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
1935 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1936 ASSERT_PRINT(attr.IsAccessor(), "Attr is not AccessorData");
1937 ObjectOperator op(thread, obj, key, OperatorType::OWN);
1938 ASSERT(!op.IsFound());
1939 op.AddProperty(JSHandle<JSObject>::Cast(obj), JSHandle<JSTaggedValue>(value), attr);
1940 }
1941
UpdatePropertyInDictionary(const JSThread * thread,JSTaggedValue key,JSTaggedValue value)1942 bool JSObject::UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value)
1943 {
1944 [[maybe_unused]] DisallowGarbageCollection noGc;
1945 NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject());
1946 int entry = dict->FindEntry(key);
1947 if (entry == -1) {
1948 return false;
1949 }
1950 dict->UpdateValue(thread, entry, value);
1951 return true;
1952 }
1953
1954 // The hash field may be a hash value, FunctionExtraInfo(JSNativePointer) or TaggedArray
SetHash(int32_t hash)1955 void ECMAObject::SetHash(int32_t hash)
1956 {
1957 JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
1958 JSTaggedValue value(hashField);
1959 if (value.IsHeapObject()) {
1960 JSThread *thread = this->GetJSThread();
1961 // Hash position reserve in advance.
1962 if (value.IsTaggedArray()) {
1963 TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject());
1964 array->Set(thread, array->GetExtraLength() + HASH_INDEX, JSTaggedValue(hash));
1965 } else if (value.IsNativePointer()) { // FunctionExtraInfo
1966 JSHandle<TaggedArray> newArray =
1967 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(RESOLVED_MAX_SIZE);
1968 newArray->SetExtraLength(0);
1969 newArray->Set(thread, HASH_INDEX, JSTaggedValue(hash));
1970 newArray->Set(thread, FUNCTION_EXTRA_INDEX, value);
1971 Barriers::SetObject<true>(thread, this, HASH_OFFSET, newArray.GetTaggedValue().GetRawData());
1972 } else {
1973 UNREACHABLE();
1974 }
1975 } else {
1976 Barriers::SetPrimitive<JSTaggedType>(this, HASH_OFFSET, JSTaggedValue(hash).GetRawData());
1977 }
1978 }
1979
GetHash() const1980 int32_t ECMAObject::GetHash() const
1981 {
1982 JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
1983 JSTaggedValue value(hashField);
1984 if (value.IsHeapObject()) {
1985 if (value.IsTaggedArray()) {
1986 TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject());
1987 return array->Get(array->GetExtraLength() + HASH_INDEX).GetInt();
1988 } else {
1989 // Default is 0
1990 return 0;
1991 }
1992 }
1993 JSThread *thread = this->GetJSThread();
1994 JSHandle<JSTaggedValue> valueHandle(thread, value);
1995 return JSTaggedValue::ToInt32(thread, valueHandle);
1996 }
1997
HasHash() const1998 bool ECMAObject::HasHash() const
1999 {
2000 JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2001 JSTaggedValue value(hashField);
2002 if (value.IsInt() && value.GetInt() == 0) {
2003 return false;
2004 }
2005 return true;
2006 }
2007
GetNativePointerField(int32_t index) const2008 void *ECMAObject::GetNativePointerField(int32_t index) const
2009 {
2010 JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2011 JSTaggedValue value(hashField);
2012 if (value.IsTaggedArray()) {
2013 JSThread *thread = this->GetJSThread();
2014 JSHandle<TaggedArray> array(thread, value);
2015 if (static_cast<int32_t>(array->GetExtraLength()) > index) {
2016 JSHandle<JSNativePointer> pointer(thread, array->Get(index));
2017 return pointer->GetExternalPointer();
2018 }
2019 }
2020 return nullptr;
2021 }
2022
SetNativePointerField(int32_t index,void * nativePointer,const DeleteEntryPoint & callBack,void * data,size_t nativeBindingsize)2023 void ECMAObject::SetNativePointerField(int32_t index, void *nativePointer,
2024 const DeleteEntryPoint &callBack, void *data, size_t nativeBindingsize)
2025 {
2026 JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2027 JSTaggedValue value(hashField);
2028 if (value.IsTaggedArray()) {
2029 JSThread *thread = this->GetJSThread();
2030 JSHandle<TaggedArray> array(thread, value);
2031 if (static_cast<int32_t>(array->GetExtraLength()) > index) {
2032 EcmaVM *vm = thread->GetEcmaVM();
2033 JSHandle<JSTaggedValue> current = JSHandle<JSTaggedValue>(thread, array->Get(thread, index));
2034 if (!current->IsHole() && nativePointer == nullptr) {
2035 // Try to remove native pointer if exists.
2036 vm->RemoveFromNativePointerList(*JSHandle<JSNativePointer>(current));
2037 array->Set(thread, index, JSTaggedValue::Hole());
2038 } else {
2039 JSHandle<JSNativePointer> pointer = vm->GetFactory()->NewJSNativePointer(
2040 nativePointer, callBack, data, false, nativeBindingsize);
2041 array->Set(thread, index, pointer.GetTaggedValue());
2042 }
2043 }
2044 }
2045 }
2046
GetNativePointerFieldCount() const2047 int32_t ECMAObject::GetNativePointerFieldCount() const
2048 {
2049 int32_t len = 0;
2050 JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2051 JSTaggedValue value(hashField);
2052 if (value.IsTaggedArray()) {
2053 TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject());
2054 len = static_cast<int32_t>(array->GetExtraLength());
2055 }
2056 return len;
2057 }
2058
SetNativePointerFieldCount(int32_t count)2059 void ECMAObject::SetNativePointerFieldCount(int32_t count)
2060 {
2061 if (count == 0) {
2062 return;
2063 }
2064 JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2065 JSThread *thread = this->GetJSThread();
2066 JSHandle<JSTaggedValue> value(thread, JSTaggedValue(hashField));
2067 JSHandle<ECMAObject> obj(thread, this);
2068 if (value->IsHeapObject()) {
2069 if (value->IsTaggedArray()) {
2070 JSHandle<TaggedArray> array(value);
2071 // Native Pointer field count is fixed.
2072 if (array->GetExtraLength() == 0) {
2073 JSHandle<TaggedArray> newArray =
2074 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + RESOLVED_MAX_SIZE);
2075 newArray->SetExtraLength(count);
2076 newArray->Set(thread, count + HASH_INDEX, array->Get(HASH_INDEX));
2077 newArray->Set(thread, count + FUNCTION_EXTRA_INDEX, array->Get(FUNCTION_EXTRA_INDEX));
2078 Barriers::SetObject<true>(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData());
2079 }
2080 } else if (value->IsJSNativePointer()) {
2081 JSHandle<TaggedArray> newArray =
2082 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + RESOLVED_MAX_SIZE);
2083 newArray->SetExtraLength(count);
2084 newArray->Set(thread, count + HASH_INDEX, JSTaggedValue(0));
2085 newArray->Set(thread, count + FUNCTION_EXTRA_INDEX, value);
2086 Barriers::SetObject<true>(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData());
2087 } else {
2088 UNREACHABLE();
2089 }
2090 } else {
2091 JSHandle<TaggedArray> newArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + 1);
2092 newArray->SetExtraLength(count);
2093 newArray->Set(thread, count + HASH_INDEX, value);
2094 Barriers::SetObject<true>(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData());
2095 }
2096 }
2097 } // namespace panda::ecmascript
2098