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