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