• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #ifndef ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H
17 #define ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H
18 
19 #include "ecmascript/object_fast_operator.h"
20 
21 #include "ecmascript/js_api/js_api_arraylist.h"
22 #include "ecmascript/js_api/js_api_deque.h"
23 #include "ecmascript/js_api/js_api_linked_list.h"
24 #include "ecmascript/js_api/js_api_list.h"
25 #include "ecmascript/js_api/js_api_plain_array.h"
26 #include "ecmascript/js_api/js_api_queue.h"
27 #include "ecmascript/js_api/js_api_stack.h"
28 #include "ecmascript/js_api/js_api_vector.h"
29 #include "ecmascript/js_date.h"
30 #include "ecmascript/js_function.h"
31 #include "ecmascript/js_hclass-inl.h"
32 #include "ecmascript/js_tagged_value-inl.h"
33 #include "ecmascript/js_typed_array.h"
34 #include "ecmascript/runtime_call_id.h"
35 #include "ecmascript/tagged_dictionary.h"
36 
37 namespace panda::ecmascript {
38 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
39 #define CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder) \
40     if (UNLIKELY((receiver) != (holder))) {           \
41         return JSTaggedValue::Hole();                 \
42     }
43 
44 template<bool UseOwn>
GetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)45 JSTaggedValue ObjectFastOperator::GetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
46 {
47     INTERPRETER_TRACE(thread, GetPropertyByName);
48     // no gc when return hole
49     ASSERT(key.IsStringOrSymbol());
50     JSTaggedValue holder = receiver;
51     do {
52         auto *hclass = holder.GetTaggedObject()->GetClass();
53         JSType jsType = hclass->GetObjectType();
54         if (IsSpecialIndexedObj(jsType)) {
55             if (IsFastTypeArray(jsType)) {
56                 JSTaggedValue res = FastGetTypeArrayProperty(thread, receiver, holder, key, jsType);
57                 if (res.IsNull()) {
58                     return JSTaggedValue::Hole();
59                 } else if (UNLIKELY(!res.IsHole())) {
60                     return res;
61                 }
62             } else {
63                 return JSTaggedValue::Hole();
64             }
65         }
66 
67         if (LIKELY(!hclass->IsDictionaryMode())) {
68             ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode());
69 
70             int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
71             if (entry != -1) {
72                 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
73                 PropertyAttributes attr(layoutInfo->GetAttr(entry));
74                 ASSERT(static_cast<int>(attr.GetOffset()) == entry);
75                 auto value = JSObject::Cast(holder)->GetProperty(hclass, attr);
76                 if (value.IsPropertyBox()) {
77                     return PropertyBox::Cast(value.GetTaggedObject())->GetValue();
78                 }
79                 if (UNLIKELY(attr.IsAccessor())) {
80                     return CallGetter(thread, receiver, holder, value);
81                 }
82                 ASSERT(!value.IsAccessor());
83                 if (!value.IsHole()) {
84                     return value;
85                 }
86             }
87         } else {
88             TaggedArray *array = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject());
89             ASSERT(array->IsDictionaryMode());
90             NameDictionary *dict = NameDictionary::Cast(array);
91             int entry = dict->FindEntry(key);
92             if (entry != -1) {
93                 auto value = dict->GetValue(entry);
94                 auto attr = dict->GetAttributes(entry);
95                 if (UNLIKELY(attr.IsAccessor())) {
96                     return CallGetter(thread, receiver, holder, value);
97                 }
98                 ASSERT(!value.IsAccessor());
99                 return value;
100             }
101         }
102         if (UseOwn) {
103             break;
104         }
105         holder = hclass->GetPrototype();
106     } while (holder.IsHeapObject());
107     // not found
108     return JSTaggedValue::Undefined();
109 }
110 
111 template<bool UseOwn>
SetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value)112 JSTaggedValue ObjectFastOperator::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
113                                                     JSTaggedValue value)
114 {
115     INTERPRETER_TRACE(thread, SetPropertyByName);
116     // property
117     JSTaggedValue holder = receiver;
118     int receiverHoleEntry = -1;
119     do {
120         auto *hclass = holder.GetTaggedObject()->GetClass();
121         JSType jsType = hclass->GetObjectType();
122         if (IsSpecialIndexedObj(jsType)) {
123             if (IsFastTypeArray(jsType)) {
124                 JSTaggedValue res = FastSetTypeArrayProperty(thread, receiver, holder, key, value, jsType);
125                 if (res.IsNull()) {
126                     return JSTaggedValue::Hole();
127                 } else if (UNLIKELY(!res.IsHole())) {
128                     return res;
129                 }
130             } else if (IsSpecialContainer(jsType)) {
131                 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property on Container", JSTaggedValue::Exception());
132             } else {
133                 return JSTaggedValue::Hole();
134             }
135         }
136         // UpdateRepresentation
137         if (LIKELY(!hclass->IsDictionaryMode())) {
138             ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode());
139             int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
140             if (entry != -1) {
141                 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
142                 PropertyAttributes attr(layoutInfo->GetAttr(entry));
143                 ASSERT(static_cast<int>(attr.GetOffset()) == entry);
144                 if (UNLIKELY(attr.IsAccessor())) {
145                     auto accessor = JSObject::Cast(holder)->GetProperty(hclass, attr);
146                     if (ShouldCallSetter(receiver, holder, accessor, attr)) {
147                         return CallSetter(thread, receiver, value, accessor);
148                     }
149                 }
150                 if (UNLIKELY(!attr.IsWritable())) {
151                     [[maybe_unused]] EcmaHandleScope handleScope(thread);
152                     THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", JSTaggedValue::Exception());
153                 }
154                 if (hclass->IsTS()) {
155                     auto attrVal = JSObject::Cast(holder)->GetProperty(hclass, attr);
156                     if (attrVal.IsHole()) {
157                         if (receiverHoleEntry == -1 && holder == receiver) {
158                             receiverHoleEntry = entry;
159                         }
160                         if (UseOwn) {
161                             break;
162                         }
163                         holder = hclass->GetPrototype();
164                         continue;
165                     }
166                 }
167                 if (UNLIKELY(holder != receiver)) {
168                     break;
169                 }
170                 JSObject::Cast(holder)->SetProperty(thread, hclass, attr, value);
171                 return JSTaggedValue::Undefined();
172             }
173         } else {
174             TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject());
175             ASSERT(properties->IsDictionaryMode());
176             NameDictionary *dict = NameDictionary::Cast(properties);
177             int entry = dict->FindEntry(key);
178             if (entry != -1) {
179                 auto attr = dict->GetAttributes(entry);
180                 if (UNLIKELY(attr.IsAccessor())) {
181                     auto accessor = dict->GetValue(entry);
182                     if (ShouldCallSetter(receiver, holder, accessor, attr)) {
183                         return CallSetter(thread, receiver, value, accessor);
184                     }
185                 }
186                 if (UNLIKELY(!attr.IsWritable())) {
187                     [[maybe_unused]] EcmaHandleScope handleScope(thread);
188                     THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", JSTaggedValue::Exception());
189                 }
190                 if (UNLIKELY(holder != receiver)) {
191                     break;
192                 }
193                 dict->UpdateValue(thread, entry, value);
194                 return JSTaggedValue::Undefined();
195             }
196         }
197         if (UseOwn) {
198             break;
199         }
200         holder = hclass->GetPrototype();
201     } while (holder.IsHeapObject());
202 
203     if (receiverHoleEntry != -1) {
204         auto *receiverHClass = receiver.GetTaggedObject()->GetClass();
205         LayoutInfo *receiverLayoutInfo = LayoutInfo::Cast(receiverHClass->GetLayout().GetTaggedObject());
206         PropertyAttributes attr(receiverLayoutInfo->GetAttr(receiverHoleEntry));
207         JSObject::Cast(receiver)->SetProperty(thread, receiverHClass, attr, value);
208         return JSTaggedValue::Undefined();
209     }
210 
211     [[maybe_unused]] EcmaHandleScope handleScope(thread);
212     JSHandle<JSObject> objHandle(thread, receiver);
213     JSHandle<JSTaggedValue> keyHandle(thread, key);
214     JSHandle<JSTaggedValue> valueHandle(thread, value);
215 
216     if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) {
217         THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", JSTaggedValue::Exception());
218     }
219     AddPropertyByName(thread, objHandle, keyHandle, valueHandle, PropertyAttributes::Default());
220     return JSTaggedValue::Undefined();
221 }
222 
223 template<bool UseOwn>
GetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)224 JSTaggedValue ObjectFastOperator::GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
225 {
226     INTERPRETER_TRACE(thread, GetPropertyByIndex);
227     [[maybe_unused]] EcmaHandleScope handleScope(thread);
228     JSTaggedValue holder = receiver;
229     do {
230         auto *hclass = holder.GetTaggedObject()->GetClass();
231         JSType jsType = hclass->GetObjectType();
232         if (IsSpecialIndexedObj(jsType)) {
233             if (jsType == JSType::JS_TYPED_ARRAY) {
234                 return JSTaggedValue::Hole();
235             }
236             if (IsFastTypeArray(jsType)) {
237                 return JSTypedArray::FastGetPropertyByIndex(thread, receiver, index, jsType);
238             }
239             if (IsSpecialContainer(jsType)) {
240                 return GetContainerProperty(thread, holder, index, jsType);
241             }
242             return JSTaggedValue::Hole();
243         }
244         TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetTaggedObject());
245 
246         if (!hclass->IsDictionaryElement()) {
247             ASSERT(!elements->IsDictionaryMode());
248             if (index < elements->GetLength()) {
249                 JSTaggedValue value = elements->Get(index);
250                 if (!value.IsHole()) {
251                     return value;
252                 }
253             } else {
254                 return JSTaggedValue::Hole();
255             }
256         } else {
257             NumberDictionary *dict = NumberDictionary::Cast(elements);
258             int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index)));
259             if (entry != -1) {
260                 auto attr = dict->GetAttributes(entry);
261                 auto value = dict->GetValue(entry);
262                 if (UNLIKELY(attr.IsAccessor())) {
263                     return CallGetter(thread, receiver, holder, value);
264                 }
265                 ASSERT(!value.IsAccessor());
266                 return value;
267             }
268         }
269         if (UseOwn) {
270             break;
271         }
272         holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype();
273     } while (holder.IsHeapObject());
274 
275     // not found
276     return JSTaggedValue::Undefined();
277 }
278 
279 template<bool UseOwn>
SetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)280 JSTaggedValue ObjectFastOperator::SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
281                                                      JSTaggedValue value)
282 {
283     INTERPRETER_TRACE(thread, SetPropertyByIndex);
284     JSTaggedValue holder = receiver;
285     do {
286         auto *hclass = holder.GetTaggedObject()->GetClass();
287         JSType jsType = hclass->GetObjectType();
288         if (IsSpecialIndexedObj(jsType)) {
289             if (jsType == JSType::JS_TYPED_ARRAY) {
290                 return JSTaggedValue::Hole();
291             }
292             if (IsFastTypeArray(jsType)) {
293                 CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
294                 return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType);
295             }
296             if (IsSpecialContainer(jsType)) {
297                 return SetContainerProperty(thread, holder, index, value, jsType);
298             }
299             return JSTaggedValue::Hole();
300         }
301         TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetTaggedObject());
302         if (!hclass->IsDictionaryElement()) {
303             ASSERT(!elements->IsDictionaryMode());
304             if (UNLIKELY(holder != receiver)) {
305                 break;
306             }
307             if (index < elements->GetLength()) {
308                 if (!elements->Get(index).IsHole()) {
309                     if (holder.IsJSCOWArray()) {
310                         [[maybe_unused]] EcmaHandleScope handleScope(thread);
311                         JSHandle<JSArray> holderHandler(thread, holder);
312                         JSHandle<JSTaggedValue> valueHandle(thread, value);
313                         // CheckAndCopyArray may cause gc.
314                         JSArray::CheckAndCopyArray(thread, holderHandler);
315                         TaggedArray::Cast(holderHandler->GetElements())->Set(thread, index, valueHandle);
316                         return JSTaggedValue::Undefined();
317                     }
318                     elements->Set(thread, index, value);
319                     return JSTaggedValue::Undefined();
320                 }
321             }
322         } else {
323             return JSTaggedValue::Hole();
324         }
325         if (UseOwn) {
326             break;
327         }
328         holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype();
329     } while (holder.IsHeapObject());
330 
331     return AddPropertyByIndex(thread, receiver, index, value);
332 }
333 
334 template<bool UseOwn>
GetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)335 JSTaggedValue ObjectFastOperator::GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
336 {
337     INTERPRETER_TRACE(thread, GetPropertyByValue);
338     if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) {
339         return JSTaggedValue::Hole();
340     }
341     // fast path
342     auto index = TryToElementsIndex(key);
343     if (LIKELY(index >= 0)) {
344         return GetPropertyByIndex<UseOwn>(thread, receiver, index);
345     }
346     if (!key.IsNumber()) {
347         if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
348             // update string stable
349             [[maybe_unused]] EcmaHandleScope handleScope(thread);
350             JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
351             key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
352             // Maybe moved by GC
353             receiver = receiverHandler.GetTaggedValue();
354         }
355         return ObjectFastOperator::GetPropertyByName<UseOwn>(thread, receiver, key);
356     }
357     return JSTaggedValue::Hole();
358 }
359 
360 template<bool UseOwn>
SetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value)361 JSTaggedValue ObjectFastOperator::SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
362                                                      JSTaggedValue value)
363 {
364     INTERPRETER_TRACE(thread, SetPropertyByValue);
365     if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) {
366         return JSTaggedValue::Hole();
367     }
368     // fast path
369     auto index = TryToElementsIndex(key);
370     if (LIKELY(index >= 0)) {
371         return SetPropertyByIndex<UseOwn>(thread, receiver, index, value);
372     }
373     if (!key.IsNumber()) {
374         if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
375             // update string stable
376             [[maybe_unused]] EcmaHandleScope handleScope(thread);
377             JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
378             JSHandle<JSTaggedValue> valueHandler(thread, value);
379             key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
380             // Maybe moved by GC
381             receiver = receiverHandler.GetTaggedValue();
382             value = valueHandler.GetTaggedValue();
383         }
384         return ObjectFastOperator::SetPropertyByName<UseOwn>(thread, receiver, key, value);
385     }
386     return JSTaggedValue::Hole();
387 }
388 
FastSetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value)389 bool ObjectFastOperator::FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
390                                                 JSTaggedValue value)
391 {
392     INTERPRETER_TRACE(thread, FastSetPropertyByValue);
393     JSTaggedValue result = ObjectFastOperator::SetPropertyByValue(thread, receiver, key, value);
394     if (!result.IsHole()) {
395         return !result.IsException();
396     }
397     return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
398                                       JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value),
399                                       true);
400 }
401 
FastSetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)402 bool ObjectFastOperator::FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
403                                                 JSTaggedValue value)
404 {
405     INTERPRETER_TRACE(thread, FastSetPropertyByIndex);
406     JSTaggedValue result = ObjectFastOperator::SetPropertyByIndex(thread, receiver, index, value);
407     if (!result.IsHole()) {
408         return !result.IsException();
409     }
410     return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver), index,
411                                       JSHandle<JSTaggedValue>(thread, value), true);
412 }
413 
FastGetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)414 JSTaggedValue ObjectFastOperator::FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
415 {
416     INTERPRETER_TRACE(thread, FastGetPropertyByName);
417     ASSERT(key.IsStringOrSymbol());
418     if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
419         JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
420         key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
421         // Maybe moved by GC
422         receiver = receiverHandler.GetTaggedValue();
423     }
424     JSTaggedValue result = ObjectFastOperator::GetPropertyByName(thread, receiver, key);
425     if (result.IsHole()) {
426         return JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
427             JSHandle<JSTaggedValue>(thread, key)).GetValue().GetTaggedValue();
428     }
429     return result;
430 }
431 
FastGetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)432 JSTaggedValue ObjectFastOperator::FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
433 {
434     INTERPRETER_TRACE(thread, FastGetPropertyByValue);
435     JSTaggedValue result = ObjectFastOperator::GetPropertyByValue(thread, receiver, key);
436     if (result.IsHole()) {
437         return JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
438             JSHandle<JSTaggedValue>(thread, key)).GetValue().GetTaggedValue();
439     }
440     return result;
441 }
442 
443 template<bool UseHole>  // UseHole is only for Array::Sort() which requires Hole order
FastGetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)444 JSTaggedValue ObjectFastOperator::FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
445 {
446     INTERPRETER_TRACE(thread, FastGetPropertyByIndex);
447     JSTaggedValue result = ObjectFastOperator::GetPropertyByIndex(thread, receiver, index);
448     if (result.IsHole() && !UseHole) {
449         return JSTaggedValue::GetProperty(thread,
450             JSHandle<JSTaggedValue>(thread, receiver), index).GetValue().GetTaggedValue();
451     }
452     return result;
453 }
454 
FastParseDate(const EcmaString * str)455 JSTaggedValue ObjectFastOperator::FastParseDate(const EcmaString *str)
456 {
457     int year = 0;
458     int month = 1;
459     int date = 1;
460     int index = 0;
461 
462     CVector<uint8_t> tmpBuf;
463     EcmaStringAccessor strAccessor(const_cast<EcmaString *>(str));
464     int len = static_cast<int>(strAccessor.GetLength());
465     auto data = reinterpret_cast<const char *>(strAccessor.GetUtf8DataFlat(str, tmpBuf));
466     if (!GetNumFromString(data, len, &index, &year)) {
467         return JSTaggedValue::Hole();
468     }
469     if (!GetNumFromString(data, len, &index, &month)) {
470         return JSTaggedValue::Hole();
471     }
472     if (!GetNumFromString(data, len, &index, &date)) {
473         return JSTaggedValue::Hole();
474     }
475     if (month < 1 || month > JSDate::MONTH_PER_YEAR) {
476         return JSTaggedValue::Hole();
477     }
478     if (date < 1 || date > JSDate::MAX_DAYS_MONTH) {
479         return JSTaggedValue::Hole();
480     }
481     double day = JSDate::MakeDay(year, month - 1, date);
482     double timeValue = JSDate::TimeClip(JSDate::MakeDate(day, 0));
483     return JSTaggedValue(timeValue);
484 }
485 
AddPropertyByName(JSThread * thread,JSHandle<JSObject> objHandle,JSHandle<JSTaggedValue> keyHandle,JSHandle<JSTaggedValue> valueHandle,PropertyAttributes attr)486 PropertyAttributes ObjectFastOperator::AddPropertyByName(JSThread *thread, JSHandle<JSObject> objHandle,
487                                                          JSHandle<JSTaggedValue> keyHandle,
488                                                          JSHandle<JSTaggedValue> valueHandle,
489                                                          PropertyAttributes attr)
490 {
491     INTERPRETER_TRACE(thread, AddPropertyByName);
492 
493     if (objHandle->IsJSArray() && keyHandle.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString()) {
494         objHandle->GetJSHClass()->SetHasConstructor(true);
495     }
496     int32_t nextInlinedPropsIndex = objHandle->GetJSHClass()->GetNextInlinedPropsIndex();
497     if (nextInlinedPropsIndex >= 0) {
498         objHandle->SetPropertyInlinedProps(thread, nextInlinedPropsIndex, valueHandle.GetTaggedValue());
499         attr.SetOffset(nextInlinedPropsIndex);
500         attr.SetIsInlinedProps(true);
501         JSHClass::AddProperty(thread, objHandle, keyHandle, attr);
502         return attr;
503     }
504 
505     JSMutableHandle<TaggedArray> array(thread, objHandle->GetProperties());
506     uint32_t length = array->GetLength();
507     if (length == 0) {
508         length = JSObject::MIN_PROPERTIES_LENGTH;
509         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
510         array.Update(factory->NewTaggedArray(length).GetTaggedValue());
511         objHandle->SetProperties(thread, array.GetTaggedValue());
512     }
513 
514     if (!array->IsDictionaryMode()) {
515         attr.SetIsInlinedProps(false);
516 
517         uint32_t nonInlinedProps = static_cast<uint32_t>(objHandle->GetJSHClass()->GetNextNonInlinedPropsIndex());
518         ASSERT(length >= nonInlinedProps);
519         // if array is full, grow array or change to dictionary mode
520         if (length >= nonInlinedProps) {
521             if (UNLIKELY(length >= JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS)) {
522                 // change to dictionary and add one.
523                 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread, objHandle));
524                 JSHandle<NameDictionary> newDict =
525                     NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr);
526                 objHandle->SetProperties(thread, newDict);
527                 // index is not essential when fastMode is false;
528                 return attr;
529             }
530             // Grow properties array size
531             uint32_t capacity = JSObject::ComputePropertyCapacity(length);
532             ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
533             array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue());
534             objHandle->SetProperties(thread, array.GetTaggedValue());
535         }
536 
537         attr.SetOffset(nonInlinedProps + objHandle->GetJSHClass()->GetInlinedProperties());
538         JSHClass::AddProperty(thread, objHandle, keyHandle, attr);
539         array->Set(thread, nonInlinedProps, valueHandle.GetTaggedValue());
540     } else {
541         JSHandle<NameDictionary> dictHandle(array);
542         JSHandle<NameDictionary> newDict =
543             NameDictionary::PutIfAbsent(thread, dictHandle, keyHandle, valueHandle, attr);
544         objHandle->SetProperties(thread, newDict);
545     }
546     return attr;
547 }
548 
CallGetter(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue value)549 JSTaggedValue ObjectFastOperator::CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder,
550                                              JSTaggedValue value)
551 {
552     INTERPRETER_TRACE(thread, CallGetter);
553     // Accessor
554     [[maybe_unused]] EcmaHandleScope handleScope(thread);
555     AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject());
556     if (UNLIKELY(accessor->IsInternal())) {
557         JSHandle<JSObject> objHandle(thread, holder);
558         return accessor->CallInternalGet(thread, objHandle);
559     }
560     JSHandle<JSTaggedValue> objHandle(thread, receiver);
561     return JSObject::CallGetter(thread, accessor, objHandle);
562 }
563 
CallSetter(JSThread * thread,JSTaggedValue receiver,JSTaggedValue value,JSTaggedValue accessorValue)564 JSTaggedValue ObjectFastOperator::CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value,
565                                              JSTaggedValue accessorValue)
566 {
567     INTERPRETER_TRACE(thread, CallSetter);
568     // Accessor
569     [[maybe_unused]] EcmaHandleScope handleScope(thread);
570     JSHandle<JSTaggedValue> objHandle(thread, receiver);
571     JSHandle<JSTaggedValue> valueHandle(thread, value);
572 
573     auto accessor = AccessorData::Cast(accessorValue.GetTaggedObject());
574     bool success = JSObject::CallSetter(thread, *accessor, objHandle, valueHandle, true);
575     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
576     return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
577 }
578 
ShouldCallSetter(JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue accessorValue,PropertyAttributes attr)579 bool ObjectFastOperator::ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue,
580                                           PropertyAttributes attr)
581 {
582     if (!AccessorData::Cast(accessorValue.GetTaggedObject())->IsInternal()) {
583         return true;
584     }
585     if (receiver != holder) {
586         return false;
587     }
588     return attr.IsWritable();
589 }
590 
IsSpecialIndexedObj(JSType jsType)591 bool ObjectFastOperator::IsSpecialIndexedObj(JSType jsType)
592 {
593     return jsType > JSType::JS_ARRAY;
594 }
595 
IsFastTypeArray(JSType jsType)596 bool ObjectFastOperator::IsFastTypeArray(JSType jsType)
597 {
598     return jsType >= JSType::JS_TYPED_ARRAY_FIRST && jsType <= JSType::JS_FLOAT64_ARRAY;
599 }
600 
FastGetTypeArrayProperty(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue key,JSType jsType)601 JSTaggedValue ObjectFastOperator::FastGetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver,
602                                                            JSTaggedValue holder,
603                                                            JSTaggedValue key, JSType jsType)
604 {
605     CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
606     JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString();
607     if (UNLIKELY(negativeZero == key)) {
608         return JSTaggedValue::Undefined();
609     }
610     uint32_t index = 0;
611     if (TryStringOrSymbolToIndex(key, &index)) {
612         if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) {
613             return JSTaggedValue::Null();
614         }
615         return JSTypedArray::FastGetPropertyByIndex(thread, receiver, index, jsType);
616     }
617     return JSTaggedValue::Hole();
618 }
619 
TryStringOrSymbolToIndex(JSTaggedValue key,uint32_t * output)620 bool ObjectFastOperator::TryStringOrSymbolToIndex(JSTaggedValue key, uint32_t *output)
621 {
622     if (key.IsSymbol()) {
623         return false;
624     }
625     auto strObj = static_cast<EcmaString *>(key.GetTaggedObject());
626     return EcmaStringAccessor(strObj).ToTypedArrayIndex(output);
627 }
628 
FastSetTypeArrayProperty(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue key,JSTaggedValue value,JSType jsType)629 JSTaggedValue ObjectFastOperator::FastSetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver,
630                                                            JSTaggedValue holder, JSTaggedValue key,
631                                                            JSTaggedValue value, JSType jsType)
632 {
633     CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
634     JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString();
635     if (UNLIKELY(negativeZero == key)) {
636         if (value.IsECMAObject()) {
637             return JSTaggedValue::Null();
638         }
639         return JSTaggedValue::Undefined();
640     }
641     uint32_t index = 0;
642     if (TryStringOrSymbolToIndex(key, &index)) {
643         if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) {
644             return JSTaggedValue::Null();
645         }
646         return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType);
647     }
648     return JSTaggedValue::Hole();
649 }
650 
IsSpecialContainer(JSType jsType)651 bool ObjectFastOperator::IsSpecialContainer(JSType jsType)
652 {
653     return jsType >= JSType::JS_API_ARRAY_LIST && jsType <= JSType::JS_API_QUEUE;
654 }
655 
GetContainerProperty(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSType jsType)656 JSTaggedValue ObjectFastOperator::GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index,
657                                                        JSType jsType)
658 {
659     JSTaggedValue res = JSTaggedValue::Undefined();
660     switch (jsType) {
661         case JSType::JS_API_ARRAY_LIST:
662             res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Get(thread, index);
663             break;
664         case JSType::JS_API_QUEUE:
665             res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Get(thread, index);
666             break;
667         case JSType::JS_API_PLAIN_ARRAY:
668             res = JSAPIPlainArray::Cast(receiver.GetTaggedObject())->Get(JSTaggedValue(index));
669             break;
670         case JSType::JS_API_DEQUE:
671             res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Get(index);
672             break;
673         case JSType::JS_API_STACK:
674             res = JSAPIStack::Cast(receiver.GetTaggedObject())->Get(index);
675             break;
676         case JSType::JS_API_VECTOR: {
677             auto self = JSHandle<JSTaggedValue>(thread, receiver);
678             res = JSAPIVector::Get(thread, JSHandle<JSAPIVector>::Cast(self), index);
679             break;
680         }
681         case JSType::JS_API_LIST: {
682             res = JSAPIList::Cast(receiver.GetTaggedObject())->Get(index);
683             break;
684         }
685         case JSType::JS_API_LINKED_LIST: {
686             res = JSAPILinkedList::Cast(receiver.GetTaggedObject())->Get(index);
687             break;
688         }
689         default:
690             break;
691     }
692     return res;
693 }
694 
SetContainerProperty(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value,JSType jsType)695 JSTaggedValue ObjectFastOperator::SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index,
696                                                        JSTaggedValue value, JSType jsType)
697 {
698     JSTaggedValue res = JSTaggedValue::Undefined();
699     switch (jsType) {
700         case JSType::JS_API_ARRAY_LIST:
701             res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
702             break;
703         case JSType::JS_API_QUEUE:
704             res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
705             break;
706         case JSType::JS_API_PLAIN_ARRAY: {
707             JSHandle<JSAPIPlainArray> plainArray(thread, receiver);
708             res = JSAPIPlainArray::Set(thread, plainArray, index, value);
709             break;
710         }
711         case JSType::JS_API_DEQUE:
712             res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
713             break;
714         case JSType::JS_API_STACK:
715             res = JSAPIStack::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
716             break;
717         case JSType::JS_API_VECTOR:
718             res = JSAPIVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
719             break;
720         case JSType::JS_API_LIST: {
721             JSHandle<JSAPIList> singleList(thread, receiver);
722             res = JSAPIList::Set(thread, singleList, index, JSHandle<JSTaggedValue>(thread, value));
723             break;
724         }
725         case JSType::JS_API_LINKED_LIST: {
726             JSHandle<JSAPILinkedList> doubleList(thread, receiver);
727             res = JSAPILinkedList::Set(thread, doubleList, index, JSHandle<JSTaggedValue>(thread, value));
728             break;
729         }
730         default:
731             break;
732     }
733     return res;
734 }
735 
AddPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)736 JSTaggedValue ObjectFastOperator::AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
737                                                      JSTaggedValue value)
738 {
739     INTERPRETER_TRACE(thread, AddPropertyByIndex);
740     [[maybe_unused]] EcmaHandleScope handleScope(thread);
741     if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) {
742         THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", JSTaggedValue::Exception());
743     }
744 
745     bool success = JSObject::AddElementInternal(thread, JSHandle<JSObject>(thread, receiver), index,
746                                                 JSHandle<JSTaggedValue>(thread, value), PropertyAttributes::Default());
747     return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
748 }
749 
TryToElementsIndex(JSTaggedValue key)750 int64_t ObjectFastOperator::TryToElementsIndex(JSTaggedValue key)
751 {
752     if (LIKELY(key.IsInt())) {
753         return key.GetInt();
754     }
755     if (key.IsString()) {
756         uint32_t index = 0;
757         if (JSTaggedValue::StringToElementIndex(key, &index)) {
758             return static_cast<int64_t>(index);
759         }
760     } else if (key.IsDouble()) {
761         double number = key.GetDouble();
762         auto integer = static_cast<int32_t>(number);
763         if (number == integer) {
764             return integer;
765         }
766     }
767     return -1;
768 }
769 
GetNumFromString(const char * str,int len,int * index,int * num)770 bool ObjectFastOperator::GetNumFromString(const char *str, int len, int *index, int *num)
771 {
772     int indexStr = *index;
773     char oneByte = 0;
774     oneByte = str[indexStr];
775     if (oneByte < '0' || oneByte > '9') {
776         return false;
777     }
778     if (indexStr >= len) {
779         return false;
780     }
781     int value = 0;
782     while (indexStr < len) {
783         oneByte = str[indexStr];
784         int val = static_cast<int>(oneByte - '0');
785         if (val >= 0 && val <= JSDate::NUM_NINE) {
786             value = value * JSDate::TEN + val;
787             indexStr++;
788         } else if (oneByte != '-') {
789             return false;
790         } else {
791             indexStr++;
792             break;
793         }
794     }
795     *num = value;
796     *index = indexStr;
797     return true;
798 }
799 }
800 #endif  // ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H
801