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