• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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