1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/object_operator.h"
17
18 #include "ecmascript/accessor_data.h"
19 #include "ecmascript/ecma_vm.h"
20 #include "ecmascript/global_dictionary-inl.h"
21 #include "ecmascript/global_dictionary.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/ic/property_box.h"
24 #include "ecmascript/js_array.h"
25 #include "ecmascript/js_function.h"
26 #include "ecmascript/js_hclass-inl.h"
27 #include "ecmascript/js_object-inl.h"
28 #include "ecmascript/js_primitive_ref.h"
29 #include "ecmascript/layout_info.h"
30 #include "ecmascript/mem/c_string.h"
31 #include "ecmascript/object_factory.h"
32 #include "ecmascript/object_fast_operator-inl.h"
33 #include "ecmascript/property_attributes.h"
34 #include "ecmascript/tagged_dictionary.h"
35
36 namespace panda::ecmascript {
HandleKey(const JSHandle<JSTaggedValue> & key)37 void ObjectOperator::HandleKey(const JSHandle<JSTaggedValue> &key)
38 {
39 if (key->IsInt()) {
40 int32_t keyInt = key->GetInt();
41 if (keyInt >= 0) {
42 elementIndex_ = static_cast<uint32_t>(keyInt);
43 return;
44 }
45 key_ = JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread_, JSTaggedValue(keyInt)));
46 return;
47 }
48
49 if (key->IsString()) {
50 uint32_t index = 0;
51 if (JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index)) {
52 ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
53 elementIndex_ = index;
54 return;
55 }
56 if (EcmaStringAccessor(key->GetTaggedObject()).IsInternString()) {
57 key_ = key;
58 return;
59 }
60 key_ = JSHandle<JSTaggedValue>(thread_, thread_->GetEcmaVM()->GetFactory()->InternString(key));
61 return;
62 }
63
64 if (key->IsDouble()) {
65 double number = key->GetDouble();
66 if (number >= 0 && number < JSObject::MAX_ELEMENT_INDEX) {
67 auto integer = static_cast<uint32_t>(number);
68 if (integer == number) {
69 elementIndex_ = static_cast<uint32_t>(number);
70 return;
71 }
72 }
73 key_ = JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread_, key.GetTaggedValue()));
74 return;
75 }
76
77 if (key->IsSymbol()) {
78 key_ = key;
79 return;
80 }
81
82 JSHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::ToPrimitive(thread_, key, PREFER_STRING));
83 if (key->IsSymbol()) {
84 key_ = keyHandle;
85 return;
86 }
87 key_ = JSHandle<JSTaggedValue>(thread_,
88 thread_->GetEcmaVM()->GetFactory()->InternString(
89 JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread_, keyHandle))));
90 }
91
UpdateHolder()92 void ObjectOperator::UpdateHolder()
93 {
94 if (holder_->IsString() &&
95 (IsElement() && elementIndex_ < EcmaStringAccessor(holder_->GetTaggedObject()).GetLength())) {
96 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
97 holder_.Update(JSPrimitiveRef::StringCreate(thread_, holder_, undefined).GetTaggedValue());
98 } else {
99 holder_.Update(JSTaggedValue::ToPrototypeOrObj(thread_, holder_).GetTaggedValue());
100 }
101 }
102
UpdateIsTSHClass()103 void ObjectOperator::UpdateIsTSHClass()
104 {
105 if (!holder_->IsECMAObject()) {
106 SetIsTSHClass(false);
107 return;
108 }
109 auto hclass = JSHandle<JSObject>::Cast(holder_)->GetClass();
110 if (hclass->IsTS()) {
111 SetIsTSHClass(true);
112 }
113 }
114
StartLookUp(OperatorType type)115 void ObjectOperator::StartLookUp(OperatorType type)
116 {
117 UpdateHolder();
118
119 if (type == OperatorType::OWN) {
120 LookupPropertyInHolder();
121 } else {
122 LookupProperty();
123 }
124 }
125
StartGlobalLookUp(OperatorType type)126 void ObjectOperator::StartGlobalLookUp(OperatorType type)
127 {
128 UpdateHolder();
129
130 if (type == OperatorType::OWN) {
131 GlobalLookupPropertyInHolder();
132 } else {
133 GlobalLookupProperty();
134 }
135 }
136
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & key,OperatorType type)137 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &key, OperatorType type)
138 : thread_(thread),
139 holder_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()),
140 receiver_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject())
141 {
142 HandleKey(key);
143 StartGlobalLookUp(type);
144 }
145
ObjectOperator(JSThread * thread,const JSHandle<JSObject> & holder,const JSHandle<JSTaggedValue> & key,OperatorType type)146 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSObject> &holder, const JSHandle<JSTaggedValue> &key,
147 OperatorType type)
148 : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue())
149 {
150 HandleKey(key);
151 StartLookUp(type);
152 }
153
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,const JSHandle<JSTaggedValue> & key,OperatorType type)154 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
155 const JSHandle<JSTaggedValue> &key, OperatorType type)
156 : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue())
157 {
158 HandleKey(key);
159 StartLookUp(type);
160 }
161
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,uint32_t index,OperatorType type)162 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, uint32_t index,
163 OperatorType type)
164 : thread_(thread),
165 holder_(thread, holder.GetTaggedValue()),
166 receiver_(thread, holder.GetTaggedValue()),
167 elementIndex_(index)
168 {
169 StartLookUp(type);
170 }
171
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,const JSHandle<JSTaggedValue> & receiver,const JSHandle<JSTaggedValue> & key,OperatorType type)172 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
173 const JSHandle<JSTaggedValue> &receiver, const JSHandle<JSTaggedValue> &key,
174 OperatorType type)
175 : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, receiver.GetTaggedValue())
176 {
177 SetHasReceiver(true);
178 HandleKey(key);
179 StartLookUp(type);
180 }
181
182 // op for fast path
ObjectOperator(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,OperatorType type)183 ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
184 OperatorType type)
185 : thread_(thread), holder_(thread, receiver), receiver_(thread, receiver), key_(thread, name)
186 {
187 ASSERT(name.IsStringOrSymbol());
188 StartLookUp(type);
189 }
FastGetValue()190 JSHandle<JSTaggedValue> ObjectOperator::FastGetValue()
191 {
192 ASSERT(IsFound() && !value_.IsEmpty());
193 if (value_->IsPropertyBox()) {
194 value_.Update(PropertyBox::Cast(value_->GetTaggedObject())->GetValue());
195 }
196 if (!IsAccessorDescriptor()) {
197 return value_;
198 }
199 AccessorData *accessor = AccessorData::Cast(value_->GetTaggedObject());
200 ASSERT(!accessor->IsInternal());
201 // 8. Return Call(getter, Receiver).
202 return JSHandle<JSTaggedValue>(thread_, JSObject::CallGetter(thread_, accessor, receiver_));
203 }
ObjectOperator(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,const PropertyAttributes & attr)204 ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
205 const PropertyAttributes &attr)
206 : thread_(thread), receiver_(thread, receiver), key_(thread, name)
207 {
208 SetAttr(attr);
209 }
FastAdd(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,const JSHandle<JSTaggedValue> & value,const PropertyAttributes & attr)210 void ObjectOperator::FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
211 const JSHandle<JSTaggedValue> &value, const PropertyAttributes &attr)
212 {
213 ObjectOperator op(thread, receiver, name, attr);
214 op.AddPropertyInternal(value);
215 }
216
ToPropertyDescriptor(PropertyDescriptor & desc) const217 void ObjectOperator::ToPropertyDescriptor(PropertyDescriptor &desc) const
218 {
219 DISALLOW_GARBAGE_COLLECTION;
220 if (!IsFound()) {
221 return;
222 }
223
224 if (!IsAccessorDescriptor()) {
225 desc.SetWritable(IsWritable());
226 JSTaggedValue val = GetValue();
227 desc.SetValue(JSHandle<JSTaggedValue>(thread_, val));
228 } else {
229 auto result = GetValue();
230 bool isPropertyBox = result.IsPropertyBox();
231 if (isPropertyBox) {
232 result = PropertyBox::Cast(result.GetTaggedObject())->GetValue();
233 }
234 AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject());
235
236 if (UNLIKELY(accessor->IsInternal())) {
237 desc.SetWritable(IsWritable());
238 auto val = accessor->CallInternalGet(thread_, JSHandle<JSObject>::Cast(GetHolder()));
239 JSMutableHandle<JSTaggedValue> value(thread_, val);
240 if (isPropertyBox) {
241 JSHandle<PropertyBox> cell(value_);
242 cell->SetValue(thread_, val);
243 value.Update(cell);
244 }
245 desc.SetValue(value);
246 } else {
247 desc.SetGetter(JSHandle<JSTaggedValue>(thread_, accessor->GetGetter()));
248 desc.SetSetter(JSHandle<JSTaggedValue>(thread_, accessor->GetSetter()));
249 }
250 }
251
252 desc.SetEnumerable(IsEnumerable());
253 desc.SetConfigurable(IsConfigurable());
254 }
255
GlobalLookupProperty()256 void ObjectOperator::GlobalLookupProperty()
257 {
258 GlobalLookupPropertyInHolder();
259 if (IsFound()) {
260 return;
261 }
262 JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_);
263 RETURN_IF_ABRUPT_COMPLETION(thread_);
264 if (!proto.IsHeapObject()) {
265 return;
266 }
267 holder_.Update(proto);
268 if (holder_->IsJSProxy()) {
269 return;
270 }
271 SetIsOnPrototype(true);
272 LookupProperty();
273 }
274
LookupProperty()275 void ObjectOperator::LookupProperty()
276 {
277 while (true) {
278 UpdateIsTSHClass();
279 LookupPropertyInHolder();
280 if (IsFound()) {
281 return;
282 }
283
284 JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_);
285 RETURN_IF_ABRUPT_COMPLETION(thread_);
286 if (!proto.IsHeapObject()) {
287 return;
288 }
289
290 holder_.Update(proto);
291 if (holder_->IsJSProxy()) {
292 return;
293 }
294
295 SetIsOnPrototype(true);
296 }
297 }
298
LookupGlobal(const JSHandle<JSObject> & obj)299 void ObjectOperator::LookupGlobal(const JSHandle<JSObject> &obj)
300 {
301 ASSERT(obj->IsJSGlobalObject());
302 if (IsElement()) {
303 LookupElementInlinedProps(obj);
304 return;
305 }
306 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
307 if (array->GetLength() == 0) {
308 return;
309 }
310 GlobalDictionary *dict = GlobalDictionary::Cast(array);
311 int entry = dict->FindEntry(key_.GetTaggedValue());
312 if (entry == -1) {
313 return;
314 }
315 JSTaggedValue value(dict->GetBox(entry));
316 uint32_t attr = dict->GetAttributes(entry).GetValue();
317 SetFound(entry, value, attr, true);
318 }
319
LookupPropertyInlinedProps(const JSHandle<JSObject> & obj)320 void ObjectOperator::LookupPropertyInlinedProps(const JSHandle<JSObject> &obj)
321 {
322 if (IsElement()) {
323 LookupElementInlinedProps(obj);
324 return;
325 }
326
327 if (!obj.GetTaggedValue().IsJSObject()) {
328 return;
329 }
330
331 if (obj->IsJSGlobalObject()) {
332 DISALLOW_GARBAGE_COLLECTION;
333 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
334 if (array->GetLength() == 0) {
335 return;
336 }
337
338 GlobalDictionary *dict = GlobalDictionary::Cast(array);
339 int entry = dict->FindEntry(key_.GetTaggedValue());
340 if (entry == -1) {
341 return;
342 }
343
344 JSTaggedValue value(dict->GetBox(entry));
345 uint32_t attr = dict->GetAttributes(entry).GetValue();
346 SetFound(entry, value, attr, true);
347 return;
348 }
349
350 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
351 if (!array->IsDictionaryMode()) {
352 JSHClass *jshclass = obj->GetJSHClass();
353 int entry = JSHClass::FindPropertyEntry(thread_, jshclass, key_.GetTaggedValue());
354 if (entry == -1) {
355 return;
356 }
357 JSTaggedValue attrs = jshclass->GetLayout();
358 LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject());
359 PropertyAttributes attr(layoutInfo->GetAttr(entry));
360 ASSERT(entry == static_cast<int>(attr.GetOffset()));
361 JSTaggedValue value;
362 if (attr.IsInlinedProps()) {
363 value = obj->GetPropertyInlinedPropsWithRep(entry, attr);
364 if (value.IsHole()) {
365 if (receiverHoleEntry_ == -1 && receiver_ == holder_) {
366 receiverHoleEntry_ = entry;
367 }
368 return;
369 }
370 } else {
371 entry -= static_cast<int>(jshclass->GetInlinedProperties());
372 value = array->Get(entry);
373 }
374
375 SetFound(entry, value, attr.GetValue(), true);
376 return;
377 }
378
379 NameDictionary *dict = NameDictionary::Cast(array);
380 int entry = dict->FindEntry(key_.GetTaggedValue());
381 if (entry == -1) {
382 return;
383 }
384
385 JSTaggedValue value = dict->GetValue(entry);
386 uint32_t attr = dict->GetAttributes(entry).GetValue();
387 SetFound(entry, value, attr, false);
388 }
389
TransitionForAttributeChanged(const JSHandle<JSObject> & receiver,PropertyAttributes attr)390 void ObjectOperator::TransitionForAttributeChanged(const JSHandle<JSObject> &receiver, PropertyAttributes attr)
391 {
392 if (IsElement()) {
393 uint32_t index = GetIndex();
394 if (!receiver->GetJSHClass()->IsDictionaryElement()) {
395 JSObject::ElementsToDictionary(thread_, receiver);
396 auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject());
397 index = static_cast<uint32_t>(dict->FindEntry(JSTaggedValue(index)));
398 PropertyAttributes origin = dict->GetAttributes(index);
399 attr.SetDictionaryOrder(origin.GetDictionaryOrder());
400 dict->SetAttributes(thread_, index, attr);
401 } else {
402 auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject());
403 dict->SetAttributes(thread_, index, attr);
404 }
405 // update found result
406 UpdateFound(index, attr.GetValue(), false, true);
407 } else if (receiver->IsJSGlobalObject()) {
408 JSHandle<GlobalDictionary> dictHandle(thread_, receiver->GetProperties());
409 GlobalDictionary::InvalidatePropertyBox(thread_, dictHandle, GetIndex(), attr);
410 } else {
411 uint32_t index = GetIndex();
412 if (!receiver->GetJSHClass()->IsDictionaryMode()) {
413 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
414 index = static_cast<uint32_t>(dict->FindEntry(key_.GetTaggedValue()));
415 PropertyAttributes origin = dict->GetAttributes(index);
416 attr.SetDictionaryOrder(origin.GetDictionaryOrder());
417 dict->SetAttributes(thread_, index, attr);
418 } else {
419 auto dict = NameDictionary::Cast(receiver->GetProperties().GetTaggedObject());
420 dict->SetAttributes(thread_, index, attr);
421 }
422 // update found result
423 UpdateFound(index, attr.GetValue(), false, true);
424 }
425 }
426
UpdateValueAndDetails(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr,bool attrChanged)427 bool ObjectOperator::UpdateValueAndDetails(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
428 PropertyAttributes attr, bool attrChanged)
429 {
430 auto valueAccessor = GetValue();
431 if (valueAccessor.IsPropertyBox()) {
432 valueAccessor = PropertyBox::Cast(valueAccessor.GetTaggedObject())->GetValue();
433 }
434 bool isInternalAccessor = IsAccessorDescriptor()
435 && AccessorData::Cast(valueAccessor.GetTaggedObject())->IsInternal();
436 if (attrChanged) {
437 TransitionForAttributeChanged(receiver, attr);
438 }
439 return UpdateDataValue(receiver, value, isInternalAccessor);
440 }
441
ConvertOrTransitionWithRep(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes & attr,bool & needBarrier)442 JSTaggedValue ObjectOperator::ConvertOrTransitionWithRep(const JSHandle<JSObject> &receiver,
443 const JSHandle<JSTaggedValue> &value, PropertyAttributes &attr, bool &needBarrier)
444 {
445 Representation oldRep = attr.GetRepresentation();
446 if (oldRep == Representation::DOUBLE) {
447 if (value->IsInt()) {
448 double doubleValue = value->GetInt();
449 needBarrier = false;
450 return JSTaggedValue(bit_cast<JSTaggedType>(doubleValue));
451 } else if (value->IsObject()) {
452 // Is Object
453 attributes_.SetRepresentation(Representation::TAGGED);
454 // Transtion
455 JSHClass::TransitionForRepChange(thread_, receiver, key_, attributes_);
456 } else {
457 // Is TaggedDouble
458 needBarrier = false;
459 return JSTaggedValue(bit_cast<JSTaggedType>(value->GetDouble()));
460 }
461 } else if (oldRep == Representation::INT) {
462 if (value->IsInt()) {
463 int intValue = value->GetInt();
464 needBarrier = false;
465 return JSTaggedValue(static_cast<JSTaggedType>(intValue));
466 } else {
467 attributes_.SetRepresentation(Representation::TAGGED);
468 JSHClass::TransitionForRepChange(thread_, receiver, key_, attributes_);
469 }
470 }
471 return value.GetTaggedValue();
472 }
473
UpdateDataValue(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,bool isInternalAccessor,bool mayThrow)474 bool ObjectOperator::UpdateDataValue(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
475 bool isInternalAccessor, bool mayThrow)
476 {
477 if (IsElement()) {
478 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
479 if (!elements->IsDictionaryMode()) {
480 if (receiver.GetTaggedValue().IsJSCOWArray()) {
481 JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
482 TaggedArray::Cast(JSHandle<JSArray>(receiver)->GetElements())->Set(thread_,
483 GetIndex(), value.GetTaggedValue());
484 return true;
485 }
486 elements->Set(thread_, GetIndex(), value.GetTaggedValue());
487 JSHClass::TransitToElementsKind(thread_, receiver, value);
488 return true;
489 }
490
491 NumberDictionary *dict = NumberDictionary::Cast(elements);
492 dict->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
493 return true;
494 }
495
496 if (receiver->IsJSGlobalObject()) {
497 // need update cell type ?
498 auto *dict = GlobalDictionary::Cast(receiver->GetProperties().GetTaggedObject());
499 if (isInternalAccessor && !value->IsAccessor()) {
500 PropertyAttributes attr = dict->GetAttributes(GetIndex());
501 attr.SetIsAccessor(false);
502 dict->SetAttributes(thread_, GetIndex(), attr);
503 }
504 PropertyBox *cell = dict->GetBox(GetIndex());
505 cell->SetValue(thread_, value.GetTaggedValue());
506 return true;
507 }
508
509 if (isInternalAccessor) {
510 auto accessor = AccessorData::Cast(GetValue().GetTaggedObject());
511 if (accessor->HasSetter()) {
512 bool res = accessor->CallInternalSet(thread_, JSHandle<JSObject>(receiver), value, mayThrow);
513 if (receiver->GetJSHClass()->IsDictionaryMode()) {
514 SetIsInlinedProps(false);
515 SetFastMode(false);
516 }
517 return res;
518 }
519 }
520
521 JSMutableHandle<TaggedArray> properties(thread_, TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()));
522 if (!properties->IsDictionaryMode()) {
523 if (thread_->IsPGOProfilerEnable() && attributes_.UpdateTrackType(value.GetTaggedValue())) {
524 LayoutInfo *layoutInfo = LayoutInfo::Cast(receiver->GetJSHClass()->GetLayout().GetTaggedObject());
525 uint32_t offset = index_;
526 if (!attributes_.IsInlinedProps()) {
527 auto *hclass = receiver_->GetTaggedObject()->GetClass();
528 offset += hclass->GetInlinedProperties();
529 }
530 layoutInfo->SetNormalAttr(thread_, offset, attributes_);
531 }
532 bool needBarrier = true;
533 JSTaggedValue actualValue = value.GetTaggedValue();
534 if (IsTSHClass()) {
535 actualValue = ConvertOrTransitionWithRep(receiver, value, attributes_, needBarrier);
536 }
537
538 PropertyAttributes attr = GetAttr();
539 if (attr.IsInlinedProps()) {
540 receiver->SetPropertyInlinedPropsWithRep(thread_, GetIndex(), actualValue);
541 } else {
542 if (receiver.GetTaggedValue().IsJSCOWArray()) {
543 JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
544 properties.Update(JSHandle<JSArray>(receiver)->GetProperties());
545 }
546 if (needBarrier) {
547 properties->Set<true>(thread_, GetIndex(), value.GetTaggedValue());
548 } else {
549 properties->Set<false>(thread_, GetIndex(), actualValue);
550 }
551 }
552 } else {
553 properties.GetObject<NameDictionary>()->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
554 }
555 return true;
556 }
557
WriteDataProperty(const JSHandle<JSObject> & receiver,const PropertyDescriptor & desc)558 bool ObjectOperator::WriteDataProperty(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc)
559 {
560 PropertyAttributes attr = GetAttr();
561 bool attrChanged = false;
562
563 // composed new attribute from desc
564 if (desc.HasConfigurable() && attr.IsConfigurable() != desc.IsConfigurable()) {
565 attr.SetConfigurable(desc.IsConfigurable());
566 attrChanged = true;
567 }
568 if (desc.HasEnumerable() && attr.IsEnumerable() != desc.IsEnumerable()) {
569 attr.SetEnumerable(desc.IsEnumerable());
570 attrChanged = true;
571 }
572
573 if (!desc.IsAccessorDescriptor()) {
574 if (desc.HasWritable() && attr.IsWritable() != desc.IsWritable()) {
575 attr.SetWritable(desc.IsWritable());
576 attrChanged = true;
577 }
578 if (!desc.HasValue()) {
579 if (attrChanged) {
580 TransitionForAttributeChanged(receiver, attr);
581 }
582 return true;
583 }
584
585 if (IsAccessorDescriptor()) {
586 auto accessor = AccessorData::Cast(GetValue().GetTaggedObject());
587 if (!accessor->IsInternal() || !accessor->HasSetter()) {
588 attr.SetIsAccessor(false);
589 attrChanged = true;
590 }
591 }
592
593 return UpdateValueAndDetails(receiver, desc.GetValue(), attr, attrChanged);
594 } else {
595 if (IsAccessorDescriptor() && !IsElement()) {
596 TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject());
597 if (attrChanged && !properties->IsDictionaryMode()) {
598 // as some accessorData is in globalEnv, we need to new accessorData.
599 JSHandle<AccessorData> accessor = thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
600
601 if (desc.HasGetter()) {
602 accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
603 } else {
604 accessor->SetGetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetGetter());
605 }
606 if (desc.HasSetter()) {
607 accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
608 } else {
609 accessor->SetSetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetSetter());
610 }
611
612 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
613 int entry = dict->FindEntry(key_.GetTaggedValue());
614 ASSERT(entry != -1);
615 dict->UpdateValueAndAttributes(thread_, entry, accessor.GetTaggedValue(), attr);
616 return true;
617 }
618 }
619
620 auto valueAccessor = GetValue();
621 if (valueAccessor.IsPropertyBox()) {
622 valueAccessor = PropertyBox::Cast(valueAccessor.GetTaggedObject())->GetValue();
623 }
624 JSHandle<AccessorData> accessor =
625 (IsAccessorDescriptor() && !JSHandle<AccessorData>(thread_, valueAccessor)->IsInternal()) ?
626 JSHandle<AccessorData>(thread_, valueAccessor) :
627 thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
628 if (desc.HasGetter()) {
629 accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
630 }
631
632 if (desc.HasSetter()) {
633 accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
634 }
635
636 if (!IsAccessorDescriptor()) {
637 attr.SetIsAccessor(true);
638 attrChanged = true;
639 }
640
641 JSHandle<JSTaggedValue> value = JSHandle<JSTaggedValue>::Cast(accessor);
642 return UpdateValueAndDetails(receiver, value, attr, attrChanged);
643 }
644 }
645
DeletePropertyInHolder()646 void ObjectOperator::DeletePropertyInHolder()
647 {
648 if (IsElement()) {
649 return DeleteElementInHolder();
650 }
651
652 JSObject::DeletePropertyInternal(thread_, JSHandle<JSObject>(holder_), key_, GetIndex());
653 }
654
AddProperty(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr)655 bool ObjectOperator::AddProperty(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
656 PropertyAttributes attr)
657 {
658 if (IsElement()) {
659 bool ret = JSObject::AddElementInternal(thread_, receiver, elementIndex_, value, attr);
660 bool isDict = receiver->GetJSHClass()->IsDictionaryElement();
661 SetFound(elementIndex_, value.GetTaggedValue(), attr.GetValue(), !isDict);
662 return ret;
663 }
664
665 ResetStateForAddProperty();
666 receiver_.Update(receiver.GetTaggedValue());
667 SetAttr(attr.GetValue());
668 AddPropertyInternal(value);
669 return true;
670 }
671
WriteElement(const JSHandle<JSObject> & receiver,JSTaggedValue value) const672 void ObjectOperator::WriteElement(const JSHandle<JSObject> &receiver, JSTaggedValue value) const
673 {
674 ASSERT(IsElement() && GetIndex() < JSObject::MAX_ELEMENT_INDEX);
675
676 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
677 if (!elements->IsDictionaryMode()) {
678 elements->Set(thread_, index_, value);
679 return;
680 }
681
682 NumberDictionary *dictionary = NumberDictionary::Cast(elements);
683 dictionary->UpdateValue(thread_, GetIndex(), value);
684 }
685
DeleteElementInHolder() const686 void ObjectOperator::DeleteElementInHolder() const
687 {
688 JSHandle<JSObject> obj(holder_);
689
690 TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
691 if (!elements->IsDictionaryMode()) {
692 elements->Set(thread_, index_, JSTaggedValue::Hole());
693 JSObject::ElementsToDictionary(thread_, JSHandle<JSObject>(holder_));
694 } else {
695 JSHandle<NumberDictionary> dictHandle(thread_, elements);
696 JSHandle<NumberDictionary> newDict = NumberDictionary::Remove(thread_, dictHandle, GetIndex());
697 obj->SetElements(thread_, newDict);
698 }
699 }
700
SetFound(uint32_t index,JSTaggedValue value,uint32_t attr,bool mode,bool transition)701 void ObjectOperator::SetFound(uint32_t index, JSTaggedValue value, uint32_t attr, bool mode, bool transition)
702 {
703 SetIndex(index);
704 SetValue(value);
705 SetFastMode(mode);
706 SetIsTransition(transition);
707 SetAttr(attr);
708 }
709
UpdateFound(uint32_t index,uint32_t attr,bool mode,bool transition)710 void ObjectOperator::UpdateFound(uint32_t index, uint32_t attr, bool mode, bool transition)
711 {
712 SetIndex(index);
713 SetFastMode(mode);
714 SetIsTransition(transition);
715 SetAttr(attr);
716 }
717
ResetState()718 void ObjectOperator::ResetState()
719 {
720 // index may used by element
721 SetIndex(NOT_FOUND_INDEX);
722 SetValue(JSTaggedValue::Undefined());
723 SetFastMode(false);
724 SetAttr(0);
725 SetIsOnPrototype(false);
726 SetHasReceiver(false);
727 SetIsTSHClass(false);
728 }
729
ResetStateForAddProperty()730 void ObjectOperator::ResetStateForAddProperty()
731 {
732 bool isOnPrototype = IsOnPrototype();
733 ResetState();
734 SetIsOnPrototype(isOnPrototype);
735 }
736
LookupElementInlinedProps(const JSHandle<JSObject> & obj)737 void ObjectOperator::LookupElementInlinedProps(const JSHandle<JSObject> &obj)
738 {
739 // if is js string, do special.
740 if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(obj.GetTaggedValue().GetTaggedObject())->IsString()) {
741 PropertyDescriptor desc(thread_);
742 bool status = JSPrimitiveRef::StringGetIndexProperty(thread_, obj, elementIndex_, &desc);
743 if (status) {
744 PropertyAttributes attr(desc);
745 SetFound(elementIndex_, desc.GetValue().GetTaggedValue(), attr.GetValue(), true);
746 return;
747 }
748 }
749 {
750 DISALLOW_GARBAGE_COLLECTION;
751 TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
752 if (elements->GetLength() == 0) {
753 return; // Empty Array
754 }
755
756 if (!elements->IsDictionaryMode()) {
757 if (elements->GetLength() <= elementIndex_) {
758 return;
759 }
760
761 JSTaggedValue value = elements->Get(elementIndex_);
762 if (value.IsHole()) {
763 return;
764 }
765 SetFound(elementIndex_, value, PropertyAttributes::GetDefaultAttributes(), true);
766 } else {
767 NumberDictionary *dictionary = NumberDictionary::Cast(obj->GetElements().GetTaggedObject());
768 JSTaggedValue key(static_cast<int>(elementIndex_));
769 int entry = dictionary->FindEntry(key);
770 if (entry == -1) {
771 return;
772 }
773
774 uint32_t attr = dictionary->GetAttributes(entry).GetValue();
775 SetFound(entry, dictionary->GetValue(entry), attr, false);
776 }
777 }
778 }
779
AddPropertyInternal(const JSHandle<JSTaggedValue> & value)780 void ObjectOperator::AddPropertyInternal(const JSHandle<JSTaggedValue> &value)
781 {
782 ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
783 JSHandle<JSObject> obj(GetReceiver());
784 PropertyAttributes attr = GetAttr();
785 if (obj->IsJSGlobalObject()) {
786 JSMutableHandle<GlobalDictionary> dict(thread_, obj->GetProperties());
787 if (dict->GetLength() == 0) {
788 dict.Update(GlobalDictionary::Create(thread_));
789 }
790
791 // Add PropertyBox to global dictionary
792 JSHandle<PropertyBox> cellHandle = factory->NewPropertyBox(key_);
793 cellHandle->SetValue(thread_, value.GetTaggedValue());
794 PropertyBoxType cellType = value->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT;
795 attr.SetBoxType(cellType);
796
797 JSHandle<GlobalDictionary> properties =
798 GlobalDictionary::PutIfAbsent(thread_, dict, key_, JSHandle<JSTaggedValue>(cellHandle), attr);
799 obj->SetProperties(thread_, properties);
800 // index and fastMode is not essential for global obj;
801 SetFound(0, cellHandle.GetTaggedValue(), attr.GetValue(), true);
802 return;
803 }
804
805 // The property has already existed whose value is hole, initialized by speculative hclass.
806 // Not need AddProperty,just SetProperty
807 if (receiverHoleEntry_ != -1) {
808 auto *hclass = receiver_->GetTaggedObject()->GetClass();
809 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
810 attr = layoutInfo->GetAttr(receiverHoleEntry_);
811 if (thread_->IsPGOProfilerEnable() && attr.UpdateTrackType(value.GetTaggedValue())) {
812 layoutInfo->SetNormalAttr(thread_, receiverHoleEntry_, attr);
813 }
814 bool needBarrier = true;
815 JSTaggedValue actualValue = ConvertOrTransitionWithRep(JSHandle<JSObject>(receiver_), value, attr, needBarrier);
816 if (needBarrier) {
817 JSObject::Cast(receiver_.GetTaggedValue())->SetProperty<true>(
818 thread_, hclass, attr, value.GetTaggedValue());
819 } else {
820 JSObject::Cast(receiver_.GetTaggedValue())->SetProperty<false>(
821 thread_, hclass, attr, actualValue);
822 }
823 uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
824 attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
825 SetIsTSHClass(true);
826 SetIsOnPrototype(false);
827 SetFound(index, value.GetTaggedValue(), attr.GetValue(), true);
828 return;
829 }
830
831 attr = ObjectFastOperator::AddPropertyByName(thread_, obj, key_, value, attr);
832 if (obj->GetJSHClass()->IsDictionaryMode()) {
833 SetFound(0, value.GetTaggedValue(), attr.GetValue(), false);
834 } else {
835 uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
836 attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
837 SetFound(index, value.GetTaggedValue(), attr.GetValue(), true, true);
838 }
839 }
840
DefineSetter(const JSHandle<JSTaggedValue> & value)841 void ObjectOperator::DefineSetter(const JSHandle<JSTaggedValue> &value)
842 {
843 ASSERT(IsAccessorDescriptor());
844 JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
845 accessor->SetSetter(thread_, value.GetTaggedValue());
846 UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
847 }
848
DefineGetter(const JSHandle<JSTaggedValue> & value)849 void ObjectOperator::DefineGetter(const JSHandle<JSTaggedValue> &value)
850 {
851 ASSERT(IsAccessorDescriptor());
852 JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
853 accessor->SetGetter(thread_, value.GetTaggedValue());
854 UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
855 }
856 } // namespace panda::ecmascript
857