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