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