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