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