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/element_accessor-inl.h"
21 #include "ecmascript/global_dictionary-inl.h"
22 #include "ecmascript/global_dictionary.h"
23 #include "ecmascript/global_env.h"
24 #include "ecmascript/ic/property_box.h"
25 #include "ecmascript/js_array.h"
26 #include "ecmascript/js_function.h"
27 #include "ecmascript/js_hclass-inl.h"
28 #include "ecmascript/js_object-inl.h"
29 #include "ecmascript/js_primitive_ref.h"
30 #include "ecmascript/layout_info.h"
31 #include "ecmascript/mem/c_string.h"
32 #include "ecmascript/object_factory.h"
33 #include "ecmascript/object_fast_operator-inl.h"
34 #include "ecmascript/property_attributes.h"
35 #include "ecmascript/property_detector-inl.h"
36 #include "ecmascript/tagged_dictionary.h"
37
38 namespace panda::ecmascript {
HandleKey(const JSHandle<JSTaggedValue> & key)39 void ObjectOperator::HandleKey(const JSHandle<JSTaggedValue> &key)
40 {
41 if (key->IsInt()) {
42 int32_t keyInt = key->GetInt();
43 if (keyInt >= 0) {
44 elementIndex_ = static_cast<uint32_t>(keyInt);
45 return;
46 }
47 key_ = JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread_, JSTaggedValue(keyInt)));
48 return;
49 }
50
51 if (key->IsString()) {
52 keyFromStringType_ = true;
53 uint32_t index = 0;
54 if (JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index)) {
55 ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
56 elementIndex_ = index;
57 return;
58 }
59 if (EcmaStringAccessor(key->GetTaggedObject()).IsInternString()) {
60 key_ = key;
61 return;
62 }
63 key_ = JSHandle<JSTaggedValue>(thread_, thread_->GetEcmaVM()->GetFactory()->InternString(key));
64 return;
65 }
66
67 if (key->IsDouble()) {
68 double number = key->GetDouble();
69 if (number >= 0 && number < JSObject::MAX_ELEMENT_INDEX) {
70 auto integer = static_cast<uint32_t>(number);
71 if (integer == number) {
72 elementIndex_ = static_cast<uint32_t>(number);
73 return;
74 }
75 }
76 key_ = JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread_, key.GetTaggedValue()));
77 return;
78 }
79
80 if (key->IsSymbol()) {
81 key_ = key;
82 return;
83 }
84
85 JSHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::ToPrimitive(thread_, key, PREFER_STRING));
86 if (key->IsSymbol()) {
87 key_ = keyHandle;
88 return;
89 }
90 key_ = JSHandle<JSTaggedValue>(thread_,
91 thread_->GetEcmaVM()->GetFactory()->InternString(
92 JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread_, keyHandle))));
93 }
94
UpdateHolder()95 void ObjectOperator::UpdateHolder()
96 {
97 if (holder_->IsString() && (GetThroughElement() || GetStringLength())) {
98 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
99 holder_.Update(JSPrimitiveRef::StringCreate(thread_, holder_, undefined).GetTaggedValue());
100 } else {
101 holder_.Update(JSTaggedValue::ToPrototypeOrObj(thread_, holder_).GetTaggedValue());
102 }
103 }
104
UpdateIsTSHClass()105 void ObjectOperator::UpdateIsTSHClass()
106 {
107 if (!holder_->IsECMAObject()) {
108 SetIsTSHClass(false);
109 return;
110 }
111 auto hclass = JSHandle<JSObject>::Cast(holder_)->GetClass();
112 if (hclass->IsTS()) {
113 SetIsTSHClass(true);
114 }
115 }
116
StartLookUp(OperatorType type)117 void ObjectOperator::StartLookUp(OperatorType type)
118 {
119 UpdateHolder();
120
121 if (type == OperatorType::OWN) {
122 LookupPropertyInHolder();
123 } else {
124 LookupProperty();
125 }
126 }
127
StartGlobalLookUp(OperatorType type)128 void ObjectOperator::StartGlobalLookUp(OperatorType type)
129 {
130 UpdateHolder();
131
132 if (type == OperatorType::OWN) {
133 GlobalLookupPropertyInHolder();
134 } else {
135 GlobalLookupProperty();
136 }
137 }
138
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & key,OperatorType type)139 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &key, OperatorType type)
140 : thread_(thread),
141 holder_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()),
142 receiver_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject())
143 {
144 HandleKey(key);
145 StartGlobalLookUp(type);
146 }
147
ObjectOperator(JSThread * thread,const JSHandle<JSObject> & holder,const JSHandle<JSTaggedValue> & key,OperatorType type)148 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSObject> &holder, const JSHandle<JSTaggedValue> &key,
149 OperatorType type)
150 : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue())
151 {
152 HandleKey(key);
153 StartLookUp(type);
154 }
155
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,const JSHandle<JSTaggedValue> & key,OperatorType type)156 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
157 const JSHandle<JSTaggedValue> &key, OperatorType type)
158 : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue())
159 {
160 HandleKey(key);
161 StartLookUp(type);
162 }
163
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,uint32_t index,OperatorType type)164 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, uint32_t index,
165 OperatorType type)
166 : thread_(thread),
167 holder_(thread, holder.GetTaggedValue()),
168 receiver_(thread, holder.GetTaggedValue()),
169 elementIndex_(index)
170 {
171 StartLookUp(type);
172 }
173
ObjectOperator(JSThread * thread,const JSHandle<JSTaggedValue> & holder,const JSHandle<JSTaggedValue> & receiver,const JSHandle<JSTaggedValue> & key,OperatorType type)174 ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
175 const JSHandle<JSTaggedValue> &receiver, const JSHandle<JSTaggedValue> &key,
176 OperatorType type)
177 : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, receiver.GetTaggedValue())
178 {
179 SetHasReceiver(true);
180 HandleKey(key);
181 StartLookUp(type);
182 }
183
184 // op for fast path
ObjectOperator(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,OperatorType type)185 ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
186 OperatorType type)
187 : thread_(thread), holder_(thread, receiver), receiver_(thread, receiver), key_(thread, name)
188 {
189 ASSERT(name.IsStringOrSymbol());
190 StartLookUp(type);
191 }
FastGetValue()192 JSHandle<JSTaggedValue> ObjectOperator::FastGetValue()
193 {
194 ASSERT(IsFound() && !value_.IsEmpty());
195 if (value_->IsPropertyBox()) {
196 value_.Update(PropertyBox::Cast(value_->GetTaggedObject())->GetValue());
197 }
198 if (!IsAccessorDescriptor()) {
199 return value_;
200 }
201 AccessorData *accessor = AccessorData::Cast(value_->GetTaggedObject());
202 ASSERT(!accessor->IsInternal());
203 // 8. Return Call(getter, Receiver).
204 return JSHandle<JSTaggedValue>(thread_, JSObject::CallGetter(thread_, accessor, receiver_));
205 }
ObjectOperator(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,const PropertyAttributes & attr)206 ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
207 const PropertyAttributes &attr)
208 : thread_(thread), receiver_(thread, receiver), key_(thread, name)
209 {
210 SetAttr(attr);
211 }
FastAdd(JSThread * thread,const JSTaggedValue & receiver,const JSTaggedValue & name,const JSHandle<JSTaggedValue> & value,const PropertyAttributes & attr)212 void ObjectOperator::FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
213 const JSHandle<JSTaggedValue> &value, const PropertyAttributes &attr)
214 {
215 ObjectOperator op(thread, receiver, name, attr);
216 op.AddPropertyInternal(value);
217 }
218
219 // static
UpdateDetectorOnSetPrototype(const JSThread * thread,JSTaggedValue receiver)220 void ObjectOperator::UpdateDetectorOnSetPrototype(const JSThread *thread, JSTaggedValue receiver)
221 {
222 // skip env prepare
223 if (!thread->IsReadyToUpdateDetector()) {
224 return;
225 }
226 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
227 JSHClass *hclass = receiver.GetTaggedObject()->GetClass();
228 JSType type = hclass->GetObjectType();
229 switch (type) {
230 case JSType::JS_REG_EXP: {
231 if (PropertyDetector::IsRegExpReplaceDetectorValid(env)) {
232 PropertyDetector::InvalidateRegExpReplaceDetector(env);
233 }
234 if (PropertyDetector::IsRegExpSplitDetectorValid(env)) {
235 PropertyDetector::InvalidateRegExpSplitDetector(env);
236 }
237 return;
238 }
239 case JSType::JS_MAP: {
240 if (PropertyDetector::IsMapIteratorDetectorValid(env)) {
241 PropertyDetector::InvalidateMapIteratorDetector(env);
242 }
243 return;
244 }
245 case JSType::JS_SET: {
246 if (PropertyDetector::IsSetIteratorDetectorValid(env)) {
247 PropertyDetector::InvalidateSetIteratorDetector(env);
248 }
249 return;
250 }
251 case JSType::JS_PRIMITIVE_REF: {
252 if (JSPrimitiveRef::Cast(receiver.GetTaggedObject())->IsString() &&
253 PropertyDetector::IsStringIteratorDetectorValid(env)) {
254 PropertyDetector::InvalidateStringIteratorDetector(env);
255 }
256 return;
257 }
258 case JSType::JS_ARRAY: {
259 if (PropertyDetector::IsArrayIteratorDetectorValid(env)) {
260 PropertyDetector::InvalidateArrayIteratorDetector(env);
261 }
262 return;
263 }
264 case JSType::JS_INT8_ARRAY:
265 case JSType::JS_UINT8_ARRAY:
266 case JSType::JS_UINT8_CLAMPED_ARRAY:
267 case JSType::JS_INT16_ARRAY:
268 case JSType::JS_UINT16_ARRAY:
269 case JSType::JS_INT32_ARRAY:
270 case JSType::JS_UINT32_ARRAY:
271 case JSType::JS_FLOAT32_ARRAY:
272 case JSType::JS_FLOAT64_ARRAY:
273 case JSType::JS_BIGINT64_ARRAY:
274 case JSType::JS_BIGUINT64_ARRAY: {
275 if (PropertyDetector::IsTypedArrayIteratorDetectorValid(env)) {
276 PropertyDetector::InvalidateTypedArrayIteratorDetector(env);
277 }
278 if (PropertyDetector::IsTypedArraySpeciesProtectDetectorValid(env)) {
279 PropertyDetector::InvalidateTypedArraySpeciesProtectDetector(env);
280 }
281 return;
282 }
283 default:
284 break;
285 }
286
287 if (hclass->IsPrototype() &&
288 (receiver == env->GetTaggedInt8ArrayFunctionPrototype() ||
289 receiver == env->GetTaggedUint8ArrayFunctionPrototype() ||
290 receiver == env->GetTaggedUint8ClampedArrayFunctionPrototype() ||
291 receiver == env->GetTaggedInt16ArrayFunctionPrototype() ||
292 receiver == env->GetTaggedUint16ArrayFunctionPrototype() ||
293 receiver == env->GetTaggedInt32ArrayFunctionPrototype() ||
294 receiver == env->GetTaggedUint32ArrayFunctionPrototype() ||
295 receiver == env->GetTaggedFloat32ArrayFunctionPrototype() ||
296 receiver == env->GetTaggedFloat64ArrayFunctionPrototype() ||
297 receiver == env->GetTaggedBigInt64ArrayFunctionPrototype() ||
298 receiver == env->GetTaggedBigUint64ArrayFunctionPrototype()) &&
299 PropertyDetector::IsTypedArrayIteratorDetectorValid(env)) {
300 PropertyDetector::InvalidateTypedArrayIteratorDetector(env);
301 return;
302 }
303 }
304
UpdateDetector()305 void ObjectOperator::UpdateDetector()
306 {
307 if (IsElement()) {
308 return;
309 }
310 ObjectOperator::UpdateDetector(thread_, holder_.GetTaggedValue(), key_.GetTaggedValue());
311 }
312
313 // static
UpdateDetector(const JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)314 void ObjectOperator::UpdateDetector(const JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
315 {
316 // skip env prepare
317 if (!thread->IsReadyToUpdateDetector()) {
318 return;
319 }
320 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
321 bool maybeDetector = IsDetectorName(env, key);
322 if (!maybeDetector) {
323 return;
324 }
325 // only support symbol keys now
326 ASSERT(key.IsSymbol());
327 if (key == env->GetTaggedReplaceSymbol()) {
328 if (receiver.IsJSRegExp() || receiver == env->GetTaggedRegExpPrototype()) {
329 if (!PropertyDetector::IsRegExpReplaceDetectorValid(env)) {
330 return;
331 }
332 PropertyDetector::InvalidateRegExpReplaceDetector(env);
333 }
334 } else if (key == env->GetTaggedSplitSymbol()) {
335 if (receiver.IsJSRegExp() || receiver == env->GetTaggedRegExpPrototype()) {
336 if (!PropertyDetector::IsRegExpSplitDetectorValid(env)) {
337 return;
338 }
339 PropertyDetector::InvalidateRegExpSplitDetector(env);
340 }
341 } else if (key == env->GetTaggedIteratorSymbol()) {
342 if (receiver.IsJSMap() || receiver == env->GetTaggedMapPrototype()) {
343 if (!PropertyDetector::IsMapIteratorDetectorValid(env)) {
344 return;
345 }
346 PropertyDetector::InvalidateMapIteratorDetector(env);
347 } else if (receiver.IsJSSet() || receiver == env->GetTaggedSetPrototype()) {
348 if (!PropertyDetector::IsSetIteratorDetectorValid(env)) {
349 return;
350 }
351 PropertyDetector::InvalidateSetIteratorDetector(env);
352 } else if ((receiver.IsJSPrimitiveRef() && JSPrimitiveRef::Cast(receiver.GetTaggedObject())->IsString()) ||
353 receiver == env->GetTaggedStringPrototype()) {
354 if (!PropertyDetector::IsStringIteratorDetectorValid(env)) {
355 return;
356 }
357 PropertyDetector::InvalidateStringIteratorDetector(env);
358 } else if (receiver.IsJSArray() || receiver == env->GetTaggedArrayPrototype()) {
359 if (!PropertyDetector::IsArrayIteratorDetectorValid(env)) {
360 return;
361 }
362 PropertyDetector::InvalidateArrayIteratorDetector(env);
363 } else if (receiver.IsTypedArray() ||
364 receiver == env->GetTaggedArrayPrototype() ||
365 receiver == env->GetTaggedInt8ArrayFunctionPrototype() ||
366 receiver == env->GetTaggedUint8ArrayFunctionPrototype() ||
367 receiver == env->GetTaggedUint8ClampedArrayFunctionPrototype() ||
368 receiver == env->GetTaggedInt16ArrayFunctionPrototype() ||
369 receiver == env->GetTaggedUint16ArrayFunctionPrototype() ||
370 receiver == env->GetTaggedInt32ArrayFunctionPrototype() ||
371 receiver == env->GetTaggedUint32ArrayFunctionPrototype() ||
372 receiver == env->GetTaggedFloat32ArrayFunctionPrototype() ||
373 receiver == env->GetTaggedFloat64ArrayFunctionPrototype() ||
374 receiver == env->GetTaggedBigInt64ArrayFunctionPrototype() ||
375 receiver == env->GetTaggedBigUint64ArrayFunctionPrototype()) {
376 if (!PropertyDetector::IsTypedArrayIteratorDetectorValid(env)) {
377 return;
378 }
379 PropertyDetector::InvalidateTypedArrayIteratorDetector(env);
380 }
381 } else if (key == env->GetTaggedSpeciesSymbol()) {
382 if (receiver.IsJSObject()) {
383 if (!PropertyDetector::IsTypedArraySpeciesProtectDetectorValid(env)) {
384 return;
385 }
386 PropertyDetector::InvalidateTypedArraySpeciesProtectDetector(env);
387 }
388 }
389 }
390
391 // static
IsDetectorName(JSHandle<GlobalEnv> env,JSTaggedValue key)392 bool ObjectOperator::IsDetectorName(JSHandle<GlobalEnv> env, JSTaggedValue key)
393 {
394 uintptr_t start = GlobalEnv::GetFirstDetectorSymbolAddr(*env);
395 uintptr_t end = GlobalEnv::GetLastDetectorSymbolAddr(*env);
396 uintptr_t addr = key.GetRawData();
397 return (start <= addr) && (addr <= end);
398 }
399
GetTrackType() const400 TrackType ObjectOperator::GetTrackType() const
401 {
402 return JSObject::Cast(holder_->GetTaggedObject())->GetJSHClass()->IsDictionaryMode()
403 ? attributes_.GetDictTrackType()
404 : attributes_.GetTrackType();
405 }
406
ToPropertyDescriptor(PropertyDescriptor & desc) const407 void ObjectOperator::ToPropertyDescriptor(PropertyDescriptor &desc) const
408 {
409 DISALLOW_GARBAGE_COLLECTION;
410 if (!IsFound()) {
411 return;
412 }
413
414 if (!IsAccessorDescriptor()) {
415 desc.SetWritable(IsWritable());
416 JSTaggedValue val = GetValue();
417 desc.SetValue(JSHandle<JSTaggedValue>(thread_, val));
418 desc.SetTrackType(GetTrackType());
419 } else {
420 auto result = GetValue();
421 bool isPropertyBox = result.IsPropertyBox();
422 if (isPropertyBox) {
423 result = PropertyBox::Cast(result.GetTaggedObject())->GetValue();
424 }
425 AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject());
426
427 if (UNLIKELY(accessor->IsInternal())) {
428 desc.SetWritable(IsWritable());
429 auto val = accessor->CallInternalGet(thread_, JSHandle<JSObject>::Cast(GetHolder()));
430 JSMutableHandle<JSTaggedValue> value(thread_, val);
431 if (isPropertyBox) {
432 JSHandle<PropertyBox> cell(value_);
433 cell->SetValue(thread_, val);
434 value.Update(cell);
435 }
436 desc.SetValue(value);
437 } else {
438 desc.SetGetter(JSHandle<JSTaggedValue>(thread_, accessor->GetGetter()));
439 desc.SetSetter(JSHandle<JSTaggedValue>(thread_, accessor->GetSetter()));
440 }
441 }
442
443 desc.SetEnumerable(IsEnumerable());
444 desc.SetConfigurable(IsConfigurable());
445 }
446
GlobalLookupProperty()447 void ObjectOperator::GlobalLookupProperty()
448 {
449 GlobalLookupPropertyInHolder();
450 if (IsFound()) {
451 return;
452 }
453 JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_);
454 RETURN_IF_ABRUPT_COMPLETION(thread_);
455 if (!proto.IsHeapObject()) {
456 return;
457 }
458 holder_.Update(proto);
459 if (holder_->IsJSProxy()) {
460 return;
461 }
462 SetIsOnPrototype(true);
463 LookupProperty();
464 }
465
LookupProperty()466 void ObjectOperator::LookupProperty()
467 {
468 while (true) {
469 UpdateIsTSHClass();
470 LookupPropertyInHolder();
471 if (IsFound()) {
472 return;
473 }
474
475 JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_);
476 RETURN_IF_ABRUPT_COMPLETION(thread_);
477 if (!proto.IsHeapObject()) {
478 return;
479 }
480
481 holder_.Update(proto);
482 if (holder_->IsJSProxy()) {
483 return;
484 }
485
486 SetIsOnPrototype(true);
487 }
488 }
489
LookupGlobal(const JSHandle<JSObject> & obj)490 void ObjectOperator::LookupGlobal(const JSHandle<JSObject> &obj)
491 {
492 ASSERT(obj->IsJSGlobalObject());
493 if (IsElement()) {
494 LookupElementInlinedProps(obj);
495 return;
496 }
497 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
498 if (array->GetLength() == 0) {
499 return;
500 }
501 GlobalDictionary *dict = GlobalDictionary::Cast(array);
502 int entry = dict->FindEntry(key_.GetTaggedValue());
503 if (entry == -1) {
504 return;
505 }
506 JSTaggedValue value(dict->GetBox(entry));
507 uint32_t attr = dict->GetAttributes(entry).GetValue();
508 SetFound(entry, value, attr, true);
509 }
510
LookupPropertyInlinedProps(const JSHandle<JSObject> & obj)511 void ObjectOperator::LookupPropertyInlinedProps(const JSHandle<JSObject> &obj)
512 {
513 if (IsElement()) {
514 LookupElementInlinedProps(obj);
515 return;
516 }
517
518 if (!obj.GetTaggedValue().IsJSObject()) {
519 return;
520 }
521
522 if (obj->IsJSGlobalObject()) {
523 DISALLOW_GARBAGE_COLLECTION;
524 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
525 if (array->GetLength() == 0) {
526 return;
527 }
528
529 GlobalDictionary *dict = GlobalDictionary::Cast(array);
530 int entry = dict->FindEntry(key_.GetTaggedValue());
531 if (entry == -1) {
532 return;
533 }
534
535 JSTaggedValue value(dict->GetBox(entry));
536 uint32_t attr = dict->GetAttributes(entry).GetValue();
537 SetFound(entry, value, attr, true);
538 return;
539 }
540
541 TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
542 if (!array->IsDictionaryMode()) {
543 JSHClass *jshclass = obj->GetJSHClass();
544 int entry = JSHClass::FindPropertyEntry(thread_, jshclass, key_.GetTaggedValue());
545 if (entry == -1) {
546 return;
547 }
548 JSTaggedValue attrs = jshclass->GetLayout();
549 LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject());
550 PropertyAttributes attr(layoutInfo->GetAttr(entry));
551 ASSERT(entry == static_cast<int>(attr.GetOffset()));
552 JSTaggedValue value;
553 if (attr.IsInlinedProps()) {
554 value = obj->GetPropertyInlinedPropsWithRep(entry, attr);
555 if (value.IsHole()) {
556 if (receiverHoleEntry_ == -1 && receiver_ == holder_) {
557 receiverHoleEntry_ = entry;
558 }
559 return;
560 }
561 } else {
562 entry -= static_cast<int>(jshclass->GetInlinedProperties());
563 value = array->Get(entry);
564 }
565
566 SetFound(entry, value, attr.GetValue(), true);
567 return;
568 }
569
570 NameDictionary *dict = NameDictionary::Cast(array);
571 int entry = dict->FindEntry(key_.GetTaggedValue());
572 if (entry == -1) {
573 return;
574 }
575
576 JSTaggedValue value = dict->GetValue(entry);
577 uint32_t attr = dict->GetAttributes(entry).GetValue();
578 SetFound(entry, value, attr, false);
579 }
580
TransitionForAttributeChanged(const JSHandle<JSObject> & receiver,PropertyAttributes attr)581 void ObjectOperator::TransitionForAttributeChanged(const JSHandle<JSObject> &receiver, PropertyAttributes attr)
582 {
583 if (IsElement()) {
584 uint32_t index = GetIndex();
585 if (!receiver->GetJSHClass()->IsDictionaryElement()) {
586 JSObject::ElementsToDictionary(thread_, receiver);
587 auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject());
588 index = static_cast<uint32_t>(dict->FindEntry(JSTaggedValue(index)));
589 PropertyAttributes origin = dict->GetAttributes(index);
590 attr.SetDictionaryOrder(origin.GetDictionaryOrder());
591 dict->SetAttributes(thread_, index, attr);
592 } else {
593 auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject());
594 dict->SetAttributes(thread_, index, attr);
595 }
596 // update found result
597 UpdateFound(index, attr.GetValue(), false, true);
598 } else if (receiver->IsJSGlobalObject()) {
599 JSHandle<GlobalDictionary> dictHandle(thread_, receiver->GetProperties());
600 GlobalDictionary::InvalidatePropertyBox(thread_, dictHandle, GetIndex());
601 } else {
602 uint32_t index = GetIndex();
603 if (!receiver->GetJSHClass()->IsDictionaryMode()) {
604 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
605 index = static_cast<uint32_t>(dict->FindEntry(key_.GetTaggedValue()));
606 PropertyAttributes origin = dict->GetAttributes(index);
607 attr.SetDictTrackType(attr.GetTrackType());
608 attr.SetDictionaryOrder(origin.GetDictionaryOrder());
609 dict->SetAttributes(thread_, index, attr);
610 } else {
611 auto dict = NameDictionary::Cast(receiver->GetProperties().GetTaggedObject());
612 dict->SetAttributes(thread_, index, attr);
613 }
614 // update found result
615 UpdateFound(index, attr.GetValue(), false, true);
616 }
617 }
618
UpdateValueAndDetails(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr,bool attrChanged)619 bool ObjectOperator::UpdateValueAndDetails(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
620 PropertyAttributes attr, bool attrChanged)
621 {
622 auto valueAccessor = GetValue();
623 if (valueAccessor.IsPropertyBox()) {
624 valueAccessor = PropertyBox::Cast(valueAccessor.GetTaggedObject())->GetValue();
625 }
626 bool isInternalAccessor = IsAccessorDescriptor()
627 && AccessorData::Cast(valueAccessor.GetTaggedObject())->IsInternal();
628 if (!attrChanged) {
629 return UpdateDataValue(receiver, value, isInternalAccessor);
630 }
631 if (attr.IsWritable()) {
632 TransitionForAttributeChanged(receiver, attr);
633 return UpdateDataValue(receiver, value, isInternalAccessor);
634 }
635 bool res = UpdateDataValue(receiver, value, isInternalAccessor);
636 if (res) {
637 TransitionForAttributeChanged(receiver, attr);
638 }
639 return res;
640 }
641
UpdateDataValue(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,bool isInternalAccessor,bool mayThrow)642 bool ObjectOperator::UpdateDataValue(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
643 bool isInternalAccessor, bool mayThrow)
644 {
645 if (IsElement()) {
646 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
647 if (!elements->IsDictionaryMode()) {
648 if (receiver.GetTaggedValue().IsJSCOWArray()) {
649 JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
650 } else if (receiver->IsTypedArray()) {
651 JSTaggedValue holder = receiver.GetTaggedValue();
652 JSType jsType = holder.GetTaggedObject()->GetClass()->GetObjectType();
653 JSTaggedValue typedArrayProperty = JSTypedArray::FastSetPropertyByIndex(thread_,
654 receiver.GetTaggedValue(), GetIndex(), value.GetTaggedValue(), jsType);
655 if (typedArrayProperty.IsHole()) {
656 return false;
657 }
658 return true;
659 }
660 ElementsKind oldKind = receiver->GetClass()->GetElementsKind();
661 if (JSHClass::TransitToElementsKind(thread_, receiver, value)) {
662 SetIsTransition(true);
663 ElementsKind newKind = receiver->GetClass()->GetElementsKind();
664 // newKind != currentKind, we need to convert the whole array to the newKind.
665 Elements::MigrateArrayWithKind(thread_, receiver, oldKind, newKind);
666 }
667 ElementAccessor::Set(thread_, receiver, GetIndex(), value, false);
668 return true;
669 }
670
671 NumberDictionary *dict = NumberDictionary::Cast(elements);
672 dict->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
673 return true;
674 }
675
676 if (receiver->IsJSGlobalObject()) {
677 // need update cell type ?
678 auto *dict = GlobalDictionary::Cast(receiver->GetProperties().GetTaggedObject());
679 if (isInternalAccessor && !value->IsAccessor()) {
680 PropertyAttributes attr = dict->GetAttributes(GetIndex());
681 attr.SetIsAccessor(false);
682 dict->SetAttributes(thread_, GetIndex(), attr);
683 }
684 PropertyBox *cell = dict->GetBox(GetIndex());
685 cell->SetValue(thread_, value.GetTaggedValue());
686 return true;
687 }
688
689 if (isInternalAccessor) {
690 auto accessor = AccessorData::Cast(GetValue().GetTaggedObject());
691 if (accessor->HasSetter()) {
692 bool res = accessor->CallInternalSet(thread_, JSHandle<JSObject>(receiver), value, mayThrow);
693 if (receiver->GetJSHClass()->IsDictionaryMode()) {
694 SetIsInlinedProps(false);
695 SetFastMode(false);
696 }
697 return res;
698 }
699 }
700
701 JSMutableHandle<TaggedArray> properties(thread_, TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()));
702 if (!properties->IsDictionaryMode()) {
703 PropertyAttributes attr = GetAttr();
704 uint32_t offset = index_;
705 if (!attr.IsInlinedProps()) {
706 auto *hclass = receiver_->GetTaggedObject()->GetClass();
707 offset += hclass->GetInlinedProperties();
708 }
709 attr.SetOffset(offset);
710
711 auto actualValue =
712 JSHClass::ConvertOrTransitionWithRep(thread_, JSHandle<JSObject>(receiver_), key_, value, attr);
713 if (std::get<1>(actualValue)) {
714 SetIsTransition(true);
715 }
716 attributes_.SetRepresentation(attr.GetRepresentation());
717
718 if (attr.IsInlinedProps()) {
719 receiver->SetPropertyInlinedPropsWithRep(thread_, GetIndex(), std::get<2>(actualValue));
720 } else {
721 if (receiver.GetTaggedValue().IsJSCOWArray()) {
722 JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
723 properties.Update(JSHandle<JSArray>(receiver)->GetProperties());
724 }
725 if (std::get<0>(actualValue)) {
726 properties->Set<true>(thread_, GetIndex(), std::get<2>(actualValue));
727 } else {
728 properties->Set<false>(thread_, GetIndex(), std::get<2>(actualValue));
729 }
730 }
731 } else {
732 properties.GetObject<NameDictionary>()->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
733 }
734 return true;
735 }
736
WriteDataProperty(const JSHandle<JSObject> & receiver,const PropertyDescriptor & desc)737 bool ObjectOperator::WriteDataProperty(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc)
738 {
739 PropertyAttributes attr = GetAttr();
740 bool attrChanged = false;
741
742 // composed new attribute from desc
743 if (desc.HasConfigurable() && attr.IsConfigurable() != desc.IsConfigurable()) {
744 attr.SetConfigurable(desc.IsConfigurable());
745 attrChanged = true;
746 }
747 if (desc.HasEnumerable() && attr.IsEnumerable() != desc.IsEnumerable()) {
748 attr.SetEnumerable(desc.IsEnumerable());
749 attrChanged = true;
750 }
751
752 if (!desc.IsAccessorDescriptor()) {
753 if (desc.HasWritable() && attr.IsWritable() != desc.IsWritable()) {
754 attr.SetWritable(desc.IsWritable());
755 attrChanged = true;
756 }
757 if (!desc.HasValue()) {
758 if (attrChanged) {
759 TransitionForAttributeChanged(receiver, attr);
760 }
761 return true;
762 }
763
764 if (IsAccessorDescriptor()) {
765 auto accessor = AccessorData::Cast(GetValue().GetTaggedObject());
766 if (!accessor->IsInternal() || !accessor->HasSetter()) {
767 attr.SetIsAccessor(false);
768 attrChanged = true;
769 }
770 }
771
772 return UpdateValueAndDetails(receiver, desc.GetValue(), attr, attrChanged);
773 } else {
774 if (IsAccessorDescriptor() && !IsElement()) {
775 TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject());
776 if (attrChanged && !properties->IsDictionaryMode()) {
777 // as some accessorData is in globalEnv, we need to new accessorData.
778 JSHandle<AccessorData> accessor = thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
779
780 if (desc.HasGetter()) {
781 accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
782 } else {
783 accessor->SetGetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetGetter());
784 }
785 if (desc.HasSetter()) {
786 accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
787 } else {
788 accessor->SetSetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetSetter());
789 }
790
791 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
792 int entry = dict->FindEntry(key_.GetTaggedValue());
793 ASSERT(entry != -1);
794 dict->UpdateValueAndAttributes(thread_, entry, accessor.GetTaggedValue(), attr);
795 return true;
796 }
797 }
798
799 auto valueAccessor = GetValue();
800 if (valueAccessor.IsPropertyBox()) {
801 valueAccessor = PropertyBox::Cast(valueAccessor.GetTaggedObject())->GetValue();
802 }
803 JSHandle<AccessorData> accessor =
804 (IsAccessorDescriptor() && !JSHandle<AccessorData>(thread_, valueAccessor)->IsInternal()) ?
805 JSHandle<AccessorData>(thread_, valueAccessor) :
806 thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
807 if (desc.HasGetter()) {
808 accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
809 }
810
811 if (desc.HasSetter()) {
812 accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
813 }
814
815 if (!IsAccessorDescriptor()) {
816 attr.SetIsAccessor(true);
817 attrChanged = true;
818 }
819
820 JSHandle<JSTaggedValue> value = JSHandle<JSTaggedValue>::Cast(accessor);
821 bool success = UpdateValueAndDetails(receiver, value, attr, attrChanged);
822 if (success) {
823 JSHandle<JSObject> obj(receiver);
824 if (obj->GetJSHClass()->IsPrototype()) {
825 JSHandle<ProtoChangeMarker> markerHandle = thread_->GetEcmaVM()->GetFactory()->NewProtoChangeMarker();
826 obj->GetJSHClass()->SetProtoChangeMarker(thread_, markerHandle.GetTaggedValue());
827 }
828 JSHClass::NotifyAccessorChanged(thread_, JSHandle<JSHClass>(thread_, obj->GetJSHClass()));
829 }
830 return success;
831 }
832 }
833
DeletePropertyInHolder()834 void ObjectOperator::DeletePropertyInHolder()
835 {
836 if (IsElement()) {
837 return DeleteElementInHolder();
838 }
839 ObjectOperator::UpdateDetector(thread_, holder_.GetTaggedValue(), key_.GetTaggedValue());
840 JSObject::DeletePropertyInternal(thread_, JSHandle<JSObject>(holder_), key_, GetIndex());
841 }
842
AddProperty(const JSHandle<JSObject> & receiver,const JSHandle<JSTaggedValue> & value,PropertyAttributes attr)843 bool ObjectOperator::AddProperty(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
844 PropertyAttributes attr)
845 {
846 if (IsElement()) {
847 bool ret = JSObject::AddElementInternal(thread_, receiver, elementIndex_, value, attr);
848 bool isDict = receiver->GetJSHClass()->IsDictionaryElement();
849 SetFound(elementIndex_, value.GetTaggedValue(), attr.GetValue(), !isDict);
850 return ret;
851 }
852
853 ResetStateForAddProperty();
854 receiver_.Update(receiver.GetTaggedValue());
855 SetAttr(attr.GetValue());
856 AddPropertyInternal(value);
857 return true;
858 }
859
WriteElement(const JSHandle<JSObject> & receiver,JSTaggedValue value) const860 void ObjectOperator::WriteElement(const JSHandle<JSObject> &receiver, JSTaggedValue value) const
861 {
862 ASSERT(IsElement() && GetIndex() < JSObject::MAX_ELEMENT_INDEX);
863
864 if (!ElementAccessor::IsDictionaryMode(receiver)) {
865 ElementAccessor::Set(thread_, receiver, index_, value, true);
866 return;
867 }
868
869 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
870 NumberDictionary *dictionary = NumberDictionary::Cast(elements);
871 dictionary->UpdateValue(thread_, GetIndex(), value);
872 }
873
DeleteElementInHolder() const874 void ObjectOperator::DeleteElementInHolder() const
875 {
876 JSHandle<JSObject> obj(holder_);
877
878 if (!ElementAccessor::IsDictionaryMode(obj)) {
879 ElementAccessor::Set(thread_, obj, index_, JSTaggedValue::Hole(), true, ElementsKind::HOLE);
880 JSObject::ElementsToDictionary(thread_, JSHandle<JSObject>(holder_));
881 } else {
882 TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
883 JSHandle<NumberDictionary> dictHandle(thread_, elements);
884 JSHandle<NumberDictionary> newDict = NumberDictionary::Remove(thread_, dictHandle, GetIndex());
885 obj->SetElements(thread_, newDict);
886 }
887 }
888
SetFound(uint32_t index,JSTaggedValue value,uint32_t attr,bool mode,bool transition)889 void ObjectOperator::SetFound(uint32_t index, JSTaggedValue value, uint32_t attr, bool mode, bool transition)
890 {
891 SetIndex(index);
892 SetValue(value);
893 SetFastMode(mode);
894 SetIsTransition(transition);
895 SetAttr(attr);
896 }
897
UpdateFound(uint32_t index,uint32_t attr,bool mode,bool transition)898 void ObjectOperator::UpdateFound(uint32_t index, uint32_t attr, bool mode, bool transition)
899 {
900 SetIndex(index);
901 SetFastMode(mode);
902 SetIsTransition(transition);
903 SetAttr(attr);
904 }
905
ResetState()906 void ObjectOperator::ResetState()
907 {
908 // index may used by element
909 SetIndex(NOT_FOUND_INDEX);
910 SetValue(JSTaggedValue::Undefined());
911 SetFastMode(false);
912 SetAttr(0);
913 SetIsOnPrototype(false);
914 SetHasReceiver(false);
915 SetIsTSHClass(false);
916 }
917
ResetStateForAddProperty()918 void ObjectOperator::ResetStateForAddProperty()
919 {
920 bool isOnPrototype = IsOnPrototype();
921 ResetState();
922 SetIsOnPrototype(isOnPrototype);
923 }
924
LookupElementInlinedProps(const JSHandle<JSObject> & obj)925 void ObjectOperator::LookupElementInlinedProps(const JSHandle<JSObject> &obj)
926 {
927 // if is js string, do special.
928 if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(obj.GetTaggedValue().GetTaggedObject())->IsString()) {
929 PropertyDescriptor desc(thread_);
930 bool status = JSPrimitiveRef::StringGetIndexProperty(thread_, obj, elementIndex_, &desc);
931 if (status) {
932 PropertyAttributes attr(desc);
933 SetFound(elementIndex_, desc.GetValue().GetTaggedValue(), attr.GetValue(), true);
934 return;
935 }
936 }
937 {
938 DISALLOW_GARBAGE_COLLECTION;
939 if (obj->IsTypedArray()) {
940 JSTaggedValue val = JSTypedArray::FastElementGet(thread_,
941 JSHandle<JSTaggedValue>::Cast(obj), elementIndex_).GetValue().GetTaggedValue();
942 if (!val.IsHole()) {
943 SetFound(elementIndex_, val, PropertyAttributes::GetDefaultAttributes(), true);
944 }
945 return;
946 }
947 TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
948 if (elements->GetLength() == 0) {
949 return; // Empty Array
950 }
951
952 if (!elements->IsDictionaryMode()) {
953 if (elements->GetLength() <= elementIndex_) {
954 return;
955 }
956
957 JSTaggedValue value = ElementAccessor::Get(obj, elementIndex_);
958 if (value.IsHole()) {
959 return;
960 }
961 SetFound(elementIndex_, value, PropertyAttributes::GetDefaultAttributes(), true);
962 } else {
963 NumberDictionary *dictionary = NumberDictionary::Cast(obj->GetElements().GetTaggedObject());
964 JSTaggedValue key(static_cast<int>(elementIndex_));
965 int entry = dictionary->FindEntry(key);
966 if (entry == -1) {
967 return;
968 }
969
970 uint32_t attr = dictionary->GetAttributes(entry).GetValue();
971 SetFound(entry, dictionary->GetValue(entry), attr, false);
972 }
973 }
974 }
975
AddPropertyInternal(const JSHandle<JSTaggedValue> & value)976 void ObjectOperator::AddPropertyInternal(const JSHandle<JSTaggedValue> &value)
977 {
978 ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
979 JSHandle<JSObject> obj(GetReceiver());
980 PropertyAttributes attr = GetAttr();
981 if (obj->IsJSGlobalObject()) {
982 JSMutableHandle<GlobalDictionary> dict(thread_, obj->GetProperties());
983 if (dict->GetLength() == 0) {
984 dict.Update(GlobalDictionary::Create(thread_));
985 }
986
987 // Add PropertyBox to global dictionary
988 JSHandle<PropertyBox> cellHandle = factory->NewPropertyBox(key_);
989 cellHandle->SetValue(thread_, value.GetTaggedValue());
990 PropertyBoxType cellType = value->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT;
991 attr.SetBoxType(cellType);
992
993 JSHandle<GlobalDictionary> properties =
994 GlobalDictionary::PutIfAbsent(thread_, dict, key_, JSHandle<JSTaggedValue>(cellHandle), attr);
995 obj->SetProperties(thread_, properties);
996 // index and fastMode is not essential for global obj;
997 SetFound(0, cellHandle.GetTaggedValue(), attr.GetValue(), true);
998 return;
999 }
1000
1001 // The property has already existed whose value is hole, initialized by speculative hclass.
1002 // Not need AddProperty,just SetProperty
1003 if (receiverHoleEntry_ != -1) {
1004 attr.SetOffset(receiverHoleEntry_);
1005 auto actualValue =
1006 JSHClass::ConvertOrTransitionWithRep(thread_, JSHandle<JSObject>(receiver_), key_, value, attr);
1007 if (std::get<1>(actualValue)) {
1008 SetIsTransition(true);
1009 }
1010 attributes_.SetRepresentation(attr.GetRepresentation());
1011 auto *hclass = receiver_->GetTaggedObject()->GetClass();
1012 if (std::get<0>(actualValue)) {
1013 JSObject::Cast(receiver_.GetTaggedValue())->SetProperty<true>(thread_, hclass, attr, std::get<2>(actualValue));
1014 } else {
1015 JSObject::Cast(receiver_.GetTaggedValue())->SetProperty<false>(thread_, hclass, attr, std::get<2>(actualValue));
1016 }
1017 uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
1018 attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
1019 SetIsTSHClass(true);
1020 SetIsOnPrototype(false);
1021 SetFound(index, value.GetTaggedValue(), attr.GetValue(), true);
1022 return;
1023 }
1024
1025 attr = ObjectFastOperator::AddPropertyByName(thread_, obj, key_, value, attr);
1026 if (obj->GetJSHClass()->IsDictionaryMode()) {
1027 SetFound(0, value.GetTaggedValue(), attr.GetValue(), false);
1028 } else {
1029 uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
1030 attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
1031 SetFound(index, value.GetTaggedValue(), attr.GetValue(), true, true);
1032 }
1033 }
1034
DefineSetter(const JSHandle<JSTaggedValue> & value)1035 void ObjectOperator::DefineSetter(const JSHandle<JSTaggedValue> &value)
1036 {
1037 ASSERT(IsAccessorDescriptor());
1038 JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
1039 accessor->SetSetter(thread_, value.GetTaggedValue());
1040 UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
1041 }
1042
DefineGetter(const JSHandle<JSTaggedValue> & value)1043 void ObjectOperator::DefineGetter(const JSHandle<JSTaggedValue> &value)
1044 {
1045 ASSERT(IsAccessorDescriptor());
1046 JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
1047 accessor->SetGetter(thread_, value.GetTaggedValue());
1048 UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
1049 }
1050 } // namespace panda::ecmascript
1051