• 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         uint32_t index = GetIndex();
409         JSHandle<GlobalDictionary> dictHandle(thread_, receiver->GetProperties());
410         dictHandle->SetAttributes(thread_, index, attr);
411         GlobalDictionary::InvalidatePropertyBox(thread_, dictHandle, GetIndex(), attr);
412     } else {
413         uint32_t index = GetIndex();
414         if (!receiver->GetJSHClass()->IsDictionaryMode()) {
415             JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
416             index = static_cast<uint32_t>(dict->FindEntry(key_.GetTaggedValue()));
417             PropertyAttributes origin = dict->GetAttributes(index);
418             attr.SetDictionaryOrder(origin.GetDictionaryOrder());
419             dict->SetAttributes(thread_, index, attr);
420         } else {
421             auto dict = NameDictionary::Cast(receiver->GetProperties().GetTaggedObject());
422             dict->SetAttributes(thread_, index, attr);
423         }
424         // update found result
425         UpdateFound(index, attr.GetValue(), false, true);
426     }
427 }
428 
UpdateValueAndDetails(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr,bool attrChanged)429 bool ObjectOperator::UpdateValueAndDetails(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
430                                            PropertyAttributes attr, bool attrChanged)
431 {
432     auto valueAccessor = GetValue();
433     if (valueAccessor.IsPropertyBox()) {
434         valueAccessor = PropertyBox::Cast(valueAccessor.GetTaggedObject())->GetValue();
435     }
436     bool isInternalAccessor = IsAccessorDescriptor()
437         && AccessorData::Cast(valueAccessor.GetTaggedObject())->IsInternal();
438     if (attrChanged) {
439         TransitionForAttributeChanged(receiver, attr);
440     }
441     return UpdateDataValue(receiver, value, isInternalAccessor);
442 }
443 
ConvertOrTransitionWithRep(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes & attr,bool & needBarrier)444 JSTaggedValue ObjectOperator::ConvertOrTransitionWithRep(const JSHandle<JSObject> &receiver,
445     const JSHandle<JSTaggedValue> &value, PropertyAttributes &attr, bool &needBarrier)
446 {
447     Representation oldRep = attr.GetRepresentation();
448     if (oldRep == Representation::DOUBLE) {
449         if (value->IsInt()) {
450             double doubleValue = value->GetInt();
451             needBarrier = false;
452             return JSTaggedValue(bit_cast<JSTaggedType>(doubleValue));
453         } else if (value->IsObject()) {
454             // Is Object
455             attributes_.SetRepresentation(Representation::TAGGED);
456             // Transtion
457             JSHClass::TransitionForRepChange(thread_, receiver, key_, attributes_);
458         } else {
459             // Is TaggedDouble
460             needBarrier = false;
461             return JSTaggedValue(bit_cast<JSTaggedType>(value->GetDouble()));
462         }
463     } else if (oldRep == Representation::INT) {
464         if (value->IsInt()) {
465             int intValue = value->GetInt();
466             needBarrier = false;
467             return JSTaggedValue(static_cast<JSTaggedType>(intValue));
468         } else {
469             attributes_.SetRepresentation(Representation::TAGGED);
470             JSHClass::TransitionForRepChange(thread_, receiver, key_, attributes_);
471         }
472     }
473     return value.GetTaggedValue();
474 }
475 
UpdateDataValue(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,bool isInternalAccessor,bool mayThrow)476 bool ObjectOperator::UpdateDataValue(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
477                                      bool isInternalAccessor, bool mayThrow)
478 {
479     if (IsElement()) {
480         TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
481         if (!elements->IsDictionaryMode()) {
482             if (receiver.GetTaggedValue().IsJSCOWArray()) {
483                 JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
484                 TaggedArray::Cast(JSHandle<JSArray>(receiver)->GetElements())->Set(thread_,
485                     GetIndex(), value.GetTaggedValue());
486                 return true;
487             }
488             elements->Set(thread_, GetIndex(), value.GetTaggedValue());
489             JSHClass::TransitToElementsKind(thread_, receiver, value);
490             return true;
491         }
492 
493         NumberDictionary *dict = NumberDictionary::Cast(elements);
494         dict->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
495         return true;
496     }
497 
498     if (receiver->IsJSGlobalObject()) {
499         // need update cell type ?
500         auto *dict = GlobalDictionary::Cast(receiver->GetProperties().GetTaggedObject());
501         if (isInternalAccessor && !value->IsAccessor()) {
502             PropertyAttributes attr = dict->GetAttributes(GetIndex());
503             attr.SetIsAccessor(false);
504             dict->SetAttributes(thread_, GetIndex(), attr);
505         }
506         PropertyBox *cell = dict->GetBox(GetIndex());
507         cell->SetValue(thread_, value.GetTaggedValue());
508         return true;
509     }
510 
511     if (isInternalAccessor) {
512         auto accessor = AccessorData::Cast(GetValue().GetTaggedObject());
513         if (accessor->HasSetter()) {
514             bool res = accessor->CallInternalSet(thread_, JSHandle<JSObject>(receiver), value, mayThrow);
515             if (receiver->GetJSHClass()->IsDictionaryMode()) {
516                 SetIsInlinedProps(false);
517                 SetFastMode(false);
518             }
519             return res;
520         }
521     }
522 
523     JSMutableHandle<TaggedArray> properties(thread_, TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()));
524     if (!properties->IsDictionaryMode()) {
525         if (thread_->IsPGOProfilerEnable() && attributes_.UpdateTrackType(value.GetTaggedValue())) {
526             LayoutInfo *layoutInfo = LayoutInfo::Cast(receiver->GetJSHClass()->GetLayout().GetTaggedObject());
527             uint32_t offset = index_;
528             if (!attributes_.IsInlinedProps()) {
529                 auto *hclass = receiver_->GetTaggedObject()->GetClass();
530                 offset += hclass->GetInlinedProperties();
531             }
532             layoutInfo->SetNormalAttr(thread_, offset, attributes_);
533         }
534         bool needBarrier = true;
535         JSTaggedValue actualValue = value.GetTaggedValue();
536         if (IsTSHClass()) {
537             actualValue = ConvertOrTransitionWithRep(receiver, value, attributes_, needBarrier);
538         }
539 
540         PropertyAttributes attr = GetAttr();
541         if (attr.IsInlinedProps()) {
542             receiver->SetPropertyInlinedPropsWithRep(thread_, GetIndex(), actualValue);
543         } else {
544             if (receiver.GetTaggedValue().IsJSCOWArray()) {
545                 JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
546                 properties.Update(JSHandle<JSArray>(receiver)->GetProperties());
547             }
548             if (needBarrier) {
549                 properties->Set<true>(thread_, GetIndex(), value.GetTaggedValue());
550             } else {
551                 properties->Set<false>(thread_, GetIndex(), actualValue);
552             }
553         }
554     } else {
555         properties.GetObject<NameDictionary>()->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
556     }
557     return true;
558 }
559 
WriteDataProperty(const JSHandle<JSObject> & receiver,const PropertyDescriptor & desc)560 bool ObjectOperator::WriteDataProperty(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc)
561 {
562     PropertyAttributes attr = GetAttr();
563     bool attrChanged = false;
564 
565     // composed new attribute from desc
566     if (desc.HasConfigurable() && attr.IsConfigurable() != desc.IsConfigurable()) {
567         attr.SetConfigurable(desc.IsConfigurable());
568         attrChanged = true;
569     }
570     if (desc.HasEnumerable() && attr.IsEnumerable() != desc.IsEnumerable()) {
571         attr.SetEnumerable(desc.IsEnumerable());
572         attrChanged = true;
573     }
574 
575     if (!desc.IsAccessorDescriptor()) {
576         if (desc.HasWritable() && attr.IsWritable() != desc.IsWritable()) {
577             attr.SetWritable(desc.IsWritable());
578             attrChanged = true;
579         }
580         if (!desc.HasValue()) {
581             if (attrChanged) {
582                 TransitionForAttributeChanged(receiver, attr);
583             }
584             return true;
585         }
586 
587         if (IsAccessorDescriptor()) {
588             TaggedObject *obj = GetValue().GetTaggedObject();
589             if (receiver->IsJSGlobalObject()) {
590                 JSTaggedValue val = GetValue();
591                 if (val.IsPropertyBox()) {
592                     PropertyBox *cell = PropertyBox::Cast(val.GetTaggedObject());
593                     obj = cell->GetValue().GetTaggedObject();
594                 }
595             }
596             auto accessor = AccessorData::Cast(obj);
597             if (!accessor->IsInternal() || !accessor->HasSetter()) {
598                 attr.SetIsAccessor(false);
599                 attrChanged = true;
600             }
601         }
602 
603         return UpdateValueAndDetails(receiver, desc.GetValue(), attr, attrChanged);
604     } else {
605         if (IsAccessorDescriptor() && !IsElement()) {
606             TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject());
607             if (attrChanged && !properties->IsDictionaryMode()) {
608                 // as some accessorData is in globalEnv, we need to new accessorData.
609                 JSHandle<AccessorData> accessor = thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
610 
611                 if (desc.HasGetter()) {
612                     accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
613                 } else {
614                     accessor->SetGetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetGetter());
615                 }
616                 if (desc.HasSetter()) {
617                     accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
618                 } else {
619                     accessor->SetSetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetSetter());
620                 }
621 
622                 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
623                 int entry = dict->FindEntry(key_.GetTaggedValue());
624                 ASSERT(entry != -1);
625                 dict->UpdateValueAndAttributes(thread_, entry, accessor.GetTaggedValue(), attr);
626                 return true;
627             }
628         }
629 
630         auto valueAccessor = GetValue();
631         if (valueAccessor.IsPropertyBox()) {
632             valueAccessor = PropertyBox::Cast(valueAccessor.GetTaggedObject())->GetValue();
633         }
634         JSHandle<AccessorData> accessor =
635             (IsAccessorDescriptor() && !JSHandle<AccessorData>(thread_, valueAccessor)->IsInternal()) ?
636             JSHandle<AccessorData>(thread_, valueAccessor) :
637             thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
638         if (desc.HasGetter()) {
639             accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
640         }
641 
642         if (desc.HasSetter()) {
643             accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
644         }
645 
646         if (!IsAccessorDescriptor()) {
647             attr.SetIsAccessor(true);
648             attrChanged = true;
649         }
650 
651         JSHandle<JSTaggedValue> value = JSHandle<JSTaggedValue>::Cast(accessor);
652         return UpdateValueAndDetails(receiver, value, attr, attrChanged);
653     }
654 }
655 
DeletePropertyInHolder()656 void ObjectOperator::DeletePropertyInHolder()
657 {
658     if (IsElement()) {
659         return DeleteElementInHolder();
660     }
661 
662     JSObject::DeletePropertyInternal(thread_, JSHandle<JSObject>(holder_), key_, GetIndex());
663 }
664 
AddProperty(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr)665 bool ObjectOperator::AddProperty(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
666                                  PropertyAttributes attr)
667 {
668     if (IsElement()) {
669         bool ret = JSObject::AddElementInternal(thread_, receiver, elementIndex_, value, attr);
670         bool isDict = receiver->GetJSHClass()->IsDictionaryElement();
671         SetFound(elementIndex_, value.GetTaggedValue(), attr.GetValue(), !isDict);
672         return ret;
673     }
674 
675     ResetStateForAddProperty();
676     receiver_.Update(receiver.GetTaggedValue());
677     SetAttr(attr.GetValue());
678     AddPropertyInternal(value);
679     return true;
680 }
681 
WriteElement(const JSHandle<JSObject> & receiver,JSTaggedValue value) const682 void ObjectOperator::WriteElement(const JSHandle<JSObject> &receiver, JSTaggedValue value) const
683 {
684     ASSERT(IsElement() && GetIndex() < JSObject::MAX_ELEMENT_INDEX);
685 
686     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
687     if (!elements->IsDictionaryMode()) {
688         elements->Set(thread_, index_, value);
689         return;
690     }
691 
692     NumberDictionary *dictionary = NumberDictionary::Cast(elements);
693     dictionary->UpdateValue(thread_, GetIndex(), value);
694 }
695 
DeleteElementInHolder() const696 void ObjectOperator::DeleteElementInHolder() const
697 {
698     JSHandle<JSObject> obj(holder_);
699 
700     TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
701     if (!elements->IsDictionaryMode()) {
702         elements->Set(thread_, index_, JSTaggedValue::Hole());
703         JSObject::ElementsToDictionary(thread_, JSHandle<JSObject>(holder_));
704     } else {
705         JSHandle<NumberDictionary> dictHandle(thread_, elements);
706         JSHandle<NumberDictionary> newDict = NumberDictionary::Remove(thread_, dictHandle, GetIndex());
707         obj->SetElements(thread_, newDict);
708     }
709 }
710 
SetFound(uint32_t index,JSTaggedValue value,uint32_t attr,bool mode,bool transition)711 void ObjectOperator::SetFound(uint32_t index, JSTaggedValue value, uint32_t attr, bool mode, bool transition)
712 {
713     SetIndex(index);
714     SetValue(value);
715     SetFastMode(mode);
716     SetIsTransition(transition);
717     SetAttr(attr);
718 }
719 
UpdateFound(uint32_t index,uint32_t attr,bool mode,bool transition)720 void ObjectOperator::UpdateFound(uint32_t index, uint32_t attr, bool mode, bool transition)
721 {
722     SetIndex(index);
723     SetFastMode(mode);
724     SetIsTransition(transition);
725     SetAttr(attr);
726 }
727 
ResetState()728 void ObjectOperator::ResetState()
729 {
730     // index may used by element
731     SetIndex(NOT_FOUND_INDEX);
732     SetValue(JSTaggedValue::Undefined());
733     SetFastMode(false);
734     SetAttr(0);
735     SetIsOnPrototype(false);
736     SetHasReceiver(false);
737     SetIsTSHClass(false);
738 }
739 
ResetStateForAddProperty()740 void ObjectOperator::ResetStateForAddProperty()
741 {
742     bool isOnPrototype = IsOnPrototype();
743     ResetState();
744     SetIsOnPrototype(isOnPrototype);
745 }
746 
LookupElementInlinedProps(const JSHandle<JSObject> & obj)747 void ObjectOperator::LookupElementInlinedProps(const JSHandle<JSObject> &obj)
748 {
749     // if is js string, do special.
750     if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(obj.GetTaggedValue().GetTaggedObject())->IsString()) {
751         PropertyDescriptor desc(thread_);
752         bool status = JSPrimitiveRef::StringGetIndexProperty(thread_, obj, elementIndex_, &desc);
753         if (status) {
754             PropertyAttributes attr(desc);
755             SetFound(elementIndex_, desc.GetValue().GetTaggedValue(), attr.GetValue(), true);
756             return;
757         }
758     }
759     {
760         DISALLOW_GARBAGE_COLLECTION;
761         TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
762         if (elements->GetLength() == 0) {
763             return;  // Empty Array
764         }
765 
766         if (!elements->IsDictionaryMode()) {
767             if (elements->GetLength() <= elementIndex_) {
768                 return;
769             }
770 
771             JSTaggedValue value = elements->Get(elementIndex_);
772             if (value.IsHole()) {
773                 return;
774             }
775             SetFound(elementIndex_, value, PropertyAttributes::GetDefaultAttributes(), true);
776         } else {
777             NumberDictionary *dictionary = NumberDictionary::Cast(obj->GetElements().GetTaggedObject());
778             JSTaggedValue key(static_cast<int>(elementIndex_));
779             int entry = dictionary->FindEntry(key);
780             if (entry == -1) {
781                 return;
782             }
783 
784             uint32_t attr = dictionary->GetAttributes(entry).GetValue();
785             SetFound(entry, dictionary->GetValue(entry), attr, false);
786         }
787     }
788 }
789 
AddPropertyInternal(const JSHandle<JSTaggedValue> & value)790 void ObjectOperator::AddPropertyInternal(const JSHandle<JSTaggedValue> &value)
791 {
792     ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
793     JSHandle<JSObject> obj(GetReceiver());
794     PropertyAttributes attr = GetAttr();
795     if (obj->IsJSGlobalObject()) {
796         JSMutableHandle<GlobalDictionary> dict(thread_, obj->GetProperties());
797         if (dict->GetLength() == 0) {
798             dict.Update(GlobalDictionary::Create(thread_));
799         }
800 
801         // Add PropertyBox to global dictionary
802         JSHandle<PropertyBox> cellHandle = factory->NewPropertyBox(key_);
803         cellHandle->SetValue(thread_, value.GetTaggedValue());
804         PropertyBoxType cellType = value->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT;
805         attr.SetBoxType(cellType);
806 
807         JSHandle<GlobalDictionary> properties =
808             GlobalDictionary::PutIfAbsent(thread_, dict, key_, JSHandle<JSTaggedValue>(cellHandle), attr);
809         obj->SetProperties(thread_, properties);
810         // index and fastMode is not essential for global obj;
811         SetFound(0, cellHandle.GetTaggedValue(), attr.GetValue(), true);
812         return;
813     }
814 
815     // The property has already existed whose value is hole, initialized by speculative hclass.
816     // Not need AddProperty,just SetProperty
817     if (receiverHoleEntry_ != -1) {
818         auto *hclass = receiver_->GetTaggedObject()->GetClass();
819         LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
820         attr = layoutInfo->GetAttr(receiverHoleEntry_);
821         if (thread_->IsPGOProfilerEnable() && attr.UpdateTrackType(value.GetTaggedValue())) {
822             layoutInfo->SetNormalAttr(thread_, receiverHoleEntry_, attr);
823         }
824         bool needBarrier = true;
825         JSTaggedValue actualValue = ConvertOrTransitionWithRep(JSHandle<JSObject>(receiver_), value, attr, needBarrier);
826         if (needBarrier) {
827             JSObject::Cast(receiver_.GetTaggedValue())->SetProperty<true>(
828                 thread_, hclass, attr, value.GetTaggedValue());
829         } else {
830             JSObject::Cast(receiver_.GetTaggedValue())->SetProperty<false>(
831                 thread_, hclass, attr, actualValue);
832         }
833         uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
834                 attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
835         SetIsTSHClass(true);
836         SetIsOnPrototype(false);
837         SetFound(index, value.GetTaggedValue(), attr.GetValue(), true);
838         return;
839     }
840 
841     attr = ObjectFastOperator::AddPropertyByName(thread_, obj, key_, value, attr);
842     if (obj->GetJSHClass()->IsDictionaryMode()) {
843         SetFound(0, value.GetTaggedValue(), attr.GetValue(), false);
844     } else {
845         uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
846                 attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
847         SetFound(index, value.GetTaggedValue(), attr.GetValue(), true, true);
848     }
849 }
850 
DefineSetter(const JSHandle<JSTaggedValue> & value)851 void ObjectOperator::DefineSetter(const JSHandle<JSTaggedValue> &value)
852 {
853     ASSERT(IsAccessorDescriptor());
854     JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
855     accessor->SetSetter(thread_, value.GetTaggedValue());
856     UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
857 }
858 
DefineGetter(const JSHandle<JSTaggedValue> & value)859 void ObjectOperator::DefineGetter(const JSHandle<JSTaggedValue> &value)
860 {
861     ASSERT(IsAccessorDescriptor());
862     JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
863     accessor->SetGetter(thread_, value.GetTaggedValue());
864     UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
865 }
866 }  // namespace panda::ecmascript
867