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