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