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