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