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