• 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/tagged_dictionary.h"
34 
35 namespace panda::ecmascript {
HandleKey(const JSHandle<JSTaggedValue> & key)36 void ObjectOperator::HandleKey(const JSHandle<JSTaggedValue> &key)
37 {
38     if (key->IsInt()) {
39         int32_t keyInt = key->GetInt();
40         if (keyInt >= 0) {
41             elementIndex_ = static_cast<uint32_t>(keyInt);
42             return;
43         }
44         key_ = JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread_, JSTaggedValue(keyInt)));
45         return;
46     }
47 
48     if (key->IsString()) {
49         uint32_t index = 0;
50         if (JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index)) {
51             ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
52             elementIndex_ = index;
53             return;
54         }
55         if (EcmaStringAccessor(key->GetTaggedObject()).IsInternString()) {
56             key_ = key;
57             return;
58         }
59         key_ = JSHandle<JSTaggedValue>(thread_, thread_->GetEcmaVM()->GetFactory()->InternString(key));
60         return;
61     }
62 
63     if (key->IsDouble()) {
64         double number = key->GetDouble();
65         if (number >= 0 && number < JSObject::MAX_ELEMENT_INDEX) {
66             auto integer = static_cast<uint32_t>(number);
67             if (integer == number) {
68                 elementIndex_ = static_cast<uint32_t>(number);
69                 return;
70             }
71         }
72         key_ = JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread_, key.GetTaggedValue()));
73         return;
74     }
75 
76     if (key->IsSymbol()) {
77         key_ = key;
78         return;
79     }
80 
81     JSHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::ToPrimitive(thread_, key, PREFER_STRING));
82     if (key->IsSymbol()) {
83         key_ = keyHandle;
84         return;
85     }
86     key_ = JSHandle<JSTaggedValue>(thread_,
87                                    thread_->GetEcmaVM()->GetFactory()->InternString(
88                                        JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread_, keyHandle))));
89 }
90 
UpdateHolder()91 void ObjectOperator::UpdateHolder()
92 {
93     if (holder_->IsString() &&
94         (IsElement() && elementIndex_ < EcmaStringAccessor(holder_->GetTaggedObject()).GetLength())) {
95         holder_.Update(JSPrimitiveRef::StringCreate(thread_, holder_).GetTaggedValue());
96     } else {
97         holder_.Update(JSTaggedValue::ToPrototypeOrObj(thread_, holder_).GetTaggedValue());
98     }
99 }
100 
UpdateIsTSHClass()101 void ObjectOperator::UpdateIsTSHClass()
102 {
103     if (!holder_->IsECMAObject()) {
104         SetIsTSHClass(false);
105         return;
106     }
107     auto hclass = JSHandle<JSObject>::Cast(holder_)->GetClass();
108     if (hclass->IsTS()) {
109         SetIsTSHClass(true);
110     }
111 }
112 
StartLookUp(OperatorType type)113 void ObjectOperator::StartLookUp(OperatorType type)
114 {
115     UpdateHolder();
116 
117     if (type == OperatorType::OWN) {
118         LookupPropertyInHolder();
119     } else {
120         LookupProperty();
121     }
122 }
123 
StartGlobalLookUp(OperatorType type)124 void ObjectOperator::StartGlobalLookUp(OperatorType type)
125 {
126     UpdateHolder();
127 
128     if (type == OperatorType::OWN) {
129         GlobalLookupPropertyInHolder();
130     } else {
131         GlobalLookupProperty();
132     }
133 }
134 
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & key,OperatorType type)135 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &key, OperatorType type)
136     : thread_(thread),
137       holder_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()),
138       receiver_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject())
139 {
140     HandleKey(key);
141     StartGlobalLookUp(type);
142 }
143 
ObjectOperator(JSThread * thread,const JSHandle<JSObject> & holder,const JSHandle<JSTaggedValue> & key,OperatorType type)144 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSObject> &holder, const JSHandle<JSTaggedValue> &key,
145                                OperatorType type)
146     : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue())
147 {
148     HandleKey(key);
149     StartLookUp(type);
150 }
151 
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,const JSHandle<JSTaggedValue> & key,OperatorType type)152 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
153                                const JSHandle<JSTaggedValue> &key, OperatorType type)
154     : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue())
155 {
156     HandleKey(key);
157     StartLookUp(type);
158 }
159 
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,uint32_t index,OperatorType type)160 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, uint32_t index,
161                                OperatorType type)
162     : thread_(thread),
163       holder_(thread, holder.GetTaggedValue()),
164       receiver_(thread, holder.GetTaggedValue()),
165       elementIndex_(index)
166 {
167     StartLookUp(type);
168 }
169 
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,const JSHandle<JSTaggedValue> & receiver,const JSHandle<JSTaggedValue> & key,OperatorType type)170 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
171                                const JSHandle<JSTaggedValue> &receiver, const JSHandle<JSTaggedValue> &key,
172                                OperatorType type)
173     : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, receiver.GetTaggedValue())
174 {
175     SetHasReceiver(true);
176     HandleKey(key);
177     StartLookUp(type);
178 }
179 
180 // op for fast path
ObjectOperator(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,OperatorType type)181 ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
182                                OperatorType type)
183     : thread_(thread), holder_(thread, receiver), receiver_(thread, receiver), key_(thread, name)
184 {
185     ASSERT(name.IsStringOrSymbol());
186     StartLookUp(type);
187 }
FastGetValue()188 JSHandle<JSTaggedValue> ObjectOperator::FastGetValue()
189 {
190     ASSERT(IsFound() && !value_.IsEmpty());
191     if (value_->IsPropertyBox()) {
192         value_.Update(PropertyBox::Cast(value_->GetTaggedObject())->GetValue());
193     }
194     if (!IsAccessorDescriptor()) {
195         return value_;
196     }
197     AccessorData *accessor = AccessorData::Cast(value_->GetTaggedObject());
198     ASSERT(!accessor->IsInternal());
199     // 8. Return Call(getter, Receiver).
200     return JSHandle<JSTaggedValue>(thread_, JSObject::CallGetter(thread_, accessor, receiver_));
201 }
ObjectOperator(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,const PropertyAttributes & attr)202 ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
203                                const PropertyAttributes &attr)
204     : thread_(thread), receiver_(thread, receiver), key_(thread, name)
205 {
206     SetAttr(attr);
207 }
FastAdd(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,const JSHandle<JSTaggedValue> & value,const PropertyAttributes & attr)208 void ObjectOperator::FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
209                              const JSHandle<JSTaggedValue> &value, const PropertyAttributes &attr)
210 {
211     ObjectOperator op(thread, receiver, name, attr);
212     op.AddPropertyInternal(value);
213 }
214 
ToPropertyDescriptor(PropertyDescriptor & desc) const215 void ObjectOperator::ToPropertyDescriptor(PropertyDescriptor &desc) const
216 {
217     DISALLOW_GARBAGE_COLLECTION;
218     if (!IsFound()) {
219         return;
220     }
221 
222     if (!IsAccessorDescriptor()) {
223         desc.SetWritable(IsWritable());
224         JSTaggedValue val = GetValue();
225         desc.SetValue(JSHandle<JSTaggedValue>(thread_, val));
226     } else {
227         auto result = GetValue();
228         if (result.IsPropertyBox()) {
229             result = PropertyBox::Cast(result.GetTaggedObject())->GetValue();
230         }
231         AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject());
232 
233         if (UNLIKELY(accessor->IsInternal())) {
234             desc.SetWritable(IsWritable());
235             auto val = accessor->CallInternalGet(thread_, JSHandle<JSObject>::Cast(GetHolder()));
236             desc.SetValue(JSHandle<JSTaggedValue>(thread_, val));
237         } else {
238             desc.SetGetter(JSHandle<JSTaggedValue>(thread_, accessor->GetGetter()));
239             desc.SetSetter(JSHandle<JSTaggedValue>(thread_, accessor->GetSetter()));
240         }
241     }
242 
243     desc.SetEnumerable(IsEnumerable());
244     desc.SetConfigurable(IsConfigurable());
245 }
246 
GlobalLookupProperty()247 void ObjectOperator::GlobalLookupProperty()
248 {
249     GlobalLookupPropertyInHolder();
250     if (IsFound()) {
251         return;
252     }
253     JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_);
254     if (!proto.IsHeapObject()) {
255         return;
256     }
257     holder_.Update(proto);
258     if (holder_->IsJSProxy()) {
259         return;
260     }
261     SetIsOnPrototype(true);
262     LookupProperty();
263 }
264 
LookupProperty()265 void ObjectOperator::LookupProperty()
266 {
267     while (true) {
268         UpdateIsTSHClass();
269         LookupPropertyInHolder();
270         if (IsFound()) {
271             return;
272         }
273 
274         JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_);
275         if (!proto.IsHeapObject()) {
276             return;
277         }
278 
279         holder_.Update(proto);
280         if (holder_->IsJSProxy()) {
281             return;
282         }
283 
284         SetIsOnPrototype(true);
285     }
286 }
287 
LookupGlobal(const JSHandle<JSObject> & obj)288 void ObjectOperator::LookupGlobal(const JSHandle<JSObject> &obj)
289 {
290     ASSERT(obj->IsJSGlobalObject());
291     if (IsElement()) {
292         LookupElementInlinedProps(obj);
293         return;
294     }
295     TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
296     if (array->GetLength() == 0) {
297         return;
298     }
299     GlobalDictionary *dict = GlobalDictionary::Cast(array);
300     int entry = dict->FindEntry(key_.GetTaggedValue());
301     if (entry == -1) {
302         return;
303     }
304     JSTaggedValue value(dict->GetBox(entry));
305     uint32_t attr = dict->GetAttributes(entry).GetValue();
306     SetFound(entry, value, attr, true);
307 }
308 
LookupPropertyInlinedProps(const JSHandle<JSObject> & obj)309 void ObjectOperator::LookupPropertyInlinedProps(const JSHandle<JSObject> &obj)
310 {
311     if (IsElement()) {
312         LookupElementInlinedProps(obj);
313         return;
314     }
315 
316     if (obj->IsJSGlobalObject()) {
317         DISALLOW_GARBAGE_COLLECTION;
318         TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
319         if (array->GetLength() == 0) {
320             return;
321         }
322 
323         GlobalDictionary *dict = GlobalDictionary::Cast(array);
324         int entry = dict->FindEntry(key_.GetTaggedValue());
325         if (entry == -1) {
326             return;
327         }
328 
329         JSTaggedValue value(dict->GetBox(entry));
330         uint32_t attr = dict->GetAttributes(entry).GetValue();
331         SetFound(entry, value, attr, true);
332         return;
333     }
334 
335     TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
336     if (!array->IsDictionaryMode()) {
337         JSHClass *jshclass = obj->GetJSHClass();
338         JSTaggedValue attrs = jshclass->GetLayout();
339         LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject());
340         uint32_t propsNumber = jshclass->NumberOfProps();
341         int entry = layoutInfo->FindElementWithCache(thread_, jshclass, key_.GetTaggedValue(), propsNumber);
342         if (entry == -1) {
343             return;
344         }
345         PropertyAttributes attr(layoutInfo->GetAttr(entry));
346         ASSERT(entry == static_cast<int>(attr.GetOffset()));
347         JSTaggedValue value;
348         if (attr.IsInlinedProps()) {
349             value = obj->GetPropertyInlinedProps(entry);
350             if (value.IsHole()) {
351                 if (receiverHoleEntry_ == -1 && receiver_ == holder_) {
352                     receiverHoleEntry_ = entry;
353                 }
354                 return;
355             }
356         } else {
357             entry -= static_cast<int>(jshclass->GetInlinedProperties());
358             value = array->Get(entry);
359         }
360 
361         SetFound(entry, value, attr.GetValue(), true);
362         return;
363     }
364 
365     NameDictionary *dict = NameDictionary::Cast(array);
366     int entry = dict->FindEntry(key_.GetTaggedValue());
367     if (entry == -1) {
368         return;
369     }
370 
371     JSTaggedValue value = dict->GetValue(entry);
372     uint32_t attr = dict->GetAttributes(entry).GetValue();
373     SetFound(entry, value, attr, false);
374 }
375 
TransitionForAttributeChanged(const JSHandle<JSObject> & receiver,PropertyAttributes attr)376 void ObjectOperator::TransitionForAttributeChanged(const JSHandle<JSObject> &receiver, PropertyAttributes attr)
377 {
378     if (IsElement()) {
379         uint32_t index = GetIndex();
380         if (!receiver->GetJSHClass()->IsDictionaryElement()) {
381             JSObject::ElementsToDictionary(thread_, receiver);
382             auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject());
383             index = static_cast<uint32_t>(dict->FindEntry(JSTaggedValue(index)));
384             PropertyAttributes origin = dict->GetAttributes(index);
385             attr.SetDictionaryOrder(origin.GetDictionaryOrder());
386             dict->SetAttributes(thread_, index, attr);
387         } else {
388             auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject());
389             dict->SetAttributes(thread_, index, attr);
390         }
391         // update found result
392         UpdateFound(index, attr.GetValue(), false, true);
393     } else if (receiver->IsJSGlobalObject()) {
394         JSHandle<GlobalDictionary> dictHandle(thread_, receiver->GetProperties());
395         GlobalDictionary::InvalidatePropertyBox(thread_, dictHandle, GetIndex(), attr);
396     } else {
397         uint32_t index = GetIndex();
398         if (!receiver->GetJSHClass()->IsDictionaryMode()) {
399             JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
400             index = static_cast<uint32_t>(dict->FindEntry(key_.GetTaggedValue()));
401             PropertyAttributes origin = dict->GetAttributes(index);
402             attr.SetDictionaryOrder(origin.GetDictionaryOrder());
403             dict->SetAttributes(thread_, index, attr);
404         } else {
405             auto dict = NameDictionary::Cast(receiver->GetProperties().GetTaggedObject());
406             dict->SetAttributes(thread_, index, attr);
407         }
408         // update found result
409         UpdateFound(index, attr.GetValue(), false, true);
410     }
411 }
412 
UpdateValueAndDetails(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr,bool attrChanged)413 bool ObjectOperator::UpdateValueAndDetails(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
414                                            PropertyAttributes attr, bool attrChanged)
415 {
416     bool isInternalAccessor = IsAccessorDescriptor() && AccessorData::Cast(GetValue().GetTaggedObject())->IsInternal();
417     if (attrChanged) {
418         TransitionForAttributeChanged(receiver, attr);
419     }
420     return UpdateDataValue(receiver, value, isInternalAccessor);
421 }
422 
UpdateDataValue(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,bool isInternalAccessor,bool mayThrow)423 bool ObjectOperator::UpdateDataValue(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
424                                      bool isInternalAccessor, bool mayThrow)
425 {
426     if (IsElement()) {
427         TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
428         if (!elements->IsDictionaryMode()) {
429             if (receiver.GetTaggedValue().IsJSCOWArray()) {
430                 JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
431                 TaggedArray::Cast(JSHandle<JSArray>(receiver)->GetElements())->Set(thread_,
432                     GetIndex(), value.GetTaggedValue());
433                 return true;
434             }
435             elements->Set(thread_, GetIndex(), value.GetTaggedValue());
436             return true;
437         }
438 
439         NumberDictionary *dict = NumberDictionary::Cast(elements);
440         dict->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
441         return true;
442     }
443 
444     if (receiver->IsJSGlobalObject()) {
445         // need update cell type ?
446         auto *dict = GlobalDictionary::Cast(receiver->GetProperties().GetTaggedObject());
447         PropertyBox *cell = dict->GetBox(GetIndex());
448         cell->SetValue(thread_, value.GetTaggedValue());
449         return true;
450     }
451 
452     if (isInternalAccessor) {
453         auto accessor = AccessorData::Cast(GetValue().GetTaggedObject());
454         if (accessor->HasSetter()) {
455             bool res = accessor->CallInternalSet(thread_, JSHandle<JSObject>(receiver), value, mayThrow);
456             if (receiver->GetJSHClass()->IsDictionaryMode()) {
457                 SetIsInlinedProps(false);
458             }
459             return res;
460         }
461     }
462 
463     TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject());
464     if (!properties->IsDictionaryMode()) {
465         PropertyAttributes attr = GetAttr();
466         if (attr.IsInlinedProps()) {
467             receiver->SetPropertyInlinedProps(thread_, GetIndex(), value.GetTaggedValue());
468         } else {
469             if (receiver.GetTaggedValue().IsJSCOWArray()) {
470                 JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
471                 TaggedArray::Cast(JSHandle<JSArray>(receiver)->GetProperties())->Set(thread_,
472                     GetIndex(), value.GetTaggedValue());
473                 return true;
474             }
475             properties->Set(thread_, GetIndex(), value.GetTaggedValue());
476         }
477     } else {
478         NameDictionary::Cast(properties)->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
479     }
480     return true;
481 }
482 
WriteDataProperty(const JSHandle<JSObject> & receiver,const PropertyDescriptor & desc)483 bool ObjectOperator::WriteDataProperty(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc)
484 {
485     PropertyAttributes attr = GetAttr();
486     bool attrChanged = false;
487 
488     // composed new attribute from desc
489     if (desc.HasConfigurable() && attr.IsConfigurable() != desc.IsConfigurable()) {
490         attr.SetConfigurable(desc.IsConfigurable());
491         attrChanged = true;
492     }
493     if (desc.HasEnumerable() && attr.IsEnumerable() != desc.IsEnumerable()) {
494         attr.SetEnumerable(desc.IsEnumerable());
495         attrChanged = true;
496     }
497 
498     if (!desc.IsAccessorDescriptor()) {
499         if (desc.HasWritable() && attr.IsWritable() != desc.IsWritable()) {
500             attr.SetWritable(desc.IsWritable());
501             attrChanged = true;
502         }
503         if (!desc.HasValue()) {
504             if (attrChanged) {
505                 TransitionForAttributeChanged(receiver, attr);
506             }
507             return true;
508         }
509 
510         if (IsAccessorDescriptor()) {
511             auto accessor = AccessorData::Cast(GetValue().GetTaggedObject());
512             if (!accessor->IsInternal() || !accessor->HasSetter()) {
513                 attr.SetIsAccessor(false);
514                 attrChanged = true;
515             }
516         }
517 
518         return UpdateValueAndDetails(receiver, desc.GetValue(), attr, attrChanged);
519     } else {
520         if (IsAccessorDescriptor() && !IsElement()) {
521             TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject());
522             if (attrChanged && !properties->IsDictionaryMode()) {
523                 // as some accessorData is in globalEnv, we need to new accessorData.
524                 JSHandle<AccessorData> accessor = thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
525 
526                 if (desc.HasGetter()) {
527                     accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
528                 } else {
529                     accessor->SetGetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetGetter());
530                 }
531                 if (desc.HasSetter()) {
532                     accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
533                 } else {
534                     accessor->SetSetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetSetter());
535                 }
536 
537                 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
538                 int entry = dict->FindEntry(key_.GetTaggedValue());
539                 ASSERT(entry != -1);
540                 dict->UpdateValueAndAttributes(thread_, entry, accessor.GetTaggedValue(), attr);
541                 return true;
542             }
543         }
544 
545         JSHandle<AccessorData> accessor =
546             (IsAccessorDescriptor() && !JSHandle<AccessorData>::Cast(value_)->IsInternal()) ?
547             JSHandle<AccessorData>::Cast(value_) :
548             thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
549         if (desc.HasGetter()) {
550             accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
551         }
552 
553         if (desc.HasSetter()) {
554             accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
555         }
556 
557         if (!IsAccessorDescriptor()) {
558             attr.SetIsAccessor(true);
559             attrChanged = true;
560         }
561 
562         JSHandle<JSTaggedValue> value = JSHandle<JSTaggedValue>::Cast(accessor);
563         return UpdateValueAndDetails(receiver, value, attr, attrChanged);
564     }
565 }
566 
DeletePropertyInHolder()567 void ObjectOperator::DeletePropertyInHolder()
568 {
569     if (IsElement()) {
570         return DeleteElementInHolder();
571     }
572 
573     JSObject::DeletePropertyInternal(thread_, JSHandle<JSObject>(holder_), key_, GetIndex());
574 }
575 
AddProperty(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr)576 bool ObjectOperator::AddProperty(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
577                                  PropertyAttributes attr)
578 {
579     if (IsElement()) {
580         bool ret = JSObject::AddElementInternal(thread_, receiver, elementIndex_, value, attr);
581         bool isDict = receiver->GetJSHClass()->IsDictionaryElement();
582         SetFound(elementIndex_, value.GetTaggedValue(), attr.GetValue(), !isDict);
583         return ret;
584     }
585 
586     ResetStateForAddProperty();
587     receiver_.Update(receiver.GetTaggedValue());
588     SetAttr(attr.GetValue());
589     AddPropertyInternal(value);
590     return true;
591 }
592 
WriteElement(const JSHandle<JSObject> & receiver,JSTaggedValue value) const593 void ObjectOperator::WriteElement(const JSHandle<JSObject> &receiver, JSTaggedValue value) const
594 {
595     ASSERT(IsElement() && GetIndex() < JSObject::MAX_ELEMENT_INDEX);
596 
597     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
598     if (!elements->IsDictionaryMode()) {
599         elements->Set(thread_, index_, value);
600         receiver->GetJSHClass()->UpdateRepresentation(value);
601         return;
602     }
603 
604     NumberDictionary *dictionary = NumberDictionary::Cast(elements);
605     dictionary->UpdateValue(thread_, GetIndex(), value);
606 }
607 
DeleteElementInHolder() const608 void ObjectOperator::DeleteElementInHolder() const
609 {
610     JSHandle<JSObject> obj(holder_);
611 
612     TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
613     if (!elements->IsDictionaryMode()) {
614         elements->Set(thread_, index_, JSTaggedValue::Hole());
615         JSObject::ElementsToDictionary(thread_, JSHandle<JSObject>(holder_));
616     } else {
617         JSHandle<NumberDictionary> dictHandle(thread_, elements);
618         JSHandle<NumberDictionary> newDict = NumberDictionary::Remove(thread_, dictHandle, GetIndex());
619         obj->SetElements(thread_, newDict);
620     }
621 }
622 
SetFound(uint32_t index,JSTaggedValue value,uint32_t attr,bool mode,bool transition)623 void ObjectOperator::SetFound(uint32_t index, JSTaggedValue value, uint32_t attr, bool mode, bool transition)
624 {
625     SetIndex(index);
626     SetValue(value);
627     SetFastMode(mode);
628     SetIsTransition(transition);
629     SetAttr(attr);
630 }
631 
UpdateFound(uint32_t index,uint32_t attr,bool mode,bool transition)632 void ObjectOperator::UpdateFound(uint32_t index, uint32_t attr, bool mode, bool transition)
633 {
634     SetIndex(index);
635     SetFastMode(mode);
636     SetIsTransition(transition);
637     SetAttr(attr);
638 }
639 
ResetState()640 void ObjectOperator::ResetState()
641 {
642     // index may used by element
643     SetIndex(NOT_FOUND_INDEX);
644     SetValue(JSTaggedValue::Undefined());
645     SetFastMode(false);
646     SetAttr(0);
647     SetIsOnPrototype(false);
648     SetHasReceiver(false);
649     SetIsTSHClass(false);
650 }
651 
ResetStateForAddProperty()652 void ObjectOperator::ResetStateForAddProperty()
653 {
654     bool isOnPrototype = IsOnPrototype();
655     ResetState();
656     SetIsOnPrototype(isOnPrototype);
657 }
658 
LookupElementInlinedProps(const JSHandle<JSObject> & obj)659 void ObjectOperator::LookupElementInlinedProps(const JSHandle<JSObject> &obj)
660 {
661     // if is js string, do special.
662     if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(obj.GetTaggedValue().GetTaggedObject())->IsString()) {
663         PropertyDescriptor desc(thread_);
664         bool status = JSPrimitiveRef::StringGetIndexProperty(thread_, obj, elementIndex_, &desc);
665         if (status) {
666             PropertyAttributes attr(desc);
667             SetFound(elementIndex_, desc.GetValue().GetTaggedValue(), attr.GetValue(), true);
668             return;
669         }
670     }
671     {
672         DISALLOW_GARBAGE_COLLECTION;
673         TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
674         if (elements->GetLength() == 0) {
675             return;  // Empty Array
676         }
677 
678         if (!elements->IsDictionaryMode()) {
679             if (elements->GetLength() <= elementIndex_) {
680                 return;
681             }
682 
683             JSTaggedValue value = elements->Get(elementIndex_);
684             if (value.IsHole()) {
685                 return;
686             }
687             SetFound(elementIndex_, value, PropertyAttributes::GetDefaultAttributes(), true);
688         } else {
689             NumberDictionary *dictionary = NumberDictionary::Cast(obj->GetElements().GetTaggedObject());
690             JSTaggedValue key(static_cast<int>(elementIndex_));
691             int entry = dictionary->FindEntry(key);
692             if (entry == -1) {
693                 return;
694             }
695 
696             uint32_t attr = dictionary->GetAttributes(entry).GetValue();
697             SetFound(entry, dictionary->GetValue(entry), attr, false);
698         }
699     }
700 }
701 
AddPropertyInternal(const JSHandle<JSTaggedValue> & value)702 void ObjectOperator::AddPropertyInternal(const JSHandle<JSTaggedValue> &value)
703 {
704     ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
705     JSHandle<JSObject> obj(GetReceiver());
706     PropertyAttributes attr = GetAttr();
707     if (obj->IsJSGlobalObject()) {
708         JSMutableHandle<GlobalDictionary> dict(thread_, obj->GetProperties());
709         if (dict->GetLength() == 0) {
710             dict.Update(GlobalDictionary::Create(thread_));
711         }
712 
713         // Add PropertyBox to global dictionary
714         JSHandle<PropertyBox> cellHandle = factory->NewPropertyBox(key_);
715         cellHandle->SetValue(thread_, value.GetTaggedValue());
716         PropertyBoxType cellType = value->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT;
717         attr.SetBoxType(cellType);
718 
719         JSHandle<GlobalDictionary> properties =
720             GlobalDictionary::PutIfAbsent(thread_, dict, key_, JSHandle<JSTaggedValue>(cellHandle), attr);
721         obj->SetProperties(thread_, properties);
722         // index and fastMode is not essential for global obj;
723         SetFound(0, cellHandle.GetTaggedValue(), attr.GetValue(), true);
724         return;
725     }
726 
727     // The property has already existed whose value is hole, initialized by speculative hclass.
728     // Not need AddProperty,just SetProperty
729     if (receiverHoleEntry_ != -1) {
730         auto *hclass = receiver_->GetTaggedObject()->GetClass();
731         LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
732         attr = layoutInfo->GetAttr(receiverHoleEntry_);
733         JSObject::Cast(receiver_.GetTaggedValue())->SetProperty(thread_, hclass, attr, value.GetTaggedValue());
734         uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
735                 attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
736         SetIsTSHClass(true);
737         SetIsOnPrototype(false);
738         SetFound(index, value.GetTaggedValue(), attr.GetValue(), true);
739         return;
740     }
741 
742     attr = ObjectFastOperator::AddPropertyByName(thread_, obj, key_, value, attr);
743     if (obj->GetJSHClass()->IsDictionaryMode()) {
744         SetFound(0, value.GetTaggedValue(), attr.GetValue(), false);
745     } else {
746         uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
747                 attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
748         SetFound(index, value.GetTaggedValue(), attr.GetValue(), true, true);
749     }
750 }
751 
DefineSetter(const JSHandle<JSTaggedValue> & value)752 void ObjectOperator::DefineSetter(const JSHandle<JSTaggedValue> &value)
753 {
754     ASSERT(IsAccessorDescriptor());
755     JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
756     accessor->SetSetter(thread_, value.GetTaggedValue());
757     UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
758 }
759 
DefineGetter(const JSHandle<JSTaggedValue> & value)760 void ObjectOperator::DefineGetter(const JSHandle<JSTaggedValue> &value)
761 {
762     ASSERT(IsAccessorDescriptor());
763     JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
764     accessor->SetGetter(thread_, value.GetTaggedValue());
765     UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
766 }
767 }  // namespace panda::ecmascript
768