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