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