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