• 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             hasReceiver = true;
749         }
750         bool isSuccess = true;
751         if (op->IsFound() && !op->IsOnPrototype()) {
752             // i. If IsAccessorDescriptor(existingDescriptor) is true, return false.
753             if (op->IsAccessorDescriptor() && !isInternalAccessor) {
754                 return false;
755             }
756 
757             // ii. If existingDescriptor.[[Writable]] is false, return false.
758             if (!op->IsWritable()) {
759                 if (mayThrow) {
760                     THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false);
761                 }
762                 return false;
763             }
764             isSuccess = op->UpdateDataValue(JSHandle<JSObject>(receiver), value, isInternalAccessor, mayThrow);
765             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, isSuccess);
766         } else {
767             // 5f. Else if Receiver does not currently have a property P, Return CreateDataProperty(Receiver, P, V).
768             if (!receiver->IsExtensible(thread)) {
769                 if (mayThrow) {
770                     THROW_TYPE_ERROR_AND_RETURN(thread, "receiver is not Extensible", false);
771                 }
772                 return false;
773             }
774             if (hasReceiver || isInternalAccessor) {
775                 return op->AddProperty(JSHandle<JSObject>(receiver), value, PropertyAttributes::Default());
776             } else {
777                 return op->AddProperty(JSHandle<JSObject>(receiver), value, op->GetAttr());
778             }
779         }
780         return isSuccess;
781     }
782     // 6. Assert: IsAccessorDescriptor(ownDesc) is true.
783     ASSERT(op->IsAccessorDescriptor());
784     // 8. If setter is undefined, return false.
785     JSTaggedValue ret = ShouldGetValueFromBox(op);
786     AccessorData *accessor = AccessorData::Cast(ret.GetTaggedObject());
787     return CallSetter(thread, *accessor, receiver, value, mayThrow);
788 }
789 
CallSetter(JSThread * thread,const AccessorData & accessor,const JSHandle<JSTaggedValue> & receiver,const JSHandle<JSTaggedValue> & value,bool mayThrow)790 bool JSObject::CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle<JSTaggedValue> &receiver,
791                           const JSHandle<JSTaggedValue> &value, bool mayThrow)
792 {
793     if (UNLIKELY(accessor.IsInternal())) {
794         return accessor.CallInternalSet(thread, JSHandle<JSObject>::Cast(receiver), value, mayThrow);
795     }
796     JSTaggedValue setter = accessor.GetSetter();
797     // 8. If setter is undefined, return false.
798     if (setter.IsUndefined()) {
799         if (mayThrow) {
800             THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property when setter is undefined", false);
801         }
802         return false;
803     }
804 
805     JSHandle<JSTaggedValue> func(thread, setter);
806     if (thread->IsPGOProfilerEnable()) {
807         auto profiler = thread->GetEcmaVM()->GetPGOProfiler();
808         profiler->ProfileCall(JSTaggedValue::VALUE_UNDEFINED, func.GetTaggedType());
809     }
810     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
811     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 1);
812     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
813     info->SetCallArg(value.GetTaggedValue());
814     JSFunction::Call(info);
815 
816     // 10. ReturnIfAbrupt(setterResult).
817     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
818 
819     return true;
820 }
821 
CallGetter(JSThread * thread,const AccessorData * accessor,const JSHandle<JSTaggedValue> & receiver)822 JSTaggedValue JSObject::CallGetter(JSThread *thread, const AccessorData *accessor,
823                                    const JSHandle<JSTaggedValue> &receiver)
824 {
825     JSTaggedValue getter = accessor->GetGetter();
826     // 7. If getter is undefined, return undefined.
827     if (getter.IsUndefined()) {
828         return JSTaggedValue::Undefined();
829     }
830 
831     JSHandle<JSTaggedValue> func(thread, getter);
832     if (thread->IsPGOProfilerEnable()) {
833         auto profiler = thread->GetEcmaVM()->GetPGOProfiler();
834         profiler->ProfileCall(JSTaggedValue::VALUE_UNDEFINED, func.GetTaggedType());
835     }
836     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
837     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 0);
838     JSTaggedValue res = JSFunction::Call(info);
839     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
840     return res;
841 }
842 
843 // 9.1.8 [[Get]] (P, Receiver)
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & receiver)844 OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
845                                       const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &receiver)
846 {
847     ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
848     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
849 
850     ObjectOperator op(thread, obj, receiver, key);
851     return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
852 }
853 
GetProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key)854 OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle<JSObject> &obj,
855                                       const JSHandle<JSTaggedValue> &key)
856 {
857     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject");
858     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
859 
860     ObjectOperator op(thread, obj, key);
861     return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
862 }
863 
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)864 OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
865                                       const JSHandle<JSTaggedValue> &key)
866 {
867     ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
868     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
869 
870     ObjectOperator op(thread, obj, key);
871     return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
872 }
873 
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index)874 OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index)
875 {
876     ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
877 
878     ObjectOperator op(thread, obj, index);
879     return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
880 }
881 
GetPropertyFromGlobal(JSThread * thread,const JSHandle<JSTaggedValue> & key)882 OperationResult JSObject::GetPropertyFromGlobal(JSThread *thread, const JSHandle<JSTaggedValue> &key)
883 {
884     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
885 
886     ObjectOperator op(thread, key);
887     return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
888 }
889 
GetProperty(JSThread * thread,ObjectOperator * op)890 JSTaggedValue JSObject::GetProperty(JSThread *thread, ObjectOperator *op)
891 {
892     JSHandle<JSTaggedValue> receiver = op->GetReceiver();
893     JSHandle<JSTaggedValue> holder = op->GetHolder();
894     if (holder->IsJSProxy()) {
895         if (op->IsElement()) {
896             JSHandle<JSTaggedValue> key(thread, JSTaggedValue(op->GetElementIndex()));
897             return JSProxy::GetProperty(thread, JSHandle<JSProxy>::Cast(holder), key, receiver)
898                 .GetValue()
899                 .GetTaggedValue();
900         }
901         return JSProxy::GetProperty(thread, JSHandle<JSProxy>::Cast(holder), op->GetKey(), receiver)
902             .GetValue()
903             .GetTaggedValue();
904     }
905 
906     // 4. If desc is undefined, then
907     if (!op->IsFound()) {
908         // 4c. If obj and parent is null, return undefined.
909         return JSTaggedValue::Undefined();
910     }
911     // 5. If IsDataDescriptor(desc) is true, return desc.[[Value]]
912     JSTaggedValue ret = ShouldGetValueFromBox(op);
913     if (!op->IsAccessorDescriptor()) {
914         return ret;
915     }
916 
917     // 6. Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]].
918     AccessorData *accessor = AccessorData::Cast(ret.GetTaggedObject());
919     // 8. Return Call(getter, Receiver).
920     if (UNLIKELY(accessor->IsInternal())) {
921         return accessor->CallInternalGet(thread, JSHandle<JSObject>::Cast(holder));
922     }
923     return CallGetter(thread, accessor, receiver);
924 }
925 
DeleteProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key)926 bool JSObject::DeleteProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key)
927 {
928     // 1. Assert: IsPropertyKey(P) is true.
929     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
930     // 2. Let desc be O.[[GetOwnProperty]](P).
931 
932     ObjectOperator op(thread, JSHandle<JSTaggedValue>(obj), key, OperatorType::OWN);
933 
934     // 4. If desc is undefined, return true.
935     if (!op.IsFound()) {
936         return true;
937     }
938     // 5. If desc.[[Configurable]] is true, then
939     // a. Remove the own property with name P from O.
940     // b. Return true.
941     // 6. Return false.
942     if (op.IsConfigurable()) {
943         op.DeletePropertyInHolder();
944         obj->GetClass()->SetHasDeleteProperty(true);
945         return true;
946     }
947     return false;
948 }
949 
GetOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)950 bool JSObject::GetOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
951                               PropertyDescriptor &desc)
952 {
953     return OrdinaryGetOwnProperty(thread, obj, key, desc);
954 }
955 
GlobalGetOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)956 bool JSObject::GlobalGetOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
957 {
958     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
959     ObjectOperator op(thread, key, OperatorType::OWN);
960 
961     if (!op.IsFound()) {
962         return false;
963     }
964 
965     op.ToPropertyDescriptor(desc);
966 
967     if (desc.HasValue()) {
968         PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject());
969         JSHandle<JSTaggedValue> valueHandle(thread, cell->GetValue());
970         desc.SetValue(valueHandle);
971     }
972     ASSERT(!desc.GetValue()->IsInternalAccessor());
973     return true;
974 }
975 
OrdinaryGetOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)976 bool JSObject::OrdinaryGetOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj,
977                                       const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
978 {
979     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
980     ObjectOperator op(thread, JSHandle<JSTaggedValue>(obj), key, OperatorType::OWN);
981 
982     if (!op.IsFound()) {
983         return false;
984     }
985 
986     op.ToPropertyDescriptor(desc);
987 
988     if (desc.HasValue() && obj->IsJSGlobalObject()) {
989         JSTaggedValue val = desc.GetValue().GetTaggedValue();
990         if (val.IsPropertyBox()) {
991             PropertyBox *cell = PropertyBox::Cast(val.GetTaggedObject());
992             JSHandle<JSTaggedValue> valueHandle(thread, cell->GetValue());
993             desc.SetValue(valueHandle);
994         }
995     }
996 
997     return true;
998 }
999 
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)1000 bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
1001                                  const PropertyDescriptor &desc)
1002 {
1003     return OrdinaryDefineOwnProperty(thread, obj, key, desc);
1004 }
1005 
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const PropertyDescriptor & desc)1006 bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
1007                                  const PropertyDescriptor &desc)
1008 {
1009     return OrdinaryDefineOwnProperty(thread, obj, index, desc);
1010 }
1011 
1012 // 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc)
OrdinaryDefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)1013 bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj,
1014                                          const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc)
1015 {
1016     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1017     // 1. Let current be O.[[GetOwnProperty]](P).
1018     JSHandle<JSTaggedValue> objValue(obj);
1019     ObjectOperator op(thread, objValue, key, OperatorType::OWN);
1020 
1021     bool extensible = obj->IsExtensible();
1022     PropertyDescriptor current(thread);
1023     op.ToPropertyDescriptor(current);
1024     // 4. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).
1025     return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current);
1026 }
1027 
OrdinaryDefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const PropertyDescriptor & desc)1028 bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
1029                                          const PropertyDescriptor &desc)
1030 {
1031     JSHandle<JSTaggedValue> objValue(obj);
1032     ObjectOperator op(thread, objValue, index, OperatorType::OWN);
1033 
1034     bool extensible = obj->IsExtensible();
1035     PropertyDescriptor current(thread);
1036     op.ToPropertyDescriptor(current);
1037     return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current);
1038 }
1039 
1040 // 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current)
ValidateAndApplyPropertyDescriptor(ObjectOperator * op,bool extensible,const PropertyDescriptor & desc,const PropertyDescriptor & current)1041 bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc,
1042                                                   const PropertyDescriptor &current)
1043 {
1044     // 2. If current is undefined, then
1045     if (current.IsEmpty()) {
1046         // 2a. If extensible is false, return false.
1047         if (!extensible) {
1048             return false;
1049         }
1050         if (!op->HasHolder()) {
1051             return true;
1052         }
1053 
1054         // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then
1055         PropertyAttributes attr(desc);
1056         bool success = false;
1057         if (!desc.IsAccessorDescriptor()) {
1058             success = op->AddPropertyInHolder(desc.GetValue(), attr);
1059         } else {  // is AccessorDescriptor
1060             // may GC in NewAccessorData, so we need to handle getter and setter.
1061             JSThread *thread = op->GetThread();
1062             JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData();
1063             if (desc.HasGetter()) {
1064                 accessor->SetGetter(thread, desc.GetGetter());
1065             }
1066 
1067             if (desc.HasSetter()) {
1068                 accessor->SetSetter(thread, desc.GetSetter());
1069             }
1070             success = op->AddPropertyInHolder(JSHandle<JSTaggedValue>::Cast(accessor), attr);
1071         }
1072 
1073         return success;
1074     }
1075 
1076     // 3. Return true, if every field in Desc is absent
1077     // 4. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the
1078     // same value as the corresponding field in current when compared using the SameValue algorithm.
1079     if ((!desc.HasEnumerable() || desc.IsEnumerable() == current.IsEnumerable()) &&
1080         (!desc.HasConfigurable() || desc.IsConfigurable() == current.IsConfigurable()) &&
1081         (!desc.HasValue() || JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) &&
1082         (!desc.HasWritable() || (current.IsWritable() == desc.IsWritable())) &&
1083         (!desc.HasGetter() ||
1084          (current.HasGetter() && JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter()))) &&
1085         (!desc.HasSetter() ||
1086          (current.HasSetter() && JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())))) {
1087         return true;
1088     }
1089 
1090     // 5. If the [[Configurable]] field of current is false, then
1091     if (!current.IsConfigurable()) {
1092         // 5a. Return false, if the [[Configurable]] field of Desc is true.
1093         if (desc.HasConfigurable() && desc.IsConfigurable()) {
1094             return false;
1095         }
1096         // b. Return false, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current
1097         // and Desc are the Boolean negation of each other.
1098         if (desc.HasEnumerable() && (desc.IsEnumerable() != current.IsEnumerable())) {
1099             return false;
1100         }
1101     }
1102 
1103     // 6. If IsGenericDescriptor(Desc) is true, no further validation is required.
1104     if (desc.IsGenericDescriptor()) {
1105         // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
1106     } else if (current.IsDataDescriptor() != desc.IsDataDescriptor()) {
1107         // 7a. Return false, if the [[Configurable]] field of current is false.
1108         if (!current.IsConfigurable()) {
1109             return false;
1110         }
1111         // 7b. If IsDataDescriptor(current) is true, then
1112         if (current.IsDataDescriptor()) {
1113             // 7bi. If O is not undefined, convert the property named P of object O from a data property to an
1114             // accessor property. Preserve the existing values of the converted property’s [[Configurable]] and
1115             // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values.
1116         } else {
1117             // 7ci.  If O is not undefined, convert the property named P of object O from an accessor property to a
1118             // data property. Preserve the existing values of the converted property’s [[Configurable]] and
1119             // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values.
1120         }
1121         // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
1122     } else if (current.IsDataDescriptor() && desc.IsDataDescriptor()) {
1123         // 8a. If the [[Configurable]] field of current is false, then
1124         if (!current.IsConfigurable()) {
1125             // 8a i. Return false, if the [[Writable]] field of current is false and the [[Writable]] field of Desc
1126             // is true.
1127             if (!current.IsWritable() && desc.HasWritable() && desc.IsWritable()) {
1128                 return false;
1129             }
1130             // 8a ii. If the [[Writable]] field of current is false, then
1131             if (!current.IsWritable()) {
1132                 if (desc.HasValue() && !JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) {
1133                     return false;
1134                 }
1135             }
1136         }
1137         // 8b. Else the [[Configurable]] field of current is true, so any change is acceptable.
1138     } else {  // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true,
1139         // 9a. If the [[Configurable]] field of current is false, then
1140         if (!current.IsConfigurable()) {
1141             // i. Return false, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]])
1142             // is false.
1143             if (desc.HasSetter() && !JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())) {
1144                 return false;
1145             }
1146             // ii. Return false, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]],
1147             // current.[[Get]]) is false.
1148             if (desc.HasGetter() && !JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter())) {
1149                 return false;
1150             }
1151         }
1152     }
1153 
1154     if (op->HasHolder()) {
1155         // 10. If O is not undefined, then
1156         // a. For each field of Desc that is present, set the corresponding attribute of the property named P of object
1157         // O to the value of the field.
1158         return op->WriteDataPropertyInHolder(desc);
1159     }
1160     return true;
1161 }
1162 
1163 // 9.1.6.2 IsCompatiblePropertyDescriptor (Extensible, Desc, Current)
IsCompatiblePropertyDescriptor(bool extensible,const PropertyDescriptor & desc,const PropertyDescriptor & current)1164 bool JSObject::IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc,
1165                                               const PropertyDescriptor &current)
1166 {
1167     // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current).
1168     ObjectOperator op;
1169     return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current);
1170 }
1171 
GetPrototype(const JSHandle<JSObject> & obj)1172 JSTaggedValue JSObject::GetPrototype(const JSHandle<JSObject> &obj)
1173 {
1174     JSHClass *hclass = obj->GetJSHClass();
1175     return hclass->GetPrototype();
1176 }
1177 
SetPrototype(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & proto)1178 bool JSObject::SetPrototype(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &proto)
1179 {
1180     ASSERT_PRINT(proto->IsECMAObject() || proto->IsNull(), "proto must be object or null");
1181     JSTaggedValue current = JSObject::GetPrototype(obj);
1182     if (current == proto.GetTaggedValue()) {
1183         return true;
1184     }
1185     if (!obj->IsExtensible()) {
1186         return false;
1187     }
1188     bool done = false;
1189     JSMutableHandle<JSTaggedValue> tempProtoHandle(thread, proto.GetTaggedValue());
1190     while (!done) {
1191         if (tempProtoHandle->IsNull() || !tempProtoHandle->IsECMAObject()) {
1192             done = true;
1193         } else if (JSTaggedValue::SameValue(tempProtoHandle.GetTaggedValue(), obj.GetTaggedValue())) {
1194             return false;
1195         } else {
1196             if (tempProtoHandle->IsJSProxy()) {
1197                 break;
1198             }
1199             tempProtoHandle.Update(JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>(tempProtoHandle)));
1200         }
1201     }
1202     // map transition
1203     JSHandle<JSHClass> hclass(thread, obj->GetJSHClass());
1204     JSHandle<JSHClass> newClass = JSHClass::TransitionProto(thread, hclass, proto);
1205     JSHClass::NotifyHclassChanged(thread, hclass, newClass);
1206     obj->SynchronizedSetClass(*newClass);
1207     thread->NotifyStableArrayElementsGuardians(obj, StableArrayChangeKind::PROTO);
1208     return true;
1209 }
1210 
GetCtorFromPrototype(JSThread * thread,JSTaggedValue prototype)1211 JSTaggedValue JSObject::GetCtorFromPrototype(JSThread *thread, JSTaggedValue prototype)
1212 {
1213     if (!prototype.IsJSObject()) {
1214         return JSTaggedValue::Undefined();
1215     }
1216     JSHandle<JSTaggedValue> object(thread, prototype);
1217     JSHandle<JSTaggedValue> ctorKey = thread->GlobalConstants()->GetHandledConstructorString();
1218     JSHandle<JSTaggedValue> ctorObj(JSObject::GetProperty(thread, object, ctorKey).GetValue());
1219     if (thread->HasPendingException()) {
1220         thread->ClearException();
1221         return JSTaggedValue::Undefined();
1222     }
1223     return ctorObj.GetTaggedValue();
1224 }
1225 
HasProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key)1226 bool JSObject::HasProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key)
1227 {
1228     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1229     JSHandle<JSTaggedValue> objValue(obj);
1230     ObjectOperator op(thread, objValue, key);
1231 
1232     JSHandle<JSTaggedValue> holder = op.GetHolder();
1233     if (holder->IsJSProxy()) {
1234         return JSProxy::HasProperty(thread, JSHandle<JSProxy>::Cast(holder), key);
1235     }
1236 
1237     return op.IsFound();
1238 }
1239 
HasProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index)1240 bool JSObject::HasProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index)
1241 {
1242     JSHandle<JSTaggedValue> objValue(obj);
1243     ObjectOperator op(thread, objValue, index);
1244 
1245     JSHandle<JSTaggedValue> holder = op.GetHolder();
1246     if (holder->IsJSProxy()) {
1247         JSHandle<JSTaggedValue> key(thread, JSTaggedValue(index));
1248         return JSProxy::HasProperty(thread, JSHandle<JSProxy>::Cast(holder), key);
1249     }
1250 
1251     return op.IsFound();
1252 }
1253 
PreventExtensions(JSThread * thread,const JSHandle<JSObject> & obj)1254 bool JSObject::PreventExtensions(JSThread *thread, const JSHandle<JSObject> &obj)
1255 {
1256     if (obj->IsExtensible()) {
1257         JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
1258         JSHandle<JSHClass> newHclass = JSHClass::TransitionExtension(thread, jshclass);
1259         obj->SynchronizedSetClass(*newHclass);
1260     }
1261 
1262     return true;
1263 }
1264 
1265 // 9.1.12 [[OwnPropertyKeys]] ( )
GetOwnPropertyKeys(JSThread * thread,const JSHandle<JSObject> & obj)1266 JSHandle<TaggedArray> JSObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj)
1267 {
1268     uint32_t numOfElements = obj->GetNumberOfElements();
1269     uint32_t keyLen = numOfElements + obj->GetNumberOfKeys();
1270 
1271     JSHandle<TaggedArray> keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen);
1272 
1273     if (numOfElements > 0) {
1274         GetAllElementKeys(thread, obj, 0, keyArray);
1275     }
1276     GetAllKeys(thread, obj, static_cast<int32_t>(numOfElements), keyArray);
1277     return keyArray;
1278 }
1279 
GetAllPropertyKeys(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t filter)1280 JSHandle<TaggedArray> JSObject::GetAllPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t filter)
1281 {
1282     JSMutableHandle<JSObject> currentObj(thread, obj);
1283     JSMutableHandle<JSTaggedValue> currentObjValue(thread, currentObj);
1284 
1285     uint32_t curObjNumberOfElements = currentObj->GetNumberOfElements();
1286     uint32_t curObjNumberOfKeys = currentObj->GetNumberOfKeys();
1287     uint32_t curObjectKeysLength = curObjNumberOfElements + curObjNumberOfKeys;
1288     uint32_t retArrayLength = curObjectKeysLength;
1289     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1290     JSMutableHandle<TaggedArray> retArray(thread, factory->NewTaggedArray(retArrayLength));
1291     uint32_t retArrayEffectivelength = 0;
1292 
1293     do {
1294         curObjNumberOfElements = currentObj->GetNumberOfElements();
1295         curObjNumberOfKeys = currentObj->GetNumberOfKeys();
1296         curObjectKeysLength = curObjNumberOfElements + curObjNumberOfKeys;
1297         uint32_t minRequireLength = curObjectKeysLength + retArrayEffectivelength;
1298         if (retArrayLength < minRequireLength) {
1299             // expand retArray
1300             retArray.Update(factory->NewAndCopyTaggedArray(retArray, minRequireLength, retArrayLength));
1301             retArrayLength = minRequireLength;
1302         }
1303 
1304         GetAllElementKeysByFilter(thread, currentObj, retArray, retArrayEffectivelength, filter);
1305 
1306         GetAllKeysByFilter(thread, currentObj, retArrayEffectivelength, retArray, filter);
1307         bool isInculdePrototypes = (filter & NATIVE_KEY_INCLUDE_PROTOTYPES);
1308         if (!isInculdePrototypes) {
1309             break;
1310         }
1311         currentObj.Update(GetPrototype(currentObj));
1312         currentObjValue.Update(currentObj);
1313     } while (currentObjValue->IsHeapObject());
1314 
1315     JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Undefined());
1316     if (filter & NATIVE_KEY_NUMBERS_TO_STRINGS) {
1317         for (uint32_t i = 0; i < retArrayEffectivelength; i++) {
1318             element.Update(retArray->Get(i));
1319             if (element->IsNumber()) {
1320                 retArray->Set(thread, i, base::NumberHelper::NumberToString(thread,
1321                     JSTaggedValue(element->GetNumber())));
1322             }
1323         }
1324     }
1325     uint32_t elementIndex = 0;
1326     while ((filter & NATIVE_KEY_SKIP_STRINGS) && (retArrayEffectivelength > 0) &&
1327            (elementIndex < retArrayEffectivelength)) {
1328         if (retArray->Get(elementIndex).IsString()) {
1329             TaggedArray::RemoveElementByIndex(thread, retArray, elementIndex, retArrayEffectivelength);
1330             retArrayEffectivelength--;
1331         } else {
1332             elementIndex++;
1333         }
1334     }
1335     retArray->Trim(thread, retArrayEffectivelength);
1336     return retArray;
1337 }
1338 
GetOwnEnumPropertyKeys(JSThread * thread,const JSHandle<JSObject> & obj)1339 JSHandle<TaggedArray> JSObject::GetOwnEnumPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj)
1340 {
1341     uint32_t numOfElements = obj->GetNumberOfElements();
1342     uint32_t keyLen = numOfElements + obj->GetNumberOfKeys();
1343 
1344     JSHandle<TaggedArray> keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen);
1345 
1346     if (numOfElements > 0) {
1347         GetEnumElementKeys(thread, obj, 0, keyArray);
1348     }
1349     GetAllEnumKeys(thread, obj, static_cast<int32_t>(numOfElements), keyArray);
1350     return keyArray;
1351 }
1352 
ObjectCreate(JSThread * thread,const JSHandle<JSObject> & proto)1353 JSHandle<JSObject> JSObject::ObjectCreate(JSThread *thread, const JSHandle<JSObject> &proto)
1354 {
1355     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1356     JSHandle<JSFunction> constructor(env->GetObjectFunction());
1357     JSHandle<JSObject> objHandle = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(constructor);
1358     SetPrototype(thread, objHandle, JSHandle<JSTaggedValue>(proto));
1359     return objHandle;
1360 }
1361 
1362 // 7.3.4 CreateDataProperty (O, P, V)
CreateDataProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)1363 bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
1364                                   const JSHandle<JSTaggedValue> &value)
1365 {
1366     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1367     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1368     auto result = ObjectFastOperator::SetPropertyByValue<true>(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
1369                                                                value.GetTaggedValue());
1370     if (!result.IsHole()) {
1371         return !result.IsException();
1372     }
1373     PropertyDescriptor desc(thread, value, true, true, true);
1374     return JSTaggedValue::DefineOwnProperty(thread, JSHandle<JSTaggedValue>::Cast(obj), key, desc);
1375 }
1376 
CreateDataProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value)1377 bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
1378                                   const JSHandle<JSTaggedValue> &value)
1379 {
1380     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1381     auto result =
1382         ObjectFastOperator::SetPropertyByIndex<true>(thread, obj.GetTaggedValue(), index, value.GetTaggedValue());
1383     if (!result.IsHole()) {
1384         return !result.IsException();
1385     }
1386     PropertyDescriptor desc(thread, value, true, true, true);
1387     return DefineOwnProperty(thread, obj, index, desc);
1388 }
1389 
1390 // 7.3.5 CreateMethodProperty (O, P, V)
CreateDataPropertyOrThrow(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)1391 bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle<JSObject> &obj,
1392                                          const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
1393 {
1394     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1395     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1396 
1397     bool success = CreateDataProperty(thread, obj, key, value);
1398     if (!success) {
1399         THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success);
1400     }
1401     return success;
1402 }
1403 
CreateDataPropertyOrThrow(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value)1404 bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
1405                                          const JSHandle<JSTaggedValue> &value)
1406 {
1407     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1408 
1409     bool success = CreateDataProperty(thread, obj, index, value);
1410     if (!success) {
1411         THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success);
1412     }
1413     return success;
1414 }
1415 // 7.3.6 CreateDataPropertyOrThrow (O, P, V)
CreateMethodProperty(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)1416 bool JSObject::CreateMethodProperty(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
1417                                     const JSHandle<JSTaggedValue> &value)
1418 {
1419     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1420     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
1421 
1422     PropertyDescriptor desc(thread, value, true, false, true);
1423     return DefineOwnProperty(thread, obj, key, desc);
1424 }
1425 
1426 // 7.3.9 GetMethod (O, P)
GetMethod(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)1427 JSHandle<JSTaggedValue> JSObject::GetMethod(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
1428                                             const JSHandle<JSTaggedValue> &key)
1429 {
1430     JSHandle<JSTaggedValue> func = JSTaggedValue::GetProperty(thread, obj, key).GetValue();
1431     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
1432     if (func->IsUndefined() || func->IsNull()) {
1433         return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined());
1434     }
1435 
1436     if (!func->IsCallable()) {
1437         THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", func);
1438     }
1439     return func;
1440 }
1441 
1442 // 7.3.14 SetIntegrityLevel (O, level)
SetIntegrityLevel(JSThread * thread,const JSHandle<JSObject> & obj,IntegrityLevel level)1443 bool JSObject::SetIntegrityLevel(JSThread *thread, const JSHandle<JSObject> &obj, IntegrityLevel level)
1444 {
1445     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1446     ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN),
1447                  "level is not a valid IntegrityLevel");
1448 
1449     bool status = JSTaggedValue::PreventExtensions(thread, JSHandle<JSTaggedValue>(obj));
1450     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1451     if (!status) {
1452         return false;
1453     }
1454 
1455     JSHandle<TaggedArray> jshandleKeys =
1456         JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle<JSTaggedValue>(obj));
1457     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1458     PropertyDescriptor descNoConf(thread);
1459     descNoConf.SetConfigurable(false);
1460     PropertyDescriptor descNoConfWrite(thread);
1461     descNoConfWrite.SetWritable(false);
1462     descNoConfWrite.SetConfigurable(false);
1463 
1464     if (level == IntegrityLevel::SEALED) {
1465         uint32_t length = jshandleKeys->GetLength();
1466         if (length == 0) {
1467             return true;
1468         }
1469         auto key = jshandleKeys->Get(0);
1470         JSMutableHandle<JSTaggedValue> handleKey(thread, key);
1471         for (uint32_t i = 0; i < length; i++) {
1472             auto taggedKey = JSTaggedValue(jshandleKeys->Get(i));
1473             handleKey.Update(taggedKey);
1474             [[maybe_unused]] bool success =
1475                 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(obj), handleKey, descNoConf);
1476             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1477         }
1478     } else {
1479         uint32_t length = jshandleKeys->GetLength();
1480         if (length == 0) {
1481             return true;
1482         }
1483         auto key = jshandleKeys->Get(0);
1484         JSMutableHandle<JSTaggedValue> handleKey(thread, key);
1485         for (uint32_t i = 0; i < length; i++) {
1486             auto taggedKey = JSTaggedValue(jshandleKeys->Get(i));
1487             handleKey.Update(taggedKey);
1488             PropertyDescriptor currentDesc(thread);
1489             bool curDescStatus =
1490                 JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj), handleKey, currentDesc);
1491             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1492             if (curDescStatus) {
1493                 PropertyDescriptor desc = currentDesc.IsAccessorDescriptor() ? descNoConf : descNoConfWrite;
1494                 [[maybe_unused]] bool success =
1495                     JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(obj), handleKey, desc);
1496                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1497             }
1498         }
1499     }
1500     return true;
1501 }
1502 
1503 // 7.3.15 TestIntegrityLevel (O, level)
TestIntegrityLevel(JSThread * thread,const JSHandle<JSObject> & obj,IntegrityLevel level)1504 bool JSObject::TestIntegrityLevel(JSThread *thread, const JSHandle<JSObject> &obj, IntegrityLevel level)
1505 {
1506     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
1507     ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN),
1508                  "level is not a valid IntegrityLevel");
1509 
1510     bool status = JSHandle<JSTaggedValue>(obj)->IsExtensible(thread);
1511     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1512     if (status) {
1513         return false;
1514     }
1515 
1516     JSHandle<TaggedArray> jshandleKeys =
1517         JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle<JSTaggedValue>(obj));
1518     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1519     uint32_t length = jshandleKeys->GetLength();
1520     if (length == 0) {
1521         return true;
1522     }
1523     auto key = jshandleKeys->Get(0);
1524     JSMutableHandle<JSTaggedValue> handleKey(thread, key);
1525     for (uint32_t i = 0; i < length; i++) {
1526         auto taggedKey = JSTaggedValue(jshandleKeys->Get(i));
1527         handleKey.Update(taggedKey);
1528         PropertyDescriptor currentDesc(thread);
1529         bool curDescStatus =
1530             JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj), handleKey, currentDesc);
1531         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1532         if (curDescStatus) {
1533             if (currentDesc.IsConfigurable()) {
1534                 return false;
1535             }
1536             if (level == IntegrityLevel::FROZEN &&
1537                 currentDesc.IsDataDescriptor() && currentDesc.IsWritable()) {
1538                 return false;
1539             }
1540         }
1541     }
1542     return true;
1543 }
1544 
1545 // 7.3.21 EnumerableOwnNames (O)
EnumerableOwnNames(JSThread * thread,const JSHandle<JSObject> & obj)1546 JSHandle<TaggedArray> JSObject::EnumerableOwnNames(JSThread *thread, const JSHandle<JSObject> &obj)
1547 {
1548     ASSERT_PRINT(obj->IsECMAObject(), "obj is not object");
1549     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1550     JSHandle<JSTaggedValue> tagObj(obj);
1551     // fast mode
1552     if (tagObj->IsJSObject() && !tagObj->IsTypedArray() && !tagObj->IsModuleNamespace()) {
1553         uint32_t copyLengthOfKeys = 0;
1554         uint32_t copyLengthOfElements = 0;
1555         auto keyElementPair = GetOwnEnumerableNamesInFastMode(thread, obj, &copyLengthOfKeys, &copyLengthOfElements);
1556         JSHandle<TaggedArray> keyArray = keyElementPair.first;
1557         JSHandle<TaggedArray> elementArray = keyElementPair.second;
1558         JSHandle<TaggedArray> keys;
1559         if (copyLengthOfKeys != 0 && copyLengthOfElements != 0) {
1560             keys = TaggedArray::AppendSkipHole(thread, elementArray, keyArray, copyLengthOfKeys + copyLengthOfElements);
1561         } else if (copyLengthOfKeys != 0) {
1562             keys = factory->CopyArray(keyArray, copyLengthOfKeys, copyLengthOfKeys);
1563         } else if (copyLengthOfElements != 0) {
1564             keys = factory->CopyArray(elementArray, copyLengthOfElements, copyLengthOfElements);
1565         } else {
1566             keys = factory->EmptyArray();
1567         }
1568         return keys;
1569     }
1570 
1571     uint32_t copyLength = 0;
1572     JSHandle<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj);
1573     RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1574     uint32_t length = keys->GetLength();
1575 
1576     JSHandle<TaggedArray> names = factory->NewTaggedArray(length);
1577     JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
1578     for (uint32_t i = 0; i < length; i++) {
1579         keyHandle.Update(keys->Get(i));
1580         if (keyHandle->IsString()) {
1581             PropertyDescriptor desc(thread);
1582             bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj),
1583                                                         keyHandle, desc);
1584             RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1585 
1586             if (status && desc.IsEnumerable()) {
1587                 names->Set(thread, copyLength, keyHandle);
1588                 copyLength++;
1589             }
1590         }
1591     }
1592 
1593     return factory->CopyArray(names, length, copyLength);
1594 }
1595 
EnumerableOwnPropertyNamesHelper(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<TaggedArray> & arr,JSHandle<TaggedArray> & prop,uint32_t & index,bool & fastMode,PropertyKind kind)1596 void JSObject::EnumerableOwnPropertyNamesHelper(JSThread *thread, const JSHandle<JSObject> &obj,
1597     const JSHandle<TaggedArray> &arr, JSHandle<TaggedArray> &prop, uint32_t &index, bool &fastMode, PropertyKind kind)
1598 {
1599     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1600     JSHandle<JSHClass> objClass(thread, obj->GetJSHClass());
1601     uint32_t length = arr->GetLength();
1602     for (uint32_t i = 0; i < length; i++) {
1603         key.Update(arr->Get(thread, i));
1604         if (!JSTaggedValue::IsPropertyKey(key)) {
1605             break;
1606         }
1607         JSTaggedValue value = JSTaggedValue::Hole();
1608         if (fastMode) {
1609             value = ObjectFastOperator::GetPropertyByValue<true>(thread, obj.GetTaggedValue(), key.GetTaggedValue());
1610             RETURN_IF_ABRUPT_COMPLETION(thread);
1611         }
1612         if (value.IsHole()) {
1613             PropertyDescriptor desc(thread);
1614             bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj), key, desc);
1615             RETURN_IF_ABRUPT_COMPLETION(thread);
1616             if (!status || !desc.IsEnumerable()) {
1617                 continue;
1618             }
1619             if (desc.HasValue()) {
1620                 value = desc.GetValue().GetTaggedValue();
1621             } else {
1622                 OperationResult opResult = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(obj), key);
1623                 RETURN_IF_ABRUPT_COMPLETION(thread);
1624                 value = opResult.GetValue().GetTaggedValue();
1625             }
1626         }
1627         index = SetValuesOrEntries(thread, prop, index, key, JSHandle<JSTaggedValue>(thread, value), kind);
1628         fastMode = fastMode ? CheckHClassHit(obj, objClass) : fastMode;
1629     }
1630 }
1631 
EnumerableOwnPropertyNames(JSThread * thread,const JSHandle<JSObject> & obj,PropertyKind kind)1632 JSHandle<TaggedArray> JSObject::EnumerableOwnPropertyNames(JSThread *thread, const JSHandle<JSObject> &obj,
1633                                                            PropertyKind kind)
1634 {
1635     // 1. Assert: Type(O) is Object.
1636     ASSERT_PRINT(obj->IsECMAObject(), "obj is not object");
1637 
1638     // 2. Let ownKeys be ? O.[[OwnPropertyKeys]]().
1639     JSHandle<JSTaggedValue> tagObj(obj);
1640     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1641     if (tagObj->IsJSObject() && !tagObj->IsJSProxy() && !tagObj->IsTypedArray() && !tagObj->IsModuleNamespace()) {
1642         uint32_t copyLengthOfKeys = 0;
1643         uint32_t copyLengthOfElements = 0;
1644         uint32_t index = 0;
1645         bool fastMode = true;
1646         auto keyElementPair = GetOwnEnumerableNamesInFastMode(thread, obj, &copyLengthOfKeys, &copyLengthOfElements);
1647         JSHandle<TaggedArray> keyArray = keyElementPair.first;
1648         JSHandle<TaggedArray> elementArray = keyElementPair.second;
1649         JSHandle<TaggedArray> properties = factory->NewTaggedArray(copyLengthOfKeys + copyLengthOfElements);
1650         if (copyLengthOfElements != 0) {
1651             EnumerableOwnPropertyNamesHelper(thread, obj, elementArray, properties, index, fastMode, kind);
1652         }
1653         if (copyLengthOfKeys != 0) {
1654             EnumerableOwnPropertyNamesHelper(thread, obj, keyArray, properties, index, fastMode, kind);
1655         }
1656         if (UNLIKELY(!fastMode && index < copyLengthOfKeys + copyLengthOfElements)) {
1657             properties->Trim(thread, index);
1658         }
1659         return properties;
1660     }
1661 
1662     JSHandle<TaggedArray> ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj);
1663     RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1664 
1665     // 3. Let properties be a new empty List.
1666     uint32_t length = ownKeys->GetLength();
1667     JSHandle<TaggedArray> properties = factory->NewTaggedArray(length);
1668 
1669     // 4. For each element key of ownKeys, do
1670     // a. If Type(key) is String, then
1671     //     i. Let desc be ? O.[[GetOwnProperty]](key).
1672     //     ii. If desc is not undefined and desc.[[Enumerable]] is true, then
1673     //         1. If kind is key, append key to properties.
1674     //         2. Else,
1675     //            a. Let value be ? Get(O, key).
1676     //            b. If kind is value, append value to properties.
1677     //            c. Else,
1678     //               i. Assert: kind is key+value.
1679     //               ii. Let entry be ! CreateArrayFromList(« key, value »).
1680     //               iii. Append entry to properties.
1681     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1682     uint32_t index = 0;
1683     for (uint32_t i = 0; i < length; i++) {
1684         key.Update(ownKeys->Get(thread, i));
1685         if (key->IsString()) {
1686             PropertyDescriptor desc(thread);
1687             bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj),
1688                                                         key, desc);
1689             RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1690             if (status && desc.IsEnumerable()) {
1691                 if (kind == PropertyKind::KEY) {
1692                     properties->Set(thread, index++, key);
1693                 } else {
1694                     OperationResult result =
1695                         JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(obj), key);
1696                     RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
1697                     JSHandle<JSTaggedValue> value = result.GetValue();
1698                     index = SetValuesOrEntries(thread, properties, index, key, value, kind);
1699                 }
1700             }
1701         }
1702     }
1703 
1704     if (UNLIKELY(index < length)) {
1705         properties->Trim(thread, index);
1706     }
1707     // 5. Return properties.
1708     return properties;
1709 }
1710 
GetFunctionRealm(JSThread * thread,const JSHandle<JSTaggedValue> & object)1711 JSHandle<GlobalEnv> JSObject::GetFunctionRealm(JSThread *thread, const JSHandle<JSTaggedValue> &object)
1712 {
1713     // 1. Assert: obj is a callable object.
1714     ASSERT(object->IsCallable());
1715     // 2. If obj has a [[Realm]] internal slot, then return obj’s [[Realm]] internal slot.
1716     // 3. If obj is a Bound Function exotic object, then
1717     if (object->IsBoundFunction()) {
1718         // a. Let target be obj’s [[BoundTargetFunction]] internal slot.
1719         JSHandle<JSTaggedValue> target(thread, JSHandle<JSBoundFunction>(object)->GetBoundTarget());
1720         // b. Return GetFunctionRealm(target).
1721         return GetFunctionRealm(thread, target);
1722     }
1723     // 4. If obj is a Proxy exotic object, then
1724     if (object->IsJSProxy()) {
1725         // a. If the value of the [[ProxyHandler]] internal slot of obj is null, throw a TypeError exception.
1726         if (JSHandle<JSProxy>(object)->GetHandler().IsNull()) {
1727             THROW_TYPE_ERROR_AND_RETURN(thread, "JSObject::GetFunctionRealm: handler is null",
1728                                         JSHandle<GlobalEnv>(thread, JSTaggedValue::Exception()));
1729         }
1730         // b. Let proxyTarget be the value of obj’s [[ProxyTarget]] internal slot.
1731         JSHandle<JSTaggedValue> proxyTarget(thread, JSHandle<JSProxy>(object)->GetTarget());
1732         return GetFunctionRealm(thread, proxyTarget);
1733     }
1734     JSTaggedValue maybeGlobalEnv = JSHandle<JSFunction>(object)->GetLexicalEnv();
1735     while (!maybeGlobalEnv.IsJSGlobalEnv()) {
1736         if (maybeGlobalEnv.IsUndefined()) {
1737             return thread->GetEcmaVM()->GetGlobalEnv();
1738         }
1739         maybeGlobalEnv = LexicalEnv::Cast(maybeGlobalEnv.GetTaggedObject())->GetParentEnv();
1740     }
1741     return JSHandle<GlobalEnv>(thread, maybeGlobalEnv);
1742 }
1743 
InstanceOf(JSThread * thread,const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & target)1744 bool JSObject::InstanceOf(JSThread *thread, const JSHandle<JSTaggedValue> &object,
1745                           const JSHandle<JSTaggedValue> &target)
1746 {
1747     // 1. If Type(target) is not Object, throw a TypeError exception.
1748     if (!target->IsECMAObject()) {
1749         THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when type of target is not Object", false);
1750     }
1751 
1752     EcmaVM *vm = thread->GetEcmaVM();
1753     // 2. Let instOfHandler be GetMethod(target, @@hasInstance).
1754     JSHandle<JSTaggedValue> instOfHandler = GetMethod(thread, target, vm->GetGlobalEnv()->GetHasInstanceSymbol());
1755 
1756     // 3. ReturnIfAbrupt(instOfHandler).
1757     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1758 
1759     // 4. If instOfHandler is not undefined, then
1760     if (!instOfHandler->IsUndefined()) {
1761         // a. Return ! ToBoolean(? Call(instOfHandler, target, «object»)).
1762         JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1763         EcmaRuntimeCallInfo *info =
1764             EcmaInterpreter::NewRuntimeCallInfo(thread, instOfHandler, target, undefined, 1);
1765         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1766         info->SetCallArg(object.GetTaggedValue());
1767         JSTaggedValue tagged = JSFunction::Call(info);
1768         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
1769         return tagged.ToBoolean();
1770     }
1771 
1772     // 5. If IsCallable(target) is false, throw a TypeError exception.
1773     if (!target->IsCallable()) {
1774         THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", false);
1775     }
1776 
1777     // 6. Return ? OrdinaryHasInstance(target, object).
1778     return JSFunction::OrdinaryHasInstance(thread, target, object);
1779 }
1780 
1781 // ecma6.0 6.2.4.4
FromPropertyDescriptor(JSThread * thread,const PropertyDescriptor & desc)1782 JSHandle<JSTaggedValue> JSObject::FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc)
1783 {
1784     // 1. If Desc is undefined, return undefined
1785     if (desc.IsEmpty()) {
1786         return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined());
1787     }
1788 
1789     // 2. Let obj be ObjectCreate(%ObjectPrototype%).
1790     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1791     JSHandle<JSFunction> objFunc(env->GetObjectFunction());
1792     JSHandle<JSObject> objHandle = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(objFunc);
1793 
1794     auto globalConst = thread->GlobalConstants();
1795     // 4. If Desc has a [[Value]] field, then Perform CreateDataProperty(obj, "value", Desc.[[Value]]).
1796     if (desc.HasValue()) {
1797         JSHandle<JSTaggedValue> valueStr = globalConst->GetHandledValueString();
1798         bool success = CreateDataProperty(thread, objHandle, valueStr, desc.GetValue());
1799         RASSERT_PRINT(success, "CreateDataProperty must be success");
1800     }
1801     // 5. If Desc has a [[Writable]] field, then Perform CreateDataProperty(obj, "writable", Desc.[[Writable]]).
1802     if (desc.HasWritable()) {
1803         JSHandle<JSTaggedValue> writableStr = globalConst->GetHandledWritableString();
1804         JSHandle<JSTaggedValue> writable(thread, JSTaggedValue(desc.IsWritable()));
1805         [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, writableStr, writable);
1806         ASSERT_PRINT(success, "CreateDataProperty must be success");
1807     }
1808     // 6. If Desc has a [[Get]] field, then Perform CreateDataProperty(obj, "get", Desc.[[Get]]).
1809     if (desc.HasGetter()) {
1810         JSHandle<JSTaggedValue> getStr = globalConst->GetHandledGetString();
1811         bool success = CreateDataProperty(thread, objHandle, getStr, desc.GetGetter());
1812         RASSERT_PRINT(success, "CreateDataProperty must be success");
1813     }
1814     // 7. If Desc has a [[Set]] field, then Perform CreateDataProperty(obj, "set", Desc.[[Set]])
1815     if (desc.HasSetter()) {
1816         JSHandle<JSTaggedValue> setStr = globalConst->GetHandledSetString();
1817         bool success = CreateDataProperty(thread, objHandle, setStr, desc.GetSetter());
1818         RASSERT_PRINT(success, "CreateDataProperty must be success");
1819     }
1820     // 8. If Desc has an [[Enumerable]] field, then Perform CreateDataProperty(obj, "enumerable",
1821     // Desc.[[Enumerable]]).
1822     if (desc.HasEnumerable()) {
1823         JSHandle<JSTaggedValue> enumerableStr = globalConst->GetHandledEnumerableString();
1824         JSHandle<JSTaggedValue> enumerable(thread, JSTaggedValue(desc.IsEnumerable()));
1825         [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, enumerableStr, enumerable);
1826         ASSERT_PRINT(success, "CreateDataProperty must be success");
1827     }
1828     // 9. If Desc has a [[Configurable]] field, then Perform CreateDataProperty(obj , "configurable",
1829     // Desc.[[Configurable]]).
1830     if (desc.HasConfigurable()) {
1831         JSHandle<JSTaggedValue> configurableStr = globalConst->GetHandledConfigurableString();
1832         JSHandle<JSTaggedValue> configurable(thread, JSTaggedValue(desc.IsConfigurable()));
1833         [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, configurableStr, configurable);
1834         ASSERT_PRINT(success, "CreateDataProperty must be success");
1835     }
1836     return JSHandle<JSTaggedValue>(objHandle);
1837 }
1838 
ToPropertyDescriptorFast(JSThread * thread,const JSHandle<JSTaggedValue> & obj,PropertyDescriptor & desc)1839 bool JSObject::ToPropertyDescriptorFast(JSThread *thread, const JSHandle<JSTaggedValue> &obj, PropertyDescriptor &desc)
1840 {
1841     auto *hclass = obj->GetTaggedObject()->GetClass();
1842     JSType jsType = hclass->GetObjectType();
1843     if (jsType != JSType::JS_OBJECT) {
1844         return false;
1845     }
1846     if (hclass->IsDictionaryMode()) {
1847         return false;
1848     }
1849     auto env = thread->GetEcmaVM()->GetGlobalEnv();
1850     auto globalConst = thread->GlobalConstants();
1851     if (hclass->GetPrototype() != env->GetObjectFunctionPrototype().GetTaggedValue()) {
1852         return false;
1853     }
1854     if (JSObject::Cast(hclass->GetPrototype().GetTaggedObject())->GetClass() !=
1855         env->GetObjectFunctionPrototypeClass().GetObject<JSHClass>()) {
1856         return false;
1857     }
1858     LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
1859     uint32_t propsNumber = hclass->NumberOfProps();
1860     for (uint32_t i = 0; i < propsNumber; i++) {
1861         auto attr = layoutInfo->GetAttr(i);
1862         if (attr.IsAccessor()) {
1863             return false;
1864         }
1865         auto key = layoutInfo->GetKey(i);
1866         auto value = JSObject::Cast(obj->GetTaggedObject())->GetProperty(hclass, attr);
1867         if (key == globalConst->GetEnumerableString()) {
1868             bool enumerable = value.ToBoolean();
1869             desc.SetEnumerable(enumerable);
1870         } else if (key == globalConst->GetConfigurableString()) {
1871             bool configurable = value.ToBoolean();
1872             desc.SetConfigurable(configurable);
1873         } else if (key == globalConst->GetValueString()) {
1874             auto handleValue = JSHandle<JSTaggedValue>(thread, value);
1875             desc.SetValue(handleValue);
1876         } else if (key == globalConst->GetWritableString()) {
1877             bool writable = value.ToBoolean();
1878             desc.SetWritable(writable);
1879         } else if (key == globalConst->GetGetString()) {
1880             if (!value.IsCallable()) {
1881                 return false;
1882             }
1883             auto getter = JSHandle<JSTaggedValue>(thread, value);
1884             desc.SetGetter(getter);
1885         } else if (key == globalConst->GetSetString()) {
1886             if (!value.IsCallable()) {
1887                 return false;
1888             }
1889             auto setter = JSHandle<JSTaggedValue>(thread, value);
1890             desc.SetSetter(setter);
1891         }
1892     }
1893 
1894     if (desc.IsAccessorDescriptor()) {
1895         // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception.
1896         if (desc.HasValue() || desc.HasWritable()) {
1897             THROW_TYPE_ERROR_AND_RETURN(thread, "either Value or Writable is present", true);
1898         }
1899     }
1900     return true;
1901 }
1902 
1903 // ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj )
ToPropertyDescriptor(JSThread * thread,const JSHandle<JSTaggedValue> & obj,PropertyDescriptor & desc)1904 void JSObject::ToPropertyDescriptor(JSThread *thread, const JSHandle<JSTaggedValue> &obj, PropertyDescriptor &desc)
1905 {
1906     if (!obj->IsECMAObject()) {
1907         // 2. If Type(Obj) is not Object, throw a TypeError exception.
1908         THROW_TYPE_ERROR(thread, "ToPropertyDescriptor error obj is not Object");
1909     }
1910 
1911     if (ToPropertyDescriptorFast(thread, obj, desc)) {
1912         return;
1913     }
1914     auto globalConst = thread->GlobalConstants();
1915     // 3. Let desc be a new Property Descriptor that initially has no fields.
1916     // 4. Let hasEnumerable be HasProperty(Obj, "enumerable")
1917     {
1918         ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetEnumerableString());
1919         if (op.IsFound()) {
1920             auto value = op.FastGetValue();
1921             bool enumerable = value->IsException() ? false : value->ToBoolean();
1922             desc.SetEnumerable(enumerable);
1923         }
1924     }
1925     // 7. Let hasConfigurable be HasProperty(Obj, "configurable").
1926     {
1927         ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetConfigurableString());
1928         if (op.IsFound()) {
1929             auto value = op.FastGetValue();
1930             bool conf = value->IsException() ? false : value->ToBoolean();
1931             desc.SetConfigurable(conf);
1932         }
1933     }
1934     // 10. Let hasValue be HasProperty(Obj, "value").
1935     {
1936         ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetValueString());
1937         if (op.IsFound()) {
1938             JSHandle<JSTaggedValue> prop = op.FastGetValue();
1939             desc.SetValue(prop);
1940         }
1941     }
1942     // 13. Let hasWritable be HasProperty(Obj, "writable").
1943     {
1944         ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetWritableString());
1945         if (op.IsFound()) {
1946             auto value = op.FastGetValue();
1947             bool writable = value->IsException() ? false : value->ToBoolean();
1948             desc.SetWritable(writable);
1949         }
1950     }
1951     // 16. Let hasGet be HasProperty(Obj, "get").
1952     {
1953         ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetGetString());
1954         if (op.IsFound()) {
1955             JSHandle<JSTaggedValue> getter = op.FastGetValue();
1956             if (!getter->IsCallable() && !getter->IsUndefined()) {
1957                 THROW_TYPE_ERROR(thread, "getter not callable or undefined");
1958             }
1959             desc.SetGetter(getter);
1960         }
1961     }
1962 
1963     // 19. Let hasSet be HasProperty(Obj, "set").
1964     {
1965         ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetSetString());
1966         if (op.IsFound()) {
1967             JSHandle<JSTaggedValue> setter = op.FastGetValue();
1968             if (!setter->IsCallable() && !setter->IsUndefined()) {
1969                 THROW_TYPE_ERROR(thread, "setter not callable or undefined");
1970             }
1971             desc.SetSetter(setter);
1972         }
1973     }
1974 
1975     // 22. If either desc.[[Get]] or desc.[[Set]] is present, then
1976     if (desc.IsAccessorDescriptor()) {
1977         // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception.
1978         if (desc.HasValue() || desc.HasWritable()) {
1979             THROW_TYPE_ERROR(thread, "either desc.[[Value]] or desc.[[Writable]] is present");
1980         }
1981     }
1982     // 23. Return desc.
1983 }
1984 
SpeciesConstructor(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & defaultConstructort)1985 JSHandle<JSTaggedValue> JSObject::SpeciesConstructor(JSThread *thread, const JSHandle<JSObject> &obj,
1986                                                      const JSHandle<JSTaggedValue> &defaultConstructort)
1987 {
1988     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1989     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1990     // Assert: Type(O) is Object.
1991     ASSERT_PRINT(obj->IsECMAObject(), "obj must be js object");
1992 
1993     // Let C be Get(O, "constructor").
1994     JSHandle<JSTaggedValue> contructorKey = globalConst->GetHandledConstructorString();
1995     JSHandle<JSTaggedValue> objConstructor(JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(obj),
1996         contructorKey).GetValue());
1997     // ReturnIfAbrupt(C).
1998     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
1999     // If C is undefined, return defaultConstructor.
2000     if (objConstructor->IsUndefined()) {
2001         return defaultConstructort;
2002     }
2003     // If Type(C) is not Object, throw a TypeError exception.
2004     if (!objConstructor->IsECMAObject()) {
2005         THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is not Object",
2006                                     JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
2007     }
2008     // Let S be Get(C, @@species).
2009     JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
2010     JSHandle<JSTaggedValue> speciesConstructor(GetProperty(thread, objConstructor, speciesSymbol).GetValue());
2011     // ReturnIfAbrupt(S).
2012     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
2013     // If S is either undefined or null, return defaultConstructor.
2014     if (speciesConstructor->IsUndefined() || speciesConstructor->IsNull()) {
2015         return defaultConstructort;
2016     }
2017     // If IsConstructor(S) is true, return S.
2018     if (speciesConstructor->IsConstructor()) {
2019         return speciesConstructor;
2020     }
2021     // Throw a TypeError exception.
2022     THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Constructor",
2023                                 JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
2024     return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception());
2025 }
2026 
2027 // 6.2.4.6 CompletePropertyDescriptor ( Desc )
CompletePropertyDescriptor(const JSThread * thread,PropertyDescriptor & desc)2028 void PropertyDescriptor::CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc)
2029 {
2030     // 1. ReturnIfAbrupt(Desc).
2031     // 2. Assert: Desc is a Property Descriptor
2032     // 3. Let like be Record{[[Value]]: undefined, [[Writable]]: false, [[Get]]: undefined, [[Set]]: undefined,
2033     // [[Enumerable]]: false, [[Configurable]]: false}.
2034     // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then
2035     if (!desc.IsAccessorDescriptor()) {
2036         // a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to like.[[Value]].
2037         // b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] to like.[[Writable]].
2038         if (!desc.HasValue()) {
2039             desc.SetValue(JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
2040         }
2041         if (!desc.HasWritable()) {
2042             desc.SetWritable(false);
2043         }
2044     } else {
2045         // a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to like.[[Get]].
2046         // b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to like.[[Set]].
2047         // Default value of Get and Set is undefined.
2048     }
2049     // 6. If Desc does not have an [[Enumerable]] field, set Desc.[[Enumerable]] to like.[[Enumerable]].
2050     // 7. If Desc does not have a [[Configurable]] field, set Desc.[[Configurable]] to like.[[Configurable]].
2051     if (!desc.HasEnumerable()) {
2052         desc.SetEnumerable(false);
2053     }
2054     if (!desc.HasConfigurable()) {
2055         desc.SetConfigurable(false);
2056     }
2057 }
2058 
2059 // 13.7.5.15 EnumerateObjectProperties ( O )
EnumerateObjectProperties(JSThread * thread,const JSHandle<JSTaggedValue> & obj)2060 JSHandle<JSForInIterator> JSObject::EnumerateObjectProperties(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
2061 {
2062     // 1. Return an Iterator object (25.1.1.2) whose next method iterates over all the String-valued keys of
2063     // enumerable properties of O. The Iterator object must inherit from %IteratorPrototype% (25.1.2). The
2064     // mechanics and order of enumerating the properties is not specified but must conform to the rules specified
2065     // below.
2066     JSHandle<JSTaggedValue> object;
2067     if (obj->IsString()) {
2068         JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
2069         object = JSHandle<JSTaggedValue>::Cast(JSPrimitiveRef::StringCreate(thread, obj, undefined));
2070     } else {
2071         object = JSTaggedValue::ToPrototypeOrObj(thread, obj);
2072     }
2073 
2074     return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object);
2075 }
2076 
DefinePropertyByLiteral(JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,bool useForClass)2077 void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle<JSObject> &obj,
2078                                        const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
2079                                        bool useForClass)
2080 {
2081     ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
2082     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
2083     PropertyAttributes attr = useForClass ? PropertyAttributes::Default(true, false, true)
2084                                           : PropertyAttributes::Default();
2085 
2086     if (value->IsAccessorData()) {
2087         attr.SetIsAccessor(true);
2088     }
2089 
2090     uint32_t index = 0;
2091     if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) {
2092         AddElementInternal(thread, obj, index, value, attr);
2093         return;
2094     }
2095     LOG_ECMA(FATAL) << "this branch is unreachable";
2096     UNREACHABLE();
2097 }
2098 
DefineSetter(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)2099 void JSObject::DefineSetter(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
2100                             const JSHandle<JSTaggedValue> &value)
2101 {
2102     ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
2103     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
2104     ObjectOperator op(thread, obj, key, OperatorType::OWN);
2105     ASSERT(op.IsFound());
2106     op.DefineSetter(value);
2107 }
2108 
DefineGetter(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)2109 void JSObject::DefineGetter(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
2110                             const JSHandle<JSTaggedValue> &value)
2111 {
2112     ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
2113     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
2114     ObjectOperator op(thread, obj, key, OperatorType::OWN);
2115     ASSERT(op.IsFound());
2116     op.DefineGetter(value);
2117 }
2118 
CreateObjectFromProperties(const JSThread * thread,const JSHandle<TaggedArray> & properties,JSTaggedValue ihcVal)2119 JSHandle<JSObject> JSObject::CreateObjectFromProperties(const JSThread *thread, const JSHandle<TaggedArray> &properties,
2120                                                         JSTaggedValue ihcVal)
2121 {
2122     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2123     size_t length = properties->GetLength();
2124     uint32_t propsLen = 0;
2125     for (size_t i = 0; i < length; i += 2) {  // 2: skip a pair of key and value
2126         if (properties->Get(i).IsHole()) {
2127             break;
2128         }
2129         propsLen++;
2130     }
2131     if (propsLen <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES) {
2132         JSHandle<JSObject> obj = factory->NewOldSpaceObjLiteralByHClass(properties, propsLen);
2133         ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object");
2134         if (ihcVal.IsJSHClass()) {
2135             bool isSuccess = true;
2136             JSHClass *ihc = JSHClass::Cast(ihcVal.GetTaggedObject());
2137             JSHClass *oldHC = obj->GetJSHClass();
2138             ihc->SetPrototype(thread, oldHC->GetPrototype());
2139             obj->SetClass(ihc);
2140             for (size_t i = 0; i < propsLen; i++) {
2141                 auto value = obj->ConvertValueWithRep(i, properties->Get(i * 2 + 1));
2142                 if (!value.first) {
2143                     isSuccess = false;
2144                     break;
2145                 }
2146                 obj->SetPropertyInlinedPropsWithRep(thread, i, value.second);
2147             }
2148             if (isSuccess) {
2149                 return obj;
2150             }
2151             // The layout representation of ihc is inaccurate and needs to be rolled back to the old HClass
2152             obj->SetClass(oldHC);
2153         }
2154         for (size_t i = 0; i < propsLen; i++) {
2155             // 2: literal contains a pair of key-value
2156             obj->SetPropertyInlinedProps(thread, i, properties->Get(i * 2 + 1));
2157         }
2158         return obj;
2159     } else {
2160         JSHandle<JSObject> obj = factory->NewEmptyJSObject();
2161         JSHClass::TransitionToDictionary(thread, obj);
2162 
2163         JSMutableHandle<NameDictionary> dict(
2164             thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propsLen)));
2165         JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
2166         JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
2167         for (size_t i = 0; i < propsLen; i++) {
2168             PropertyAttributes attr = PropertyAttributes::Default();
2169             // 2: literal contains a pair of key-value
2170             valueHandle.Update(properties->Get(i * 2 + 1));
2171             // 2: literal contains a pair of key-value
2172             keyHandle.Update(properties->Get(i * 2));
2173             JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr);
2174             dict.Update(newDict);
2175         }
2176         obj->SetProperties(thread, dict);
2177         return obj;
2178     }
2179 }
2180 
AddAccessor(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<AccessorData> & value,PropertyAttributes attr)2181 void JSObject::AddAccessor(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
2182                            const JSHandle<AccessorData> &value, PropertyAttributes attr)
2183 {
2184     ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object");
2185     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
2186     ASSERT_PRINT(attr.IsAccessor(), "Attr is not AccessorData");
2187     ObjectOperator op(thread, obj, key, OperatorType::OWN);
2188     ASSERT(!op.IsFound());
2189     op.AddProperty(JSHandle<JSObject>::Cast(obj), JSHandle<JSTaggedValue>(value), attr);
2190 }
2191 
UpdatePropertyInDictionary(const JSThread * thread,JSTaggedValue key,JSTaggedValue value)2192 bool JSObject::UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value)
2193 {
2194     [[maybe_unused]] DisallowGarbageCollection noGc;
2195     NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject());
2196     int entry = dict->FindEntry(key);
2197     if (entry == -1) {
2198         return false;
2199     }
2200     dict->UpdateValue(thread, entry, value);
2201     return true;
2202 }
2203 
2204 // The hash field may be a hash value, FunctionExtraInfo(JSNativePointer) or TaggedArray
SetHash(int32_t hash)2205 void ECMAObject::SetHash(int32_t hash)
2206 {
2207     JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2208     JSTaggedValue value(hashField);
2209     if (value.IsHeapObject()) {
2210         JSThread *thread = this->GetJSThread();
2211         // Hash position reserve in advance.
2212         if (value.IsTaggedArray()) {
2213             TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject());
2214             array->Set(thread, array->GetExtraLength() + HASH_INDEX, JSTaggedValue(hash));
2215         } else if (value.IsNativePointer()) { // FunctionExtraInfo
2216             JSHandle<TaggedArray> newArray =
2217                 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(RESOLVED_MAX_SIZE);
2218             newArray->SetExtraLength(0);
2219             newArray->Set(thread, HASH_INDEX, JSTaggedValue(hash));
2220             newArray->Set(thread, FUNCTION_EXTRA_INDEX, value);
2221             Barriers::SetObject<true>(thread, this, HASH_OFFSET, newArray.GetTaggedValue().GetRawData());
2222         } else {
2223             LOG_ECMA(FATAL) << "this branch is unreachable";
2224             UNREACHABLE();
2225         }
2226     } else {
2227         Barriers::SetPrimitive<JSTaggedType>(this, HASH_OFFSET, JSTaggedValue(hash).GetRawData());
2228     }
2229 }
2230 
GetHash() const2231 int32_t ECMAObject::GetHash() const
2232 {
2233     JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2234     JSTaggedValue value(hashField);
2235     if (value.IsHeapObject()) {
2236         if (value.IsTaggedArray()) {
2237             TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject());
2238             return array->Get(array->GetExtraLength() + HASH_INDEX).GetInt();
2239         } else {
2240             // Default is 0
2241             return 0;
2242         }
2243     }
2244     JSThread *thread = this->GetJSThread();
2245     JSHandle<JSTaggedValue> valueHandle(thread, value);
2246     return JSTaggedValue::ToInt32(thread, valueHandle);
2247 }
2248 
HasHash() const2249 bool ECMAObject::HasHash() const
2250 {
2251     JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2252     JSTaggedValue value(hashField);
2253     if (value.IsInt() && value.GetInt() == 0) {
2254         return false;
2255     }
2256     return true;
2257 }
2258 
GetNativePointerField(int32_t index) const2259 void *ECMAObject::GetNativePointerField(int32_t index) const
2260 {
2261     JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2262     JSTaggedValue value(hashField);
2263     if (value.IsTaggedArray()) {
2264         JSThread *thread = this->GetJSThread();
2265         JSHandle<TaggedArray> array(thread, value);
2266         if (static_cast<int32_t>(array->GetExtraLength()) > index) {
2267             JSHandle<JSNativePointer> pointer(thread, array->Get(index));
2268             return pointer->GetExternalPointer();
2269         }
2270     }
2271     return nullptr;
2272 }
2273 
SetNativePointerField(int32_t index,void * nativePointer,const DeleteEntryPoint & callBack,void * data,size_t nativeBindingsize)2274 void ECMAObject::SetNativePointerField(int32_t index, void *nativePointer,
2275     const DeleteEntryPoint &callBack, void *data, size_t nativeBindingsize)
2276 {
2277     JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2278     JSTaggedValue value(hashField);
2279     if (value.IsTaggedArray()) {
2280         JSThread *thread = this->GetJSThread();
2281         JSHandle<TaggedArray> array(thread, value);
2282         if (static_cast<int32_t>(array->GetExtraLength()) > index) {
2283             EcmaVM *vm = thread->GetEcmaVM();
2284             JSHandle<JSTaggedValue> current = JSHandle<JSTaggedValue>(thread, array->Get(thread, index));
2285             if (!current->IsHole() && nativePointer == nullptr) {
2286                 // Try to remove native pointer if exists.
2287                 vm->RemoveFromNativePointerList(*JSHandle<JSNativePointer>(current));
2288                 array->Set(thread, index, JSTaggedValue::Hole());
2289             } else {
2290                 JSHandle<JSNativePointer> pointer = vm->GetFactory()->NewJSNativePointer(
2291                     nativePointer, callBack, data, false, nativeBindingsize);
2292                 array->Set(thread, index, pointer.GetTaggedValue());
2293             }
2294         }
2295     }
2296 }
2297 
GetNativePointerFieldCount() const2298 int32_t ECMAObject::GetNativePointerFieldCount() const
2299 {
2300     int32_t len = 0;
2301     JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2302     JSTaggedValue value(hashField);
2303     if (value.IsTaggedArray()) {
2304         TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject());
2305         len = static_cast<int32_t>(array->GetExtraLength());
2306     }
2307     return len;
2308 }
2309 
SetNativePointerFieldCount(int32_t count)2310 void ECMAObject::SetNativePointerFieldCount(int32_t count)
2311 {
2312     if (count == 0) {
2313         return;
2314     }
2315     JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
2316     JSThread *thread = this->GetJSThread();
2317     JSHandle<JSTaggedValue> value(thread, JSTaggedValue(hashField));
2318     JSHandle<ECMAObject> obj(thread, this);
2319     if (value->IsHeapObject()) {
2320         if (value->IsTaggedArray()) {
2321             JSHandle<TaggedArray> array(value);
2322             // Native Pointer field count is fixed.
2323             if (array->GetExtraLength() == 0) {
2324                 JSHandle<TaggedArray> newArray =
2325                     thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + RESOLVED_MAX_SIZE);
2326                 newArray->SetExtraLength(count);
2327                 newArray->Set(thread, count + HASH_INDEX, array->Get(HASH_INDEX));
2328                 newArray->Set(thread, count + FUNCTION_EXTRA_INDEX, array->Get(FUNCTION_EXTRA_INDEX));
2329                 Barriers::SetObject<true>(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData());
2330             }
2331         } else if (value->IsJSNativePointer()) {
2332             JSHandle<TaggedArray> newArray =
2333                 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + RESOLVED_MAX_SIZE);
2334             newArray->SetExtraLength(count);
2335             newArray->Set(thread, count + HASH_INDEX, JSTaggedValue(0));
2336             newArray->Set(thread, count + FUNCTION_EXTRA_INDEX, value);
2337             Barriers::SetObject<true>(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData());
2338         } else {
2339             LOG_ECMA(FATAL) << "this branch is unreachable";
2340             UNREACHABLE();
2341         }
2342     } else {
2343         JSHandle<TaggedArray> newArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + 1);
2344         newArray->SetExtraLength(count);
2345         newArray->Set(thread, count + HASH_INDEX, value);
2346         Barriers::SetObject<true>(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData());
2347     }
2348 }
2349 }  // namespace panda::ecmascript
2350