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