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