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