1 /*
2 * Copyright (c) 2023-2024 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/js_handle.h"
20 #include "ecmascript/js_tagged_value.h"
21 #include "ecmascript/jspandafile/class_info_extractor.h"
22 #include "ecmascript/mem/assert_scope.h"
23 #include "ecmascript/object_fast_operator.h"
24
25 #include "ecmascript/base/array_helper.h"
26 #include "ecmascript/ecma_string_table.h"
27 #include "ecmascript/element_accessor-inl.h"
28 #include "ecmascript/global_env.h"
29 #include "ecmascript/js_api/js_api_arraylist.h"
30 #include "ecmascript/js_api/js_api_deque.h"
31 #include "ecmascript/js_api/js_api_linked_list.h"
32 #include "ecmascript/js_api/js_api_list.h"
33 #include "ecmascript/js_api/js_api_plain_array.h"
34 #include "ecmascript/js_api/js_api_queue.h"
35 #include "ecmascript/js_api/js_api_stack.h"
36 #include "ecmascript/js_api/js_api_vector.h"
37 #include "ecmascript/js_api/js_api_bitvector.h"
38 #include "ecmascript/js_date.h"
39 #include "ecmascript/js_function.h"
40 #include "ecmascript/js_hclass-inl.h"
41 #include "ecmascript/js_object-inl.h"
42 #include "ecmascript/js_tagged_value-inl.h"
43 #include "ecmascript/js_typed_array.h"
44 #include "ecmascript/message_string.h"
45 #include "ecmascript/property_attributes.h"
46 #include "ecmascript/runtime_call_id.h"
47 #include "ecmascript/shared_objects/concurrent_api_scope.h"
48 #include "ecmascript/shared_objects/js_shared_array.h"
49 #include "ecmascript/tagged_array.h"
50 #include "ecmascript/tagged_dictionary.h"
51
52 namespace panda::ecmascript {
53 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
54 #define CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder) \
55 if (UNLIKELY((receiver) != (holder))) { \
56 return JSTaggedValue::Hole(); \
57 }
58
HasOwnProperty(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)59 std::pair<JSTaggedValue, bool> ObjectFastOperator::HasOwnProperty(JSThread *thread,
60 JSTaggedValue receiver, JSTaggedValue key)
61 {
62 [[maybe_unused]] DisallowGarbageCollection noGc;
63 if (!receiver.IsHeapObject() || !(receiver.IsRegularObject())) {
64 return std::make_pair(JSTaggedValue::Hole(), false);
65 }
66 if (!key.IsString()) {
67 return std::make_pair(JSTaggedValue::Hole(), false);
68 }
69
70 uint32_t index = 0;
71 if (JSTaggedValue::ToElementIndex(key, &index)) {
72 ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
73 JSHandle<JSObject> receiverObj(thread, receiver);
74 if (ElementAccessor::GetElementsLength(receiverObj) == 0) {
75 return std::make_pair(JSTaggedValue::Hole(), true); // Empty Array
76 }
77
78 if (!ElementAccessor::IsDictionaryMode(receiverObj)) {
79 if (ElementAccessor::GetElementsLength(receiverObj) <= index) {
80 return std::make_pair(JSTaggedValue::Hole(), true);
81 }
82 JSTaggedValue value = ElementAccessor::Get(receiverObj, index);
83 return std::make_pair(value, true);
84 } else {
85 NumberDictionary *dictionary =
86 NumberDictionary::Cast(JSObject::Cast(receiver)->GetElements().GetTaggedObject());
87 int entry = dictionary->FindEntry(JSTaggedValue(static_cast<int>(index)));
88 if (entry == -1) {
89 return std::make_pair(JSTaggedValue::Hole(), true);
90 }
91 return std::make_pair(JSTaggedValue::Undefined(), true);
92 }
93 }
94
95 if (!EcmaStringAccessor(key).IsInternString()) {
96 JSHandle<EcmaString> keyHandle(thread, key);
97 EcmaString *str = thread->GetEcmaVM()->GetEcmaStringTable()->TryGetInternString(thread, keyHandle);
98 if (str == nullptr) {
99 return std::make_pair(JSTaggedValue::Hole(), true);
100 }
101 key = JSTaggedValue(str);
102 }
103 auto *hclass = receiver.GetTaggedObject()->GetClass();
104 if (LIKELY(!hclass->IsDictionaryMode())) {
105 ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
106 int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
107 if (entry != -1) {
108 return std::make_pair(JSTaggedValue::Undefined(), true);
109 }
110 } else {
111 TaggedArray *array = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject());
112 ASSERT(array->IsDictionaryMode());
113 NameDictionary *dict = NameDictionary::Cast(array);
114 int entry = dict->FindEntry(key);
115 if (entry != -1) {
116 return std::make_pair(JSTaggedValue::Undefined(), true);
117 }
118 }
119 return std::make_pair(JSTaggedValue::Hole(), true);
120 }
121
122 template <ObjectFastOperator::Status status>
TryFastHasProperty(JSThread * thread,JSTaggedValue receiver,JSMutableHandle<JSTaggedValue> keyHandle)123 JSTaggedValue ObjectFastOperator::TryFastHasProperty(JSThread *thread, JSTaggedValue receiver,
124 JSMutableHandle<JSTaggedValue> keyHandle)
125 {
126 JSTaggedValue key = keyHandle.GetTaggedValue();
127 if (UNLIKELY(!receiver.IsHeapObject() || !receiver.IsRegularObject())) {
128 return JSTaggedValue::Hole();
129 }
130 if (UNLIKELY(!key.IsNumber() && !key.IsString())) {
131 return JSTaggedValue::Hole();
132 }
133
134 // Elements
135 auto index = TryToElementsIndex(key);
136 if (index >= 0) {
137 ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
138 JSHandle<JSObject> receiverObj(thread, receiver);
139 if (!ElementAccessor::IsDictionaryMode(receiverObj)) {
140 if (index < ElementAccessor::GetElementsLength(receiverObj)) {
141 JSTaggedValue value = ElementAccessor::Get(receiverObj, index);
142 return value.IsHole() ? JSTaggedValue::Hole() : JSTaggedValue::True();
143 }
144 }
145 return JSTaggedValue::Hole();
146 }
147
148 // layout cache
149 auto *hclass = receiver.GetTaggedObject()->GetClass();
150 if (LIKELY(!hclass->IsDictionaryMode())) {
151 if (!EcmaStringAccessor(key).IsInternString()) {
152 JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
153 auto string = thread->GetEcmaVM()->GetFactory()->InternString(keyHandle);
154 EcmaStringAccessor(string).SetInternString();
155 keyHandle.Update(JSTaggedValue(string));
156 key = keyHandle.GetTaggedValue();
157 receiver = receiverHandler.GetTaggedValue();
158 }
159 ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
160 int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
161 if (entry != -1) {
162 return JSTaggedValue::True();
163 }
164 }
165 return JSTaggedValue::Hole();
166 }
167
168 template <ObjectFastOperator::Status status>
TryFastGetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSMutableHandle<JSTaggedValue> keyHandle)169 JSTaggedValue ObjectFastOperator::TryFastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver,
170 JSMutableHandle<JSTaggedValue> keyHandle)
171 {
172 JSTaggedValue key = keyHandle.GetTaggedValue();
173 if (UNLIKELY(!receiver.IsHeapObject() || !receiver.IsRegularObject())) {
174 return JSTaggedValue::Hole();
175 }
176 if (UNLIKELY(!key.IsNumber() && !key.IsString())) {
177 return JSTaggedValue::Hole();
178 }
179 auto index = TryToElementsIndex(key);
180 if (index >= 0) {
181 return TryFastGetPropertyByIndex<status>(thread, receiver, index);
182 }
183 if (key.IsString()) {
184 if (!EcmaStringAccessor(key).IsInternString()) {
185 [[maybe_unused]] EcmaHandleScope handleScope(thread);
186 JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
187 auto string = thread->GetEcmaVM()->GetFactory()->InternString(keyHandle);
188 EcmaStringAccessor(string).SetInternString();
189 keyHandle.Update(JSTaggedValue(string));
190 key = keyHandle.GetTaggedValue();
191 receiver = receiverHandler.GetTaggedValue();
192 }
193 auto ret = TryGetPropertyByNameThroughCacheAtLocal(thread, receiver, key);
194 if (!ret.IsHole()) {
195 return ret;
196 }
197 return ObjectFastOperator::GetPropertyByName<status>(thread, receiver, key);
198 }
199 return JSTaggedValue::Hole();
200 }
201
202 template<ObjectFastOperator::Status status>
TryFastGetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)203 JSTaggedValue ObjectFastOperator::TryFastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
204 {
205 JSTaggedValue holder = receiver;
206 auto *hclass = holder.GetTaggedObject()->GetClass();
207 JSHandle<JSObject> currentHolder(thread, holder);
208 if (!hclass->IsDictionaryElement()) {
209 ASSERT(!ElementAccessor::IsDictionaryMode(currentHolder));
210 if (index < ElementAccessor::GetElementsLength(currentHolder)) {
211 JSTaggedValue value = ElementAccessor::Get(currentHolder, index);
212 if (!value.IsHole()) {
213 return value;
214 }
215 }
216 }
217 return JSTaggedValue::Hole();
218 }
219
220 template<ObjectFastOperator::Status status>
TryGetPropertyByNameThroughCacheAtLocal(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)221 JSTaggedValue ObjectFastOperator::TryGetPropertyByNameThroughCacheAtLocal(JSThread *thread, JSTaggedValue receiver,
222 JSTaggedValue key)
223 {
224 auto *hclass = receiver.GetTaggedObject()->GetClass();
225 if (LIKELY(!hclass->IsDictionaryMode())) {
226 ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
227
228 int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
229 if (entry != -1) {
230 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
231 PropertyAttributes attr(layoutInfo->GetAttr(entry));
232 ASSERT(static_cast<int>(attr.GetOffset()) == entry);
233 auto value = JSObject::Cast(receiver)->GetProperty(hclass, attr);
234 if (UNLIKELY(attr.IsAccessor())) {
235 if (GetInternal(status)) {
236 return value;
237 }
238 return CallGetter(thread, receiver, receiver, value);
239 }
240 ASSERT(!value.IsAccessor());
241 if (!value.IsHole()) {
242 return value;
243 }
244 }
245 }
246 return JSTaggedValue::Hole();
247 }
248
249 template<ObjectFastOperator::Status status>
GetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,bool noAllocate,bool * isCallGetter)250 JSTaggedValue ObjectFastOperator::GetPropertyByName(JSThread *thread, JSTaggedValue receiver,
251 JSTaggedValue key, [[maybe_unused]]bool noAllocate,
252 [[maybe_unused]]bool *isCallGetter)
253 {
254 INTERPRETER_TRACE(thread, GetPropertyByName);
255 // no gc when return hole
256 ASSERT(key.IsStringOrSymbol());
257 JSTaggedValue holder = receiver;
258 do {
259 auto *hclass = holder.GetTaggedObject()->GetClass();
260 JSType jsType = hclass->GetObjectType();
261 if (IsSpecialIndexedObj(jsType)) {
262 if (IsFastTypeArray(jsType)) {
263 JSTaggedValue res = FastGetTypeArrayProperty(thread, receiver, holder, key, jsType);
264 if (res.IsNull()) {
265 return JSTaggedValue::Hole();
266 } else if (UNLIKELY(!res.IsHole())) {
267 return res;
268 }
269 } else if (IsString(jsType) && key.IsString()) {
270 auto vm = thread->GetEcmaVM();
271 JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString();
272 bool isLenKey = EcmaStringAccessor::StringsAreEqual(vm,
273 JSHandle<EcmaString>(thread, key), JSHandle<EcmaString>(thread, lenKey));
274 if (isLenKey) { // get string length
275 return JSTaggedValue(EcmaStringAccessor(holder).GetLength());
276 } else { // get string prototype
277 JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
278 JSHandle<JSTaggedValue> stringPrototype = env->GetStringPrototype();
279 holder = stringPrototype.GetTaggedValue();
280 continue;
281 }
282 } else if (!IsJSPrimitiveRef(jsType)) { // not string prototype etc.
283 return JSTaggedValue::Hole();
284 }
285 }
286
287 if (LIKELY(!hclass->IsDictionaryMode())) {
288 ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode());
289
290 int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
291 if (entry != -1) {
292 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
293 PropertyAttributes attr(layoutInfo->GetAttr(entry));
294 ASSERT(static_cast<int>(attr.GetOffset()) == entry);
295 auto value = JSObject::Cast(holder)->GetProperty(hclass, attr);
296 if (UNLIKELY(attr.IsAccessor())) {
297 if (GetInternal(status)) {
298 return value;
299 }
300 if (noAllocate) {
301 *isCallGetter = true;
302 return value;
303 }
304 return CallGetter(thread, receiver, holder, value);
305 }
306 ASSERT(!value.IsAccessor());
307 if (!value.IsHole()) {
308 return value;
309 }
310 }
311 } else {
312 TaggedArray *array = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject());
313 ASSERT(array->IsDictionaryMode());
314 NameDictionary *dict = NameDictionary::Cast(array);
315 int entry = dict->FindEntry(key);
316 if (entry != -1) {
317 auto value = dict->GetValue(entry);
318 auto attr = dict->GetAttributes(entry);
319 if (UNLIKELY(attr.IsAccessor())) {
320 if (GetInternal(status)) {
321 return value;
322 }
323 if (noAllocate) {
324 *isCallGetter = true;
325 return value;
326 }
327 return CallGetter(thread, receiver, holder, value);
328 }
329 ASSERT(!value.IsAccessor());
330 return value;
331 }
332 }
333 if (UseOwn(status)) {
334 break;
335 }
336 holder = hclass->GetPrototype();
337 } while (holder.IsHeapObject());
338 // not found
339 return JSTaggedValue::Undefined();
340 }
341
342 template<ObjectFastOperator::Status status>
TrySetPropertyByNameThroughCacheAtLocal(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value)343 JSTaggedValue ObjectFastOperator::TrySetPropertyByNameThroughCacheAtLocal(JSThread *thread, JSTaggedValue receiver,
344 JSTaggedValue key, JSTaggedValue value)
345 {
346 bool isTagged = true;
347 JSTaggedValue originValue = value;
348 auto *hclass = receiver.GetTaggedObject()->GetClass();
349 if (LIKELY(!hclass->IsDictionaryMode())) {
350 ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
351 int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
352 if (entry != -1) {
353 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
354 PropertyAttributes attr(layoutInfo->GetAttr(entry));
355 ASSERT(static_cast<int>(attr.GetOffset()) == entry);
356 if (UNLIKELY(attr.IsAccessor())) {
357 if (DefineSemantics(status)) {
358 return JSTaggedValue::Hole();
359 }
360 auto accessor = JSObject::Cast(receiver)->GetProperty(hclass, attr);
361 if (ShouldCallSetter(receiver, receiver, accessor, attr)) {
362 return CallSetter(thread, receiver, value, accessor);
363 }
364 }
365 if (UNLIKELY(!attr.IsWritable())) {
366 if (DefineSemantics(status)) {
367 return JSTaggedValue::Hole();
368 }
369 [[maybe_unused]] EcmaHandleScope handleScope(thread);
370 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
371 JSTaggedValue::Exception());
372 }
373 if (hclass->IsTS()) {
374 auto attrVal = JSObject::Cast(receiver)->GetProperty(hclass, attr);
375 if (attrVal.IsHole()) {
376 return JSTaggedValue::Hole();
377 }
378 JSHandle<JSObject> objHandle(thread, receiver);
379 JSHandle<JSTaggedValue> valueHandle(thread, value);
380 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
381 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
382 JSHandle<JSTaggedValue>(thread, key), valueHandle, attr);
383 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
384 receiver = objHandle.GetTaggedValue();
385 originValue = valueHandle.GetTaggedValue();
386 value = actualValue.value;
387 isTagged = actualValue.isTagged;
388 }
389 if (receiver.IsJSShared()) {
390 if (!ClassHelper::MatchFieldType(attr.GetSharedFieldType(), originValue)) {
391 THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
392 JSTaggedValue::Exception());
393 }
394 }
395 if (isTagged) {
396 JSObject::Cast(receiver)->SetProperty<true>(thread, hclass, attr, value);
397 } else {
398 JSObject::Cast(receiver)->SetProperty<false>(thread, hclass, attr, value);
399 }
400 return JSTaggedValue::Undefined();
401 }
402 }
403 return JSTaggedValue::Hole();
404 }
405
406 template<ObjectFastOperator::Status status>
SetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,SCheckMode sCheckMode)407 JSTaggedValue ObjectFastOperator::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
408 JSTaggedValue value, SCheckMode sCheckMode)
409 {
410 INTERPRETER_TRACE(thread, SetPropertyByName);
411 // property
412 JSTaggedValue holder = receiver;
413 int receiverHoleEntry = -1;
414 do {
415 auto *hclass = holder.GetTaggedObject()->GetClass();
416 JSType jsType = hclass->GetObjectType();
417 if (IsSpecialIndexedObj(jsType)) {
418 if (IsFastTypeArray(jsType)) {
419 JSTaggedValue res = FastSetTypeArrayProperty(thread, receiver, holder, key, value, jsType);
420 if (res.IsNull()) {
421 return JSTaggedValue::Hole();
422 } else if (UNLIKELY(!res.IsHole())) {
423 return res;
424 }
425 } else if (IsSpecialContainer(jsType)) {
426 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property on Container", JSTaggedValue::Exception());
427 } else {
428 return JSTaggedValue::Hole();
429 }
430 }
431 // UpdateRepresentation
432 if (LIKELY(!hclass->IsDictionaryMode())) {
433 ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode());
434 int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
435 if (entry != -1) {
436 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
437 PropertyAttributes attr(layoutInfo->GetAttr(entry));
438 ASSERT(static_cast<int>(attr.GetOffset()) == entry);
439 if (UNLIKELY(attr.IsAccessor())) {
440 if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
441 return JSTaggedValue::Hole();
442 }
443 auto accessor = JSObject::Cast(holder)->GetProperty(hclass, attr);
444 if (ShouldCallSetter(receiver, holder, accessor, attr)) {
445 return CallSetter(thread, receiver, value, accessor);
446 }
447 }
448 if (UNLIKELY(!attr.IsWritable())) {
449 if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
450 return JSTaggedValue::Hole();
451 }
452 [[maybe_unused]] EcmaHandleScope handleScope(thread);
453 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
454 JSTaggedValue::Exception());
455 }
456 if (hclass->IsTS()) {
457 auto attrVal = JSObject::Cast(holder)->GetProperty(hclass, attr);
458 if (attrVal.IsHole()) {
459 if (receiverHoleEntry == -1 && holder == receiver) {
460 receiverHoleEntry = entry;
461 }
462 if (UseOwn(status)) {
463 break;
464 }
465 holder = hclass->GetPrototype();
466 continue;
467 }
468 }
469 if (UNLIKELY(holder != receiver)) {
470 break;
471 }
472 if (holder.IsJSShared() && (sCheckMode == SCheckMode::CHECK)) {
473 if (!ClassHelper::MatchFieldType(attr.GetSharedFieldType(), value)) {
474 THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
475 JSTaggedValue::Exception());
476 }
477 }
478 JSHandle<JSObject> objHandle(thread, receiver);
479 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
480 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
481 JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), attr);
482 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
483 receiver = objHandle.GetTaggedValue();
484 hclass = objHandle->GetClass();
485 if (actualValue.isTagged) {
486 JSObject::Cast(receiver)->SetProperty<true>(thread, hclass, attr, actualValue.value);
487 } else {
488 JSObject::Cast(receiver)->SetProperty<false>(thread, hclass, attr, actualValue.value);
489 }
490 return JSTaggedValue::Undefined();
491 }
492 } else {
493 TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject());
494 ASSERT(properties->IsDictionaryMode());
495 NameDictionary *dict = NameDictionary::Cast(properties);
496 int entry = dict->FindEntry(key);
497 if (entry != -1) {
498 auto attr = dict->GetAttributes(entry);
499 if (UNLIKELY(attr.IsAccessor())) {
500 if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
501 return JSTaggedValue::Hole();
502 }
503 auto accessor = dict->GetValue(entry);
504 if (ShouldCallSetter(receiver, holder, accessor, attr)) {
505 return CallSetter(thread, receiver, value, accessor);
506 }
507 }
508 if (UNLIKELY(!attr.IsWritable())) {
509 if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
510 return JSTaggedValue::Hole();
511 }
512 [[maybe_unused]] EcmaHandleScope handleScope(thread);
513 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
514 JSTaggedValue::Exception());
515 }
516 if (UNLIKELY(holder != receiver)) {
517 break;
518 }
519 if ((sCheckMode == SCheckMode::CHECK) && holder.IsJSShared()) {
520 if (!ClassHelper::MatchFieldType(attr.GetDictSharedFieldType(), value)) {
521 THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
522 JSTaggedValue::Exception());
523 }
524 }
525 dict->UpdateValue(thread, entry, value);
526 return JSTaggedValue::Undefined();
527 }
528 }
529 if (UseOwn(status) || DefineSemantics(status)) {
530 break;
531 }
532 holder = hclass->GetPrototype();
533 } while (holder.IsHeapObject());
534
535 if (receiverHoleEntry != -1) {
536 auto *receiverHClass = receiver.GetTaggedObject()->GetClass();
537 LayoutInfo *receiverLayoutInfo = LayoutInfo::Cast(receiverHClass->GetLayout().GetTaggedObject());
538 PropertyAttributes attr(receiverLayoutInfo->GetAttr(receiverHoleEntry));
539 JSHandle<JSObject> objHandle(thread, receiver);
540 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
541 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
542 JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), attr);
543 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
544 receiver = objHandle.GetTaggedValue();
545 receiverHClass = objHandle->GetClass();
546 if (actualValue.isTagged) {
547 JSObject::Cast(receiver)->SetProperty<true>(thread, receiverHClass, attr, actualValue.value);
548 } else {
549 JSObject::Cast(receiver)->SetProperty<false>(thread, receiverHClass, attr, actualValue.value);
550 }
551 return JSTaggedValue::Undefined();
552 }
553
554 [[maybe_unused]] EcmaHandleScope handleScope(thread);
555 JSHandle<JSObject> objHandle(thread, receiver);
556 JSHandle<JSTaggedValue> keyHandle(thread, key);
557 JSHandle<JSTaggedValue> valueHandle(thread, value);
558
559 if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) {
560 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible),
561 JSTaggedValue::Exception());
562 }
563 ASSERT(!receiver.IsJSShared());
564 PropertyAttributes attr = PropertyAttributes::Default();
565 AddPropertyByName(thread, objHandle, keyHandle, valueHandle, attr);
566 return JSTaggedValue::Undefined();
567 }
568
569 template <ObjectFastOperator::Status status>
GetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)570 JSTaggedValue ObjectFastOperator::GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
571 {
572 INTERPRETER_TRACE(thread, GetPropertyByIndex);
573 [[maybe_unused]] EcmaHandleScope handleScope(thread);
574 JSTaggedValue holder = receiver;
575 do {
576 auto *hclass = holder.GetTaggedObject()->GetClass();
577 JSType jsType = hclass->GetObjectType();
578 if (IsSpecialIndexedObj(jsType)) {
579 if (jsType == JSType::JS_TYPED_ARRAY) {
580 return JSTaggedValue::Hole();
581 }
582 if (IsFastTypeArray(jsType)) {
583 return JSTypedArray::FastGetPropertyByIndex(thread, holder, index, jsType);
584 }
585 if (IsSpecialContainer(jsType)) {
586 return GetContainerProperty(thread, holder, index, jsType);
587 }
588 if (IsString(jsType)) {
589 if (index < EcmaStringAccessor(holder).GetLength()) {
590 EcmaString *subStr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
591 JSHandle<EcmaString>(thread, holder), index, 1);
592 return JSTaggedValue(subStr);
593 }
594 }
595 return JSTaggedValue::Hole();
596 }
597 JSHandle<JSObject> currentHolder(thread, holder);
598 if (!hclass->IsDictionaryElement()) {
599 ASSERT(!ElementAccessor::IsDictionaryMode(currentHolder));
600 if (index < ElementAccessor::GetElementsLength(currentHolder)) {
601 JSTaggedValue value = ElementAccessor::Get(currentHolder, index);
602 if (!value.IsHole()) {
603 return value;
604 }
605 }
606 } else {
607 TaggedArray *elements = TaggedArray::Cast(currentHolder->GetElements().GetTaggedObject());
608 NumberDictionary *dict = NumberDictionary::Cast(elements);
609 int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index)));
610 if (entry != -1) {
611 auto attr = dict->GetAttributes(entry);
612 auto value = dict->GetValue(entry);
613 if (UNLIKELY(attr.IsAccessor())) {
614 return CallGetter(thread, receiver, holder, value);
615 }
616 ASSERT(!value.IsAccessor());
617 return value;
618 }
619 }
620 if (UseOwn(status)) {
621 break;
622 }
623 holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype();
624 } while (holder.IsHeapObject());
625
626 // not found
627 return JSTaggedValue::Undefined();
628 }
629
630 template <ObjectFastOperator::Status status>
SetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)631 JSTaggedValue ObjectFastOperator::SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
632 JSTaggedValue value)
633 {
634 INTERPRETER_TRACE(thread, SetPropertyByIndex);
635 JSTaggedValue holder = receiver;
636 do {
637 auto *hclass = holder.GetTaggedObject()->GetClass();
638 JSType jsType = hclass->GetObjectType();
639 if (IsSpecialIndexedObj(jsType)) {
640 if (jsType == JSType::JS_TYPED_ARRAY) {
641 return JSTaggedValue::Hole();
642 }
643 if (IsFastTypeArray(jsType)) {
644 CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
645 return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType);
646 }
647 if (IsSpecialContainer(jsType)) {
648 if (DefineSemantics(status)) {
649 return JSTaggedValue::Hole();
650 }
651 return SetContainerProperty(thread, holder, index, value, jsType);
652 }
653 return JSTaggedValue::Hole();
654 }
655 JSHandle<JSObject> arrayHandler(thread, holder);
656 TaggedArray *elements = TaggedArray::Cast(arrayHandler->GetElements().GetTaggedObject());
657 if (!hclass->IsDictionaryElement()) {
658 ASSERT(!elements->IsDictionaryMode());
659 if (UNLIKELY(holder != receiver)) {
660 break;
661 }
662 if (index < elements->GetLength()) {
663 JSTaggedValue oldValue = ElementAccessor::Get(arrayHandler, index);
664 if (!oldValue.IsHole()) {
665 if (holder.IsJSCOWArray()) {
666 [[maybe_unused]] EcmaHandleScope handleScope(thread);
667 JSHandle<JSArray> holderHandler(thread, holder);
668 JSHandle<JSObject> obj(thread, holder);
669 JSHandle<JSTaggedValue> valueHandle(thread, value);
670 // CheckAndCopyArray may cause gc.
671 JSArray::CheckAndCopyArray(thread, holderHandler);
672 ElementAccessor::Set(thread, obj, index, valueHandle, true);
673 return JSTaggedValue::Undefined();
674 }
675 JSHandle<JSTaggedValue> valueHandle(thread, value);
676 ElementAccessor::Set(thread, arrayHandler, index, valueHandle, true);
677 return JSTaggedValue::Undefined();
678 }
679 }
680 } else {
681 NumberDictionary *dict = NumberDictionary::Cast(elements);
682 int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index)));
683 if (entry != -1) {
684 auto attr = dict->GetAttributes(entry);
685 if (UNLIKELY(!attr.IsWritable() || !attr.IsConfigurable())) {
686 return JSTaggedValue::Hole();
687 }
688 if (UNLIKELY(holder != receiver)) {
689 break;
690 }
691 if (UNLIKELY(attr.IsAccessor())) {
692 if (DefineSemantics(status)) {
693 return JSTaggedValue::Hole();
694 }
695 auto accessor = dict->GetValue(entry);
696 if (ShouldCallSetter(receiver, holder, accessor, attr)) {
697 return CallSetter(thread, receiver, value, accessor);
698 }
699 }
700 dict->UpdateValue(thread, entry, value);
701 return JSTaggedValue::Undefined();
702 }
703 return JSTaggedValue::Hole();
704 }
705 if (UseOwn(status) || DefineSemantics(status)) {
706 break;
707 }
708 holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype();
709 } while (holder.IsHeapObject());
710
711 return AddPropertyByIndex(thread, receiver, index, value);
712 }
713
714 template <ObjectFastOperator::Status status>
GetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)715 JSTaggedValue ObjectFastOperator::GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
716 {
717 INTERPRETER_TRACE(thread, GetPropertyByValue);
718 if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) {
719 return JSTaggedValue::Hole();
720 }
721 // fast path
722 auto index = TryToElementsIndex(key);
723 if (LIKELY(index >= 0)) {
724 return GetPropertyByIndex<status>(thread, receiver, index);
725 }
726 if (!key.IsNumber()) {
727 if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
728 // update string stable
729 [[maybe_unused]] EcmaHandleScope handleScope(thread);
730 JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
731 key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
732 // Maybe moved by GC
733 receiver = receiverHandler.GetTaggedValue();
734 }
735 return ObjectFastOperator::GetPropertyByName<status>(thread, receiver, key);
736 }
737 return JSTaggedValue::Hole();
738 }
739
740 template<ObjectFastOperator::Status status>
SetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,SCheckMode sCheckMode)741 JSTaggedValue ObjectFastOperator::SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
742 JSTaggedValue value, SCheckMode sCheckMode)
743 {
744 INTERPRETER_TRACE(thread, SetPropertyByValue);
745 if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) {
746 return JSTaggedValue::Hole();
747 }
748 // fast path
749 auto index = TryToElementsIndex(key);
750 if (LIKELY(index >= 0)) {
751 return SetPropertyByIndex<status>(thread, receiver, index, value);
752 }
753 if (!key.IsNumber()) {
754 if (key.IsString()) {
755 if (!EcmaStringAccessor(key).IsInternString()) {
756 // update string stable
757 [[maybe_unused]] EcmaHandleScope handleScope(thread);
758 JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
759 JSHandle<JSTaggedValue> valueHandler(thread, value);
760 key = JSTaggedValue(
761 thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
762 // Maybe moved by GC
763 receiver = receiverHandler.GetTaggedValue();
764 value = valueHandler.GetTaggedValue();
765 }
766 }
767 ObjectOperator::UpdateDetector(thread, receiver, key);
768 return ObjectFastOperator::SetPropertyByName<status>(thread, receiver, key, value, sCheckMode);
769 }
770 return JSTaggedValue::Hole();
771 }
772
FastSetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,SCheckMode sCheckMode)773 bool ObjectFastOperator::FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
774 JSTaggedValue value, SCheckMode sCheckMode)
775 {
776 INTERPRETER_TRACE(thread, FastSetPropertyByValue);
777 JSTaggedValue result = ObjectFastOperator::SetPropertyByValue(thread, receiver, key, value, sCheckMode);
778 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
779 if (!result.IsHole()) {
780 return !result.IsException();
781 }
782 return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
783 JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value),
784 true, sCheckMode);
785 }
786
FastSetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)787 bool ObjectFastOperator::FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
788 JSTaggedValue value)
789 {
790 INTERPRETER_TRACE(thread, FastSetPropertyByIndex);
791 JSTaggedValue result = ObjectFastOperator::SetPropertyByIndex(thread, receiver, index, value);
792 if (!result.IsHole()) {
793 return !result.IsException();
794 }
795 return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver), index,
796 JSHandle<JSTaggedValue>(thread, value), true);
797 }
798
FastGetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)799 JSTaggedValue ObjectFastOperator::FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver,
800 JSTaggedValue key)
801 {
802 INTERPRETER_TRACE(thread, FastGetPropertyByName);
803 ASSERT(key.IsStringOrSymbol());
804 if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
805 JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
806 key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
807 // Maybe moved by GC
808 receiver = receiverHandler.GetTaggedValue();
809 }
810 JSTaggedValue result = ObjectFastOperator::GetPropertyByName<Status::GetInternal>(thread, receiver, key);
811 if (result.IsHole()) {
812 return JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
813 JSHandle<JSTaggedValue>(thread, key)).GetValue().GetTaggedValue();
814 }
815 return result;
816 }
817
FastGetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,SCheckMode sCheckMode)818 JSTaggedValue ObjectFastOperator::FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
819 SCheckMode sCheckMode)
820 {
821 INTERPRETER_TRACE(thread, FastGetPropertyByValue);
822 JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
823 JSHandle<JSTaggedValue> keyHandler(thread, key);
824 JSTaggedValue result = ObjectFastOperator::GetPropertyByValue(thread, receiver, key);
825 if (result.IsHole()) {
826 return JSTaggedValue::GetProperty(thread, receiverHandler, keyHandler, sCheckMode).GetValue().GetTaggedValue();
827 }
828 return result;
829 }
830
FastGetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)831 JSTaggedValue ObjectFastOperator::FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
832 {
833 INTERPRETER_TRACE(thread, FastGetPropertyByIndex);
834 JSTaggedValue result = ObjectFastOperator::GetPropertyByIndex(thread, receiver, index);
835 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
836 if (result.IsHole()) {
837 return JSTaggedValue::GetProperty(thread,
838 JSHandle<JSTaggedValue>(thread, receiver), index).GetValue().GetTaggedValue();
839 }
840 return result;
841 }
842
FastParseDate(const EcmaString * str)843 JSTaggedValue ObjectFastOperator::FastParseDate(const EcmaString *str)
844 {
845 int year = 0;
846 int month = 1;
847 int date = 1;
848 int index = 0;
849
850 CVector<uint8_t> tmpBuf;
851 EcmaStringAccessor strAccessor(const_cast<EcmaString *>(str));
852 int len = static_cast<int>(strAccessor.GetLength());
853 auto data = reinterpret_cast<const char *>(strAccessor.GetUtf8DataFlat(str, tmpBuf));
854 if (!GetNumFromString(data, len, &index, &year)) {
855 return JSTaggedValue::Hole();
856 }
857 if (!GetNumFromString(data, len, &index, &month)) {
858 return JSTaggedValue::Hole();
859 }
860 if (!GetNumFromString(data, len, &index, &date)) {
861 return JSTaggedValue::Hole();
862 }
863 if (month < 1 || month > JSDate::MONTH_PER_YEAR) {
864 return JSTaggedValue::Hole();
865 }
866 if (date < 1 || date > JSDate::MAX_DAYS_MONTH) {
867 return JSTaggedValue::Hole();
868 }
869 double day = JSDate::MakeDay(year, month - 1, date);
870 double timeValue = JSDate::TimeClip(JSDate::MakeDate(day, 0));
871 return JSTaggedValue(timeValue);
872 }
873
AddPropertyByName(JSThread * thread,JSHandle<JSObject> objHandle,JSHandle<JSTaggedValue> keyHandle,JSHandle<JSTaggedValue> valueHandle,PropertyAttributes attr)874 PropertyAttributes ObjectFastOperator::AddPropertyByName(JSThread *thread, JSHandle<JSObject> objHandle,
875 JSHandle<JSTaggedValue> keyHandle,
876 JSHandle<JSTaggedValue> valueHandle,
877 PropertyAttributes attr)
878 {
879 INTERPRETER_TRACE(thread, AddPropertyByName);
880
881 if ((objHandle->IsJSArray() || objHandle->IsTypedArray()) &&
882 keyHandle.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString()) {
883 objHandle->GetJSHClass()->SetHasConstructor(true);
884 }
885 int32_t nextInlinedPropsIndex = objHandle->GetJSHClass()->GetNextInlinedPropsIndex();
886 if (nextInlinedPropsIndex >= 0) {
887 attr.SetOffset(nextInlinedPropsIndex);
888 attr.SetIsInlinedProps(true);
889 attr.SetRepresentation(Representation::TAGGED);
890 auto rep = PropertyAttributes::TranslateToRep(valueHandle.GetTaggedValue());
891 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
892 JSHClass::AddProperty(thread, objHandle, keyHandle, attr, rep);
893 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
894 oldKind = objHandle->GetJSHClass()->GetElementsKind();
895 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, keyHandle, valueHandle, attr);
896 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
897 if (actualValue.isTagged) {
898 objHandle->SetPropertyInlinedProps<true>(thread, nextInlinedPropsIndex, valueHandle.GetTaggedValue());
899 } else {
900 objHandle->SetPropertyInlinedProps<false>(thread, nextInlinedPropsIndex, actualValue.value);
901 }
902 return attr;
903 }
904
905 JSMutableHandle<TaggedArray> array(thread, objHandle->GetProperties());
906 uint32_t length = array->GetLength();
907 if (length == 0) {
908 length = JSObject::MIN_PROPERTIES_LENGTH;
909 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
910 array.Update(factory->NewTaggedArray(length).GetTaggedValue());
911 objHandle->SetProperties(thread, array.GetTaggedValue());
912 }
913
914 if (!array->IsDictionaryMode()) {
915 attr.SetIsInlinedProps(false);
916 uint32_t nonInlinedProps = static_cast<uint32_t>(objHandle->GetJSHClass()->GetNextNonInlinedPropsIndex());
917 ASSERT(length >= nonInlinedProps);
918 uint32_t numberOfProps = objHandle->GetJSHClass()->NumberOfProps();
919 if (UNLIKELY(numberOfProps >= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
920 // change to dictionary and add one.
921 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread, objHandle));
922 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, attr);
923 JSHandle<NameDictionary> newDict =
924 NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr);
925 objHandle->SetProperties(thread, newDict);
926 // index is not essential when fastMode is false;
927 return attr;
928 }
929 // if array is full, grow array or change to dictionary mode
930 if (length == nonInlinedProps) {
931 uint32_t maxNonInlinedFastPropsCapacity = objHandle->GetNonInlinedFastPropsCapacity();
932 // Grow properties array size
933 uint32_t capacity = JSObject::ComputeNonInlinedFastPropsCapacity(thread, length,
934 maxNonInlinedFastPropsCapacity);
935 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
936 array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue());
937 objHandle->SetProperties(thread, array.GetTaggedValue());
938 }
939
940 attr.SetOffset(nonInlinedProps + objHandle->GetJSHClass()->GetInlinedProperties());
941 attr.SetRepresentation(Representation::TAGGED);
942 auto rep = PropertyAttributes::TranslateToRep(valueHandle.GetTaggedValue());
943 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
944 JSHClass::AddProperty(thread, objHandle, keyHandle, attr, rep);
945 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
946 oldKind = objHandle->GetJSHClass()->GetElementsKind();
947 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, keyHandle, valueHandle, attr);
948 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
949 if (actualValue.isTagged) {
950 array->Set<true>(thread, nonInlinedProps, valueHandle.GetTaggedValue());
951 } else {
952 array->Set<false>(thread, nonInlinedProps, actualValue.value);
953 }
954 } else {
955 JSHandle<NameDictionary> dictHandle(array);
956 JSHandle<NameDictionary> newDict =
957 NameDictionary::PutIfAbsent(thread, dictHandle, keyHandle, valueHandle, attr);
958 objHandle->SetProperties(thread, newDict);
959 }
960 return attr;
961 }
962
CallGetter(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue value)963 JSTaggedValue ObjectFastOperator::CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder,
964 JSTaggedValue value)
965 {
966 INTERPRETER_TRACE(thread, CallGetter);
967 // Accessor
968 [[maybe_unused]] EcmaHandleScope handleScope(thread);
969 AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject());
970 if (UNLIKELY(accessor->IsInternal())) {
971 JSHandle<JSObject> objHandle(thread, holder);
972 return accessor->CallInternalGet(thread, objHandle);
973 }
974 JSHandle<JSTaggedValue> objHandle(thread, receiver);
975 return JSObject::CallGetter(thread, accessor, objHandle);
976 }
977
CallSetter(JSThread * thread,JSTaggedValue receiver,JSTaggedValue value,JSTaggedValue accessorValue)978 JSTaggedValue ObjectFastOperator::CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value,
979 JSTaggedValue accessorValue)
980 {
981 INTERPRETER_TRACE(thread, CallSetter);
982 // Accessor
983 [[maybe_unused]] EcmaHandleScope handleScope(thread);
984 JSHandle<JSTaggedValue> objHandle(thread, receiver);
985 JSHandle<JSTaggedValue> valueHandle(thread, value);
986
987 auto accessor = AccessorData::Cast(accessorValue.GetTaggedObject());
988 bool success = JSObject::CallSetter(thread, *accessor, objHandle, valueHandle, true);
989 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
990 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
991 }
992
ShouldCallSetter(JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue accessorValue,PropertyAttributes attr)993 bool ObjectFastOperator::ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue,
994 PropertyAttributes attr)
995 {
996 if (!AccessorData::Cast(accessorValue.GetTaggedObject())->IsInternal()) {
997 return true;
998 }
999 if (receiver != holder) {
1000 return false;
1001 }
1002 return attr.IsWritable();
1003 }
1004
IsSpecialIndexedObj(JSType jsType)1005 bool ObjectFastOperator::IsSpecialIndexedObj(JSType jsType)
1006 {
1007 return jsType > JSType::JS_ARRAY;
1008 }
1009
IsJSSharedArray(JSType jsType)1010 bool ObjectFastOperator::IsJSSharedArray(JSType jsType)
1011 {
1012 return jsType == JSType::JS_SHARED_ARRAY;
1013 }
1014
IsFastTypeArray(JSType jsType)1015 bool ObjectFastOperator::IsFastTypeArray(JSType jsType)
1016 {
1017 return jsType >= JSType::JS_TYPED_ARRAY_FIRST && jsType <= JSType::JS_TYPED_ARRAY_LAST;
1018 }
1019
IsString(JSType jsType)1020 bool ObjectFastOperator::IsString(JSType jsType)
1021 {
1022 return JSType::STRING_FIRST <= jsType && jsType <= JSType::STRING_LAST;
1023 }
1024
IsJSPrimitiveRef(JSType jsType)1025 bool ObjectFastOperator::IsJSPrimitiveRef(JSType jsType)
1026 {
1027 return jsType == JSType::JS_PRIMITIVE_REF;
1028 }
1029
FastGetTypeArrayProperty(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue key,JSType jsType)1030 JSTaggedValue ObjectFastOperator::FastGetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver,
1031 JSTaggedValue holder,
1032 JSTaggedValue key, JSType jsType)
1033 {
1034 CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
1035 JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString();
1036 if (UNLIKELY(negativeZero == key)) {
1037 return JSTaggedValue::Undefined();
1038 }
1039 uint32_t index = 0;
1040 if (TryStringOrSymbolToIndex(key, &index)) {
1041 if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) {
1042 return JSTaggedValue::Null();
1043 }
1044 return JSTypedArray::FastGetPropertyByIndex(thread, receiver, index, jsType);
1045 }
1046 return JSTaggedValue::Hole();
1047 }
1048
TryStringOrSymbolToIndex(JSTaggedValue key,uint32_t * output)1049 bool ObjectFastOperator::TryStringOrSymbolToIndex(JSTaggedValue key, uint32_t *output)
1050 {
1051 if (key.IsSymbol()) {
1052 return false;
1053 }
1054 auto strObj = static_cast<EcmaString *>(key.GetTaggedObject());
1055 return EcmaStringAccessor(strObj).ToTypedArrayIndex(output);
1056 }
1057
FastSetTypeArrayProperty(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue key,JSTaggedValue value,JSType jsType)1058 JSTaggedValue ObjectFastOperator::FastSetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver,
1059 JSTaggedValue holder, JSTaggedValue key,
1060 JSTaggedValue value, JSType jsType)
1061 {
1062 CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
1063 JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString();
1064 if (UNLIKELY(negativeZero == key)) {
1065 if (value.IsECMAObject()) {
1066 return JSTaggedValue::Null();
1067 }
1068 return JSTaggedValue::Undefined();
1069 }
1070 uint32_t index = 0;
1071 if (TryStringOrSymbolToIndex(key, &index)) {
1072 if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) {
1073 return JSTaggedValue::Null();
1074 }
1075 return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType);
1076 }
1077 return JSTaggedValue::Hole();
1078 }
1079
IsSpecialContainer(JSType jsType)1080 bool ObjectFastOperator::IsSpecialContainer(JSType jsType)
1081 {
1082 return jsType >= JSType::JS_API_ARRAY_LIST && jsType <= JSType::JS_API_QUEUE;
1083 }
1084
GetContainerProperty(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSType jsType)1085 JSTaggedValue ObjectFastOperator::GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1086 JSType jsType)
1087 {
1088 JSTaggedValue res = JSTaggedValue::Undefined();
1089 switch (jsType) {
1090 case JSType::JS_API_ARRAY_LIST:
1091 res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Get(thread, index);
1092 break;
1093 case JSType::JS_API_QUEUE:
1094 res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Get(thread, index);
1095 break;
1096 case JSType::JS_API_PLAIN_ARRAY:
1097 res = JSAPIPlainArray::Cast(receiver.GetTaggedObject())->Get(JSTaggedValue(index));
1098 break;
1099 case JSType::JS_API_DEQUE:
1100 res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Get(index);
1101 break;
1102 case JSType::JS_API_STACK:
1103 res = JSAPIStack::Cast(receiver.GetTaggedObject())->Get(index);
1104 break;
1105 case JSType::JS_API_VECTOR: {
1106 auto self = JSHandle<JSTaggedValue>(thread, receiver);
1107 res = JSAPIVector::Get(thread, JSHandle<JSAPIVector>::Cast(self), index);
1108 break;
1109 }
1110 case JSType::JS_API_LIST: {
1111 res = JSAPIList::Cast(receiver.GetTaggedObject())->Get(index);
1112 break;
1113 }
1114 case JSType::JS_API_BITVECTOR: {
1115 res = JSAPIBitVector::Cast(receiver.GetTaggedObject())->Get(thread, index);
1116 break;
1117 }
1118 case JSType::JS_API_LINKED_LIST: {
1119 res = JSAPILinkedList::Cast(receiver.GetTaggedObject())->Get(index);
1120 break;
1121 }
1122 default:
1123 break;
1124 }
1125 return res;
1126 }
1127
SetContainerProperty(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value,JSType jsType)1128 JSTaggedValue ObjectFastOperator::SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1129 JSTaggedValue value, JSType jsType)
1130 {
1131 JSTaggedValue res = JSTaggedValue::Undefined();
1132 switch (jsType) {
1133 case JSType::JS_API_ARRAY_LIST:
1134 res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1135 break;
1136 case JSType::JS_API_QUEUE:
1137 res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1138 break;
1139 case JSType::JS_API_PLAIN_ARRAY: {
1140 JSHandle<JSAPIPlainArray> plainArray(thread, receiver);
1141 res = JSAPIPlainArray::Set(thread, plainArray, index, value);
1142 break;
1143 }
1144 case JSType::JS_API_DEQUE:
1145 res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1146 break;
1147 case JSType::JS_API_STACK:
1148 res = JSAPIStack::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1149 break;
1150 case JSType::JS_API_VECTOR:
1151 res = JSAPIVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1152 break;
1153 case JSType::JS_API_BITVECTOR:
1154 res = JSAPIBitVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1155 break;
1156 case JSType::JS_API_LIST: {
1157 JSHandle<JSAPIList> singleList(thread, receiver);
1158 res = JSAPIList::Set(thread, singleList, index, JSHandle<JSTaggedValue>(thread, value));
1159 break;
1160 }
1161 case JSType::JS_API_LINKED_LIST: {
1162 JSHandle<JSAPILinkedList> doubleList(thread, receiver);
1163 res = JSAPILinkedList::Set(thread, doubleList, index, JSHandle<JSTaggedValue>(thread, value));
1164 break;
1165 }
1166 default:
1167 break;
1168 }
1169 return res;
1170 }
1171
AddPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)1172 JSTaggedValue ObjectFastOperator::AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1173 JSTaggedValue value)
1174 {
1175 INTERPRETER_TRACE(thread, AddPropertyByIndex);
1176 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1177 // fixme(hzzhouzebin) this makes SharedArray's frozen no sense.
1178 if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible()) && !receiver.IsJSSharedArray()) {
1179 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible),
1180 JSTaggedValue::Exception());
1181 }
1182
1183 bool success = JSObject::AddElementInternal(thread, JSHandle<JSObject>(thread, receiver), index,
1184 JSHandle<JSTaggedValue>(thread, value), PropertyAttributes::Default());
1185 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
1186 }
1187
TryToElementsIndex(JSTaggedValue key)1188 int64_t ObjectFastOperator::TryToElementsIndex(JSTaggedValue key)
1189 {
1190 if (LIKELY(key.IsInt())) {
1191 return key.GetInt();
1192 }
1193 if (key.IsString()) {
1194 uint32_t index = 0;
1195 if (JSTaggedValue::StringToElementIndex(key, &index)) {
1196 return static_cast<int64_t>(index);
1197 }
1198 } else if (key.IsDouble()) {
1199 double number = key.GetDouble();
1200 auto integer = static_cast<int32_t>(number);
1201 if (number == integer) {
1202 return integer;
1203 }
1204 }
1205 return -1;
1206 }
1207
GetNumFromString(const char * str,int len,int * index,int * num)1208 bool ObjectFastOperator::GetNumFromString(const char *str, int len, int *index, int *num)
1209 {
1210 int indexStr = *index;
1211 char oneByte = 0;
1212 oneByte = str[indexStr];
1213 if (oneByte < '0' || oneByte > '9') {
1214 return false;
1215 }
1216 if (indexStr >= len) {
1217 return false;
1218 }
1219 int value = 0;
1220 while (indexStr < len) {
1221 oneByte = str[indexStr];
1222 int val = static_cast<int>(oneByte - '0');
1223 if (val >= 0 && val <= JSDate::NUM_NINE) {
1224 value = value * JSDate::TEN + val;
1225 indexStr++;
1226 } else if (oneByte != '-') {
1227 return false;
1228 } else {
1229 indexStr++;
1230 break;
1231 }
1232 }
1233 *num = value;
1234 *index = indexStr;
1235 return true;
1236 }
1237
FastGetPropertyByPorpsIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)1238 JSTaggedValue ObjectFastOperator::FastGetPropertyByPorpsIndex(JSThread *thread,
1239 JSTaggedValue receiver, uint32_t index)
1240 {
1241 JSTaggedValue value = JSTaggedValue::Hole();
1242 JSObject *obj = JSObject::Cast(receiver);
1243 TaggedArray *properties = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
1244 if (!properties->IsDictionaryMode()) {
1245 JSHClass *jsHclass = obj->GetJSHClass();
1246 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
1247 PropertyAttributes attr = layoutInfo->GetAttr(index);
1248 value = obj->GetProperty(jsHclass, attr);
1249 } else {
1250 NameDictionary *dict = NameDictionary::Cast(properties);
1251 value = dict->GetValue(index);
1252 }
1253 if (UNLIKELY(value.IsAccessor())) {
1254 return CallGetter(thread, JSTaggedValue(obj), JSTaggedValue(obj), value);
1255 }
1256 ASSERT(!value.IsAccessor());
1257 return value;
1258 }
1259 }
1260 #endif // ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H
1261