1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/js_stable_array.h"
17 #include "ecmascript/base/config.h"
18 #include "ecmascript/base/sort_helper.h"
19 #include "ecmascript/base/typed_array_helper-inl.h"
20 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
21
22 namespace panda::ecmascript {
23 using TypedArrayHelper = base::TypedArrayHelper;
24 using TypedArrayKind = base::TypedArrayKind;
25 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
26 using BuiltinsSendableArrayBuffer = builtins::BuiltinsSendableArrayBuffer;
27 template<TypedArrayKind typedArrayKind>
28 using BuiltinsArrayBufferType = base::BuiltinsArrayBufferType<typedArrayKind>;
29
Push(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)30 JSTaggedValue JSStableArray::Push(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
31 {
32 JSThread *thread = argv->GetThread();
33 uint32_t argc = argv->GetArgsNumber();
34 uint32_t oldLength = receiver->GetArrayLength();
35 uint32_t newLength = argc + oldLength;
36 JSHandle<JSObject> thisObjHandle(receiver);
37
38 if (newLength > ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
39 JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
40 }
41 bool needTransition = true;
42 for (uint32_t k = 0; k < argc; k++) {
43 JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
44 ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value, needTransition);
45 }
46 receiver->SetArrayLength(thread, newLength);
47
48 return JSTaggedValue(newLength);
49 }
50
Push(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)51 JSTaggedValue JSStableArray::Push(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
52 {
53 JSThread *thread = argv->GetThread();
54 uint32_t argc = argv->GetArgsNumber();
55 uint32_t oldLength = receiver->GetArrayLength();
56 uint32_t newLength = argc + oldLength;
57 JSHandle<JSObject> thisObjHandle(receiver);
58
59 if (newLength > ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
60 JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
61 }
62 bool needTransition = true;
63 for (uint32_t k = 0; k < argc; k++) {
64 JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
65 ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value, needTransition);
66 }
67 receiver->SetArrayLength(thread, newLength);
68
69 return JSTaggedValue(newLength);
70 }
71
Pop(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)72 JSTaggedValue JSStableArray::Pop(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
73 {
74 JSThread *thread = argv->GetThread();
75 uint32_t length = receiver->GetArrayLength();
76 if (length == 0) {
77 return JSTaggedValue::Undefined();
78 }
79 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
80 JSSharedArray::CheckAndCopyArray(thread, receiver);
81 JSHandle<JSObject> obj(receiver);
82 uint32_t capacity = ElementAccessor::GetElementsLength(thread, obj);
83 uint32_t index = length - 1;
84 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Hole());
85 if (index < capacity) {
86 result.Update(ElementAccessor::Get(thread, obj, index));
87 }
88 if (!result->IsHole()) {
89 if (TaggedArray::ShouldTrim(capacity, index)) {
90 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements(thread).GetTaggedObject());
91 elements->Trim(thread, index);
92 } else {
93 ElementAccessor::Set(thread, obj, index, holeHandle, false);
94 }
95 } else {
96 JSHandle<JSTaggedValue> thisObjVal(receiver);
97 result.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue());
98 }
99 receiver->SetArrayLength(thread, index);
100 return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
101 }
102
Pop(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)103 JSTaggedValue JSStableArray::Pop(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
104 {
105 JSThread *thread = argv->GetThread();
106 uint32_t length = receiver->GetArrayLength();
107 if (length == 0) {
108 return JSTaggedValue::Undefined();
109 }
110 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
111 JSArray::CheckAndCopyArray(thread, receiver);
112 JSHandle<JSObject> obj(receiver);
113 uint32_t capacity = ElementAccessor::GetElementsLength(thread, obj);
114 uint32_t index = length - 1;
115 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Hole());
116 if (index < capacity) {
117 result.Update(ElementAccessor::Get(thread, obj, index));
118 }
119 if (!result->IsHole()) {
120 if (TaggedArray::ShouldTrim(capacity, index)) {
121 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements(thread).GetTaggedObject());
122 elements->Trim(thread, index);
123 } else {
124 ElementAccessor::Set(thread, obj, index, holeHandle, false);
125 }
126 } else {
127 JSHandle<JSTaggedValue> thisObjVal(receiver);
128 result.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue());
129 }
130 receiver->SetArrayLength(thread, index);
131 return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
132 }
133
HandleArray(JSHandle<JSObject> & newArrayHandle,uint32_t & actualDeleteCount,JSThread * thread,uint32_t & start,JSHandle<JSObject> & thisObjHandle,JSHandle<JSTaggedValue> & holeHandle)134 void JSStableArray::HandleArray(JSHandle<JSObject> &newArrayHandle, uint32_t &actualDeleteCount,
135 JSThread *thread, uint32_t &start, JSHandle<JSObject> &thisObjHandle,
136 JSHandle<JSTaggedValue> &holeHandle)
137 {
138 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
139 if (actualDeleteCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
140 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
141 }
142
143 for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
144 if ((start + idx) >= ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
145 ElementAccessor::Set(thread, newArrayHandle, idx, holeHandle, true);
146 } else {
147 JSHandle<JSTaggedValue> valueHandle(thread, ElementAccessor::Get(thread, thisObjHandle, start + idx));
148 ElementAccessor::Set(thread, newArrayHandle, idx, valueHandle, true);
149 }
150 }
151 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
152 }
153
UpdateArrayCapacity(JSHandle<JSObject> & thisObjHandle,uint32_t & len,uint32_t & insertCount,uint32_t & actualDeleteCount,JSHandle<JSArray> & receiver,uint32_t & start,JSThread * thread,bool & needTransition,JSHandle<JSTaggedValue> & holeHandle,EcmaRuntimeCallInfo * argv,JSHandle<JSTaggedValue> & thisObjVal,JSHandle<JSTaggedValue> & lengthKey)154 JSTaggedValue JSStableArray::UpdateArrayCapacity(JSHandle<JSObject> &thisObjHandle, uint32_t &len,
155 uint32_t &insertCount, uint32_t &actualDeleteCount,
156 JSHandle<JSArray> &receiver, uint32_t &start,
157 JSThread *thread, bool &needTransition,
158 JSHandle<JSTaggedValue> &holeHandle,
159 EcmaRuntimeCallInfo *argv, JSHandle<JSTaggedValue> &thisObjVal,
160 JSHandle<JSTaggedValue> &lengthKey)
161 {
162 uint32_t oldCapacity = ElementAccessor::GetElementsLength(thread, thisObjHandle);
163 ASSERT(len + insertCount >= actualDeleteCount);
164 uint32_t newCapacity = len - actualDeleteCount + insertCount;
165 TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements(thread).GetTaggedObject());
166 JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
167 uint32_t argc = argv->GetArgsNumber();
168 if (newCapacity > oldCapacity) {
169 srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
170 }
171 if (insertCount < actualDeleteCount) {
172 JSArray::CheckAndCopyArray(thread, receiver);
173 srcElementsHandle.Update(receiver->GetElements(thread));
174 for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
175 JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Hole());
176 if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
177 element.Update(ElementAccessor::Get(thread, thisObjHandle, idx + actualDeleteCount));
178 }
179 if ((idx + insertCount) < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
180 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
181 }
182 }
183
184 if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
185 srcElementsHandle->Trim(thread, newCapacity);
186 } else {
187 for (uint32_t idx = newCapacity; idx < len; idx++) {
188 if (idx < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
189 ElementAccessor::Set(thread, thisObjHandle, idx, holeHandle, needTransition);
190 }
191 }
192 }
193 } else {
194 ASSERT(len >= actualDeleteCount);
195 for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
196 JSHandle<JSTaggedValue> element(thread,
197 ElementAccessor::Get(thread, thisObjHandle, idx + actualDeleteCount - 1));
198 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount - 1, element, needTransition);
199 }
200 }
201
202 for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
203 ElementAccessor::Set(thread, thisObjHandle, idx, argv->GetCallArg(i), needTransition);
204 }
205
206 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
207 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
208 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
209 return JSTaggedValue::Undefined();
210 }
211
Splice(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount,JSHandle<JSObject> newArrayHandle,uint32_t len)212 JSTaggedValue JSStableArray::Splice(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
213 uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount,
214 JSHandle<JSObject> newArrayHandle, uint32_t len)
215 {
216 JSThread *thread = argv->GetThread();
217
218 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
219 JSHandle<JSObject> thisObjHandle(receiver);
220 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
221 JSArray::CheckAndCopyArray(thread, receiver);
222 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
223 bool needTransition = true;
224 if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
225 HandleArray(newArrayHandle, actualDeleteCount, thread, start, thisObjHandle, holeHandle);
226 } else {
227 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
228 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
229 uint32_t k = 0;
230 while (k < actualDeleteCount) {
231 uint32_t from = start + k;
232 fromKey.Update(JSTaggedValue(from));
233 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
234 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
235 if (exists) {
236 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
237 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
238 toKey.Update(JSTaggedValue(k));
239 if (newArrayHandle->IsJSProxy()) {
240 toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
241 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
242 }
243 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
244 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
245 }
246 k++;
247 }
248
249 JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
250 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
251 true);
252 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
253 }
254 UpdateArrayCapacity(thisObjHandle, len, insertCount, actualDeleteCount, receiver, start,
255 thread, needTransition, holeHandle, argv, thisObjVal, lengthKey);
256 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
257 return newArrayHandle.GetTaggedValue();
258 }
259
Splice(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount,JSHandle<JSObject> newArrayHandle,uint32_t len)260 JSTaggedValue JSStableArray::Splice(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv,
261 uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount,
262 JSHandle<JSObject> newArrayHandle, uint32_t len)
263 {
264 JSThread *thread = argv->GetThread();
265 uint32_t argc = argv->GetArgsNumber();
266
267 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
268 JSHandle<JSObject> thisObjHandle(receiver);
269 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
270 JSSharedArray::CheckAndCopyArray(thread, receiver);
271 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
272 TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements(thread).GetTaggedObject());
273 JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
274 bool needTransition = true;
275 if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
276 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
277 if (actualDeleteCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
278 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
279 }
280
281 for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
282 if ((start + idx) >= ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
283 ElementAccessor::Set(thread, newArrayHandle, idx, holeHandle, needTransition);
284 } else {
285 JSHandle<JSTaggedValue> valueHandle(thread, ElementAccessor::Get(thread, thisObjHandle, start + idx));
286 ElementAccessor::Set(thread, newArrayHandle, idx, valueHandle, needTransition);
287 }
288 }
289 JSHandle<JSSharedArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
290 } else {
291 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
292 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
293 uint32_t k = 0;
294 while (k < actualDeleteCount) {
295 uint32_t from = start + k;
296 fromKey.Update(JSTaggedValue(from));
297 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
298 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
299 if (exists) {
300 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
301 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
302 toKey.Update(JSTaggedValue(k));
303 if (newArrayHandle->IsJSProxy()) {
304 toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
305 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
306 }
307 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
308 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
309 }
310 k++;
311 }
312
313 JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
314 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
315 true);
316 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
317 }
318 uint32_t oldCapacity = ElementAccessor::GetElementsLength(thread, thisObjHandle);
319 ASSERT(len + insertCount >= actualDeleteCount);
320 uint32_t newCapacity = len - actualDeleteCount + insertCount;
321 if (newCapacity > oldCapacity) {
322 srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
323 }
324 if (insertCount < actualDeleteCount) {
325 JSSharedArray::CheckAndCopyArray(thread, receiver);
326 srcElementsHandle.Update(receiver->GetElements(thread));
327 for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
328 JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Hole());
329 if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
330 element.Update(ElementAccessor::Get(thread, thisObjHandle, idx + actualDeleteCount));
331 }
332 if ((idx + insertCount) < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
333 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
334 }
335 }
336
337 if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
338 srcElementsHandle->Trim(thread, newCapacity);
339 } else {
340 for (uint32_t idx = newCapacity; idx < len; idx++) {
341 if (idx < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
342 ElementAccessor::Set(thread, thisObjHandle, idx, holeHandle, needTransition);
343 }
344 }
345 }
346 } else {
347 ASSERT(len >= actualDeleteCount);
348 for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
349 JSHandle<JSTaggedValue> element(thread,
350 ElementAccessor::Get(thread, thisObjHandle, idx + actualDeleteCount - 1));
351 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount - 1, element, needTransition);
352 }
353 }
354
355 for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
356 ElementAccessor::Set(thread, thisObjHandle, idx, argv->GetCallArg(i), needTransition);
357 }
358
359 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
360 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
361 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
362 return newArrayHandle.GetTaggedValue();
363 }
364
Shift(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)365 JSTaggedValue JSStableArray::Shift(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
366 {
367 JSThread *thread = argv->GetThread();
368 JSHandle<JSObject> thisObjHandle(receiver);
369 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
370 uint32_t length = receiver->GetArrayLength();
371 if (length == 0) {
372 return JSTaggedValue::Undefined();
373 }
374 JSSharedArray::CheckAndCopyArray(thread, receiver);
375 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements(thread).GetTaggedObject());
376 JSHandle<JSTaggedValue> result(thread, ElementAccessor::Get(thread, thisObjHandle, 0));
377 bool needTransition = false;
378 for (uint32_t k = 1; k < length; k++) {
379 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, k));
380 ElementAccessor::Set(thread, thisObjHandle, k - 1, kValue, needTransition);
381 }
382 uint32_t capacity = ElementAccessor::GetElementsLength(thread, thisObjHandle);
383 uint32_t index = length - 1;
384 if (TaggedArray::ShouldTrim(capacity, index)) {
385 elements->Trim(thread, index);
386 } else {
387 ElementAccessor::Set(thread, thisObjHandle, index, holeHandle, needTransition);
388 }
389 receiver->SetArrayLength(thread, index);
390 return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
391 }
392
Shift(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)393 JSTaggedValue JSStableArray::Shift(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
394 {
395 JSThread *thread = argv->GetThread();
396 JSHandle<JSObject> thisObjHandle(receiver);
397 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
398 uint32_t length = receiver->GetArrayLength();
399 if (length == 0) {
400 return JSTaggedValue::Undefined();
401 }
402 JSArray::CheckAndCopyArray(thread, receiver);
403 JSHandle<TaggedArray> elements(thread, TaggedArray::Cast(receiver->GetElements(thread).GetTaggedObject()));
404 JSHandle<JSTaggedValue> result(thread, ElementAccessor::Get(thread, thisObjHandle, 0));
405 bool needTransition = false;
406 for (uint32_t k = 1; k < length; k++) {
407 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, k));
408 ElementAccessor::Set(thread, thisObjHandle, k - 1, kValue, needTransition);
409 }
410 uint32_t capacity = ElementAccessor::GetElementsLength(thread, thisObjHandle);
411 uint32_t index = length - 1;
412 if (TaggedArray::ShouldTrim(capacity, index)) {
413 elements->Trim(thread, index);
414 } else {
415 ElementAccessor::Set(thread, thisObjHandle, index, holeHandle, needTransition);
416 }
417 receiver->SetArrayLength(thread, index);
418 return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
419 }
420
421 #if ENABLE_NEXT_OPTIMIZATION
WorthUseTreeString(uint32_t sepLength,size_t allocateLength,uint32_t len)422 bool JSStableArray::WorthUseTreeString(uint32_t sepLength, size_t allocateLength, uint32_t len)
423 {
424 if (allocateLength >= TREE_STRING_THRESHOLD) {
425 // if sepLength is 0, means all the elements in treeString is len -1;
426 // otherwise, the num of elements is (len-1)(string in vector) + (len -1)(num of seps)
427 size_t treeStringElementNum = (sepLength == 0) ? (len - 1) : (2 * (len - 1));
428
429 if (treeStringElementNum * TreeString::SIZE <= allocateLength) {
430 // heuristic: if tree string uses less memory than linestring, it is worth.
431 // In other words, we hope tree string can work for the large strings join.
432 return true;
433 }
434 }
435 return false;
436 }
437
438 template <typename Container>
JoinUseTreeString(const JSThread * thread,const JSHandle<JSTaggedValue> receiverValue,const JSHandle<EcmaString> sepStringHandle,uint32_t sepLength,Container & arrElements,uint32_t elemNum)439 JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread *thread, const JSHandle<JSTaggedValue> receiverValue,
440 const JSHandle<EcmaString> sepStringHandle, uint32_t sepLength,
441 Container &arrElements, uint32_t elemNum)
442 {
443 // Do not concat the elements one by one, it will make the tree string unbalanced. Concat each element with its
444 // right neighbor first level by level, then the tree string will be balanced as possible.
445 if (sepLength != 0 && elemNum > 1) {
446 for (uint32_t k = 0; k < elemNum - 1; k++) {
447 arrElements[k] = JSHandle<EcmaString>(
448 thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), arrElements[k], sepStringHandle));
449 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
450 }
451 }
452
453 while (elemNum > 1) {
454 uint32_t newNum = (elemNum + 1) / NUM_2;
455 for (uint32_t i = 0; i < elemNum / NUM_2; ++i) {
456 arrElements[i] = JSHandle<EcmaString>(
457 thread,
458 EcmaStringAccessor::Concat(thread->GetEcmaVM(), arrElements[NUM_2 * i], arrElements[NUM_2 * i + 1])
459 );
460 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
461 }
462 if (elemNum % NUM_2 == 1) {
463 arrElements[newNum - 1] = arrElements[elemNum - 1];
464 }
465 elemNum = newNum;
466 }
467 ArrayJoinStack::Pop(thread, receiverValue);
468 return arrElements[0].GetTaggedValue();
469 }
470
471 template <typename Container>
ProcessElements(JSThread * thread,JSHandle<JSTaggedValue> receiverValue,uint32_t len,Container & arrElements,bool & isOneByte,uint64_t & allocateLength)472 void JSStableArray::ProcessElements(JSThread *thread, JSHandle<JSTaggedValue> receiverValue, uint32_t len,
473 Container &arrElements, bool &isOneByte, uint64_t &allocateLength)
474 {
475 JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
476 JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
477 JSTaggedValue element = JSTaggedValue::Undefined();
478 for (uint32_t k = 0; k < len; k++) {
479 if (receiverValue->IsStableJSArray(thread) || receiverValue->IsJSSharedArray()) {
480 element = k < ElementAccessor::GetElementsLength(thread, obj) ? ElementAccessor::Get(thread, obj, k)
481 : JSTaggedValue::Hole();
482 } else {
483 element = JSArray::FastGetPropertyByValue(thread, receiverValue, k).GetTaggedValue();
484 RETURN_IF_ABRUPT_COMPLETION(thread);
485 }
486 if (!element.IsUndefinedOrNull() && !element.IsHole()) {
487 if (!element.IsString()) {
488 elementHandle.Update(element);
489 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
490 RETURN_IF_ABRUPT_COMPLETION(thread);
491 element = strElement.GetTaggedValue();
492 }
493 auto nextStr = EcmaString::Cast(element.GetTaggedObject());
494 arrElements[k] = JSHandle<EcmaString>(thread, nextStr);
495 isOneByte = isOneByte & EcmaStringAccessor(nextStr).IsUtf8();
496 allocateLength += EcmaStringAccessor(nextStr).GetLength();
497 } else {
498 arrElements[k] = JSHandle<EcmaString>(thread->GlobalConstants()->GetHandledEmptyString());
499 }
500 }
501 }
502
503 template <typename Container>
DoStableArrayJoin(JSThread * thread,JSHandle<JSTaggedValue> receiverValue,uint32_t len,Container & arrElements,bool & isOneByte,uint32_t sep,uint32_t sepLength,JSHandle<EcmaString> sepStringHandle)504 JSTaggedValue JSStableArray::DoStableArrayJoin(JSThread *thread, JSHandle<JSTaggedValue> receiverValue, uint32_t len,
505 Container &arrElements, bool &isOneByte, uint32_t sep,
506 uint32_t sepLength, JSHandle<EcmaString> sepStringHandle)
507 {
508 uint64_t allocateLength = 0;
509 ProcessElements(thread, receiverValue, len, arrElements, isOneByte, allocateLength);
510 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
511
512 if (len > 0) {
513 allocateLength += static_cast<uint64_t>(sepLength) * (len - 1);
514 }
515 if (allocateLength > BaseString::MAX_STRING_LENGTH) {
516 ArrayJoinStack::Pop(thread, receiverValue);
517 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
518 }
519 if (WorthUseTreeString(sepLength, allocateLength, len)) {
520 return JoinUseTreeString(thread, receiverValue, sepStringHandle, sepLength, arrElements, len);
521 }
522
523 // 5. Let R be the empty String.
524 auto newString =
525 EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
526 int current = 0;
527 {
528 DISALLOW_GARBAGE_COLLECTION;
529 // 6. Repeat, while k < len
530 for (uint32_t k = 0; k < len; k++) {
531 // a. If k > 0, set R to the string-concatenation of R and sep.
532 if (k > 0) {
533 if (sepLength == 1) {
534 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
535 } else if (sepLength > 1) {
536 EcmaStringAccessor::ReadData(thread, newString, *sepStringHandle, current,
537 allocateLength - static_cast<uint32_t>(current), sepLength);
538 }
539 current += static_cast<int>(sepLength);
540 }
541 // b. Let element be ? Get(O, ToString((k))).
542 JSHandle<EcmaString> nextStr = arrElements[k];
543
544 // c. Set R to the string-concatenation of R and S
545 int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
546 EcmaStringAccessor::ReadData(thread, newString, *nextStr, current,
547 allocateLength - static_cast<uint32_t>(current), nextLength);
548 current += nextLength;
549 }
550 }
551 ASSERT_PRINT(isOneByte == EcmaStringAccessor::CanBeCompressed(newString),
552 "isOneByte does not match the real value!");
553 JSHandle<JSTaggedValue> stringValue(thread, newString);
554 ArrayJoinStack::Pop(thread, receiverValue);
555 // return R
556 return stringValue.GetTaggedValue();
557 }
558
Join(JSHandle<JSTaggedValue> receiverValue,EcmaRuntimeCallInfo * argv)559 JSTaggedValue JSStableArray::Join(JSHandle<JSTaggedValue> receiverValue, EcmaRuntimeCallInfo *argv)
560 {
561 JSThread *thread = argv->GetThread();
562 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
563
564 // 1. Let O be ToObject(this.value)
565 JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
566
567 // 2. Let len be ToLength(Get(O, "length"))
568 int64_t len = base::ArrayHelper::GetArrayLength(thread, receiverValue);
569
570 int sep = ',';
571 uint32_t sepLength = 1;
572 JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
573 JSHandle<EcmaString> sepStringHandle;
574 if (sepHandle->IsUndefined()) {
575 // 3. If separator is undefined, let sep be ",".
576 sepHandle = globalConst->GetHandledCommaString();
577 sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
578 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
579 } else {
580 // 4. Else, let sep be ? ToString(separator).
581 sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
582 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
583 sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
584 if (sepLength == 1) {
585 sep = EcmaStringAccessor(sepStringHandle).Get<false>(thread, 0);
586 }
587 }
588
589 bool isOneByte = EcmaStringAccessor(sepStringHandle).IsUtf8();
590
591 // Fastpath should put after parsing "sep". Error may occur if sep cannot be transformed to string,
592 // which should be handled before fastpath return.
593 if (len == 0 || !ArrayJoinStack::Push(thread, receiverValue)) {
594 return globalConst->GetEmptyString();
595 }
596
597 if (len == 1) {
598 // sep unused, set isOneByte to default(true)
599 isOneByte = true;
600 }
601
602 // Use stack memory if the number of elements is less than USE_STACK_MEMORY_THRESHOLD.
603 // arr can be faster then vector.
604 if (len <= USE_STACK_MEMORY_THRESHOLD) {
605 std::array<JSHandle<EcmaString>, USE_STACK_MEMORY_THRESHOLD> arr;
606 return DoStableArrayJoin(thread, receiverValue, len, arr, isOneByte, sep, sepLength, sepStringHandle);
607 } else {
608 CVector<JSHandle<EcmaString>> vec(len);
609 return DoStableArrayJoin(thread, receiverValue, len, vec, isOneByte, sep, sepLength, sepStringHandle);
610 }
611 }
612 #endif
613
614 #if !ENABLE_NEXT_OPTIMIZATION
SetSepValue(JSThread * thread,JSHandle<EcmaString> sepStringHandle,int & sep,uint32_t & sepLength)615 void JSStableArray::SetSepValue(JSThread *thread, JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength)
616 {
617 if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) {
618 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
619 sep = EcmaStringAccessor(sepStringHandle).Get(thread, 0);
620 } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) {
621 sep = JSStableArray::SeparatorFlag::MINUS_TWO;
622 sepLength = 0;
623 } else {
624 sep = JSStableArray::SeparatorFlag::MINUS_ONE;
625 sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
626 }
627 }
628
WorthUseTreeString(int sep,size_t allocateLength,uint32_t len)629 bool JSStableArray::WorthUseTreeString(int sep, size_t allocateLength, uint32_t len)
630 {
631 if (allocateLength >= TREE_STRING_THRESHOLD) {
632 size_t treeStringElementNum = (sep == MINUS_TWO) ? (len - 1) : (2 * (len - 1));
633 // if sep is MINUS_TWO, means all the elements in treeString is len -1;
634 // otherwise, the num of elements is (len-1)(string in vector) + (len -1)(num of seps)
635 if (treeStringElementNum * TreeString::SIZE <= allocateLength) {
636 // heuristic: if tree string uses less memory than linestring, it is worth.
637 // In other words, we hope tree string can work for the large strings join.
638 return true;
639 }
640 }
641 return false;
642 }
643
JoinUseTreeString(const JSThread * thread,const JSHandle<JSTaggedValue> receiverValue,const JSHandle<EcmaString> sepStringHandle,const int sep,CVector<JSHandle<EcmaString>> & vec)644 JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread *thread,
645 const JSHandle<JSTaggedValue> receiverValue,
646 const JSHandle<EcmaString> sepStringHandle, const int sep,
647 CVector<JSHandle<EcmaString>> &vec)
648 {
649 // Do not concat the elements one by one, it will make the tree string unbalanced. Concat each element with its
650 // right neighbor first level by level, then the tree string will be balanced as possible.
651 if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
652 auto last = std::prev(vec.end());
653 for (auto iter = vec.begin(); iter != last; ++iter) {
654 *iter =
655 JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), *iter, sepStringHandle));
656 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
657 }
658 }
659 size_t elemNum = vec.size();
660 while (elemNum > 1) {
661 size_t newNum = (elemNum + 1) / NUM_2;
662 for (size_t i = 0; i < elemNum / NUM_2; ++i) {
663 vec[i] = JSHandle<EcmaString>(
664 thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), vec[NUM_2 * i], vec[NUM_2 * i + 1]));
665 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
666 }
667 if (elemNum % NUM_2 == 1) {
668 vec[newNum - 1] = vec[elemNum - 1];
669 }
670 elemNum = newNum;
671 }
672 ArrayJoinStack::Pop(thread, receiverValue);
673 return vec.front().GetTaggedValue();
674 }
675
Join(JSHandle<JSTaggedValue> receiverValue,EcmaRuntimeCallInfo * argv)676 JSTaggedValue JSStableArray::Join(JSHandle<JSTaggedValue> receiverValue, EcmaRuntimeCallInfo *argv)
677 {
678 JSThread *thread = argv->GetThread();
679 uint32_t length = base::ArrayHelper::GetArrayLength(thread, receiverValue);
680 JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
681 int sep = ',';
682 uint32_t sepLength = 1;
683 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
684 JSHandle<EcmaString> sepStringHandle = JSHandle<EcmaString>::Cast(globalConst->GetHandledCommaString());
685 if (!sepHandle->IsUndefined()) {
686 if (sepHandle->IsString()) {
687 sepStringHandle = JSHandle<EcmaString>::Cast(sepHandle);
688 } else {
689 sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
690 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
691 }
692 SetSepValue(thread, sepStringHandle, sep, sepLength);
693 }
694 if (length == 0 || !ArrayJoinStack::Push(thread, receiverValue)) {
695 return globalConst->GetEmptyString();
696 }
697 JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
698 uint64_t allocateLength = 0;
699 bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
700 JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
701 uint32_t elementsLength = ElementAccessor::GetElementsLength(thread, obj);
702 uint32_t len = elementsLength > length ? length : elementsLength;
703 if (elementsLength == 0 && length != 0) {
704 len = length;
705 }
706 if (len <= 1) {
707 // sep unused, set isOneByte to default(true)
708 isOneByte = true;
709 }
710 CVector<JSHandle<EcmaString>> vec;
711 vec.reserve(len);
712 JSTaggedValue element = JSTaggedValue::Hole();
713 for (uint32_t k = 0; k < len; k++) {
714 if (receiverValue->IsStableJSArray(thread)) {
715 element = k < ElementAccessor::GetElementsLength(thread, obj) ? ElementAccessor::Get(thread, obj, k)
716 : JSTaggedValue::Hole();
717 } else {
718 element = JSArray::FastGetPropertyByValue(thread, receiverValue, k).GetTaggedValue();
719 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
720 }
721 if (!element.IsUndefinedOrNull() && !element.IsHole()) {
722 if (!element.IsString()) {
723 elementHandle.Update(element);
724 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
725 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
726 element = strElement.GetTaggedValue();
727 }
728 auto nextStr = EcmaString::Cast(element.GetTaggedObject());
729 vec.emplace_back(thread, nextStr);
730 isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
731 allocateLength += EcmaStringAccessor(nextStr).GetLength();
732 } else {
733 vec.emplace_back(globalConst->GetHandledEmptyString());
734 }
735 }
736 if (len > 0) {
737 allocateLength += static_cast<uint64_t>(sepLength) * (len - 1);
738 }
739 if (allocateLength > BaseString::MAX_STRING_LENGTH) {
740 ArrayJoinStack::Pop(thread, receiverValue);
741 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
742 }
743 if (WorthUseTreeString(sep, allocateLength, len)) {
744 return JoinUseTreeString(thread, receiverValue, sepStringHandle, sep, vec);
745 }
746 auto newString =
747 EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
748 int current = 0;
749 {
750 DISALLOW_GARBAGE_COLLECTION;
751 for (uint32_t k = 0; k < len; k++) {
752 if (k > 0) {
753 if (sep >= 0) {
754 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
755 } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
756 EcmaStringAccessor::ReadData(thread, newString, *sepStringHandle, current,
757 allocateLength - static_cast<uint32_t>(current), sepLength);
758 }
759 current += static_cast<int>(sepLength);
760 }
761 JSHandle<EcmaString> nextStr = vec[k];
762 int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
763 EcmaStringAccessor::ReadData(thread, newString, *nextStr, current,
764 allocateLength - static_cast<uint32_t>(current), nextLength);
765 current += nextLength;
766 }
767 }
768 ASSERT_PRINT(isOneByte == EcmaStringAccessor::CanBeCompressed(newString),
769 "isOneByte does not match the real value!");
770 JSHandle<JSTaggedValue> stringValue(thread, newString);
771 ArrayJoinStack::Pop(thread, receiverValue);
772 return stringValue.GetTaggedValue();
773 }
774 #endif
775
HandleFindIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)776 JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
777 JSHandle<JSTaggedValue> callbackFnHandle,
778 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
779 {
780 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
781 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
782 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
783 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
784 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
785 const int32_t argsLength = 3; // 3: ?kValue, k, O?
786 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
787 while (k < len) {
788 // Elements of thisObjHandle may change.
789 JSTaggedValue val = ElementAccessor::Get(thread, thisObjHandle, k);
790 if (val.IsHole()) {
791 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
792 if (res.IsHole()) {
793 kValue.Update(JSTaggedValue::Undefined());
794 } else {
795 kValue.Update(res);
796 }
797 } else {
798 kValue.Update(val);
799 }
800 EcmaRuntimeCallInfo *info =
801 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
802 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
803 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
804 callResult = JSFunction::Call(info);
805 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
806 if (callResult.ToBoolean()) {
807 return callResult;
808 }
809 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
810 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
811 }
812 k++;
813 if (!thisObjVal->IsStableJSArray(thread)) {
814 return callResult;
815 }
816 }
817 return callResult;
818 }
819
HandleFindLastIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)820 JSTaggedValue JSStableArray::HandleFindLastIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
821 JSHandle<JSTaggedValue> callbackFnHandle,
822 JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
823 {
824 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
825 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
826 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
827 const int32_t argsLength = 3; // 3: ?kValue, k, O?
828 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
829 while (k >= 0) {
830 // Elements of thisObjHandle may change.
831 JSTaggedValue val = ElementAccessor::Get(thread, thisObjHandle, k);
832 if (val.IsHole()) {
833 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
834 if (res.IsHole()) {
835 kValue.Update(JSTaggedValue::Undefined());
836 } else {
837 kValue.Update(res);
838 }
839 } else {
840 kValue.Update(val);
841 }
842 EcmaRuntimeCallInfo *info =
843 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
844 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
845 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
846 callResult = JSFunction::Call(info);
847 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
848 if (callResult.ToBoolean()) {
849 return callResult;
850 }
851 k--;
852 if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) <= k) {
853 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
854 return callResult;
855 }
856 if (!thisObjVal->IsStableJSArray(thread)) {
857 return callResult;
858 }
859 }
860 return callResult;
861 }
862
HandleEveryOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)863 JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
864 JSHandle<JSTaggedValue> callbackFnHandle,
865 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
866 {
867 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
868 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
869 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
870 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
871 const int32_t argsLength = 3; // 3: ?kValue, k, O?
872 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(true);
873 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
874 while (k < len) {
875 // Elements of thisObjHandle may change.
876 kValue.Update(ElementAccessor::Get(thread, thisObjHandle, k));
877 if (!kValue.GetTaggedValue().IsHole()) {
878 EcmaRuntimeCallInfo *info =
879 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
880 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
881 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
882 callResult = JSFunction::Call(info);
883 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
884 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
885 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
886 }
887 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
888 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
889 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
890 EcmaRuntimeCallInfo *info =
891 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
892 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
893 info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
894 callResult = JSFunction::Call(info);
895 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
896 }
897 if (!callResult.ToBoolean()) {
898 return base::BuiltinsBase::GetTaggedBoolean(false);
899 }
900 k++;
901 if (!thisObjVal->IsStableJSArray(thread)) {
902 return base::BuiltinsBase::GetTaggedBoolean(true);
903 }
904 }
905 return base::BuiltinsBase::GetTaggedBoolean(true);
906 }
907
HandleSomeOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)908 JSTaggedValue JSStableArray::HandleSomeOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
909 JSHandle<JSTaggedValue> callbackFnHandle,
910 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
911 {
912 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
913 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
914 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
915 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
916 const int32_t argsLength = 3; // 3: ?kValue, k, O?
917 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
918 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
919 while (k < len) {
920 // Elements of thisObjHandle may change.
921 kValue.Update(ElementAccessor::Get(thread, thisObjHandle, k));
922 if (!kValue.GetTaggedValue().IsHole()) {
923 EcmaRuntimeCallInfo *info =
924 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
925 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
926 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
927 callResult = JSFunction::Call(info);
928 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
929 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
930 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
931 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
932 EcmaRuntimeCallInfo *info =
933 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
934 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
935 info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
936 callResult = JSFunction::Call(info);
937 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
938 }
939 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
940 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
941 }
942 if (callResult.ToBoolean()) {
943 return base::BuiltinsBase::GetTaggedBoolean(true);
944 }
945 k++;
946 if (!thisObjVal->IsStableJSArray(thread)) {
947 return base::BuiltinsBase::GetTaggedBoolean(false);
948 }
949 }
950 return base::BuiltinsBase::GetTaggedBoolean(false);
951 }
952
HandleforEachOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t len,uint32_t & k)953 JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
954 JSHandle<JSTaggedValue> callbackFnHandle,
955 JSHandle<JSTaggedValue> thisArgHandle, uint32_t len, uint32_t &k)
956 {
957 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
958 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
959 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
960 const int32_t argsLength = 3; // 3: ?kValue, k, O?
961 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
962 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) <= k) {
963 return base::BuiltinsBase::GetTaggedBoolean(false);
964 }
965 while (k < len) {
966 // Elements of thisObjHandle may change.
967 kValue.Update(ElementAccessor::Get(thread, thisObjHandle, k));
968 if (!kValue.GetTaggedValue().IsHole()) {
969 key.Update(JSTaggedValue(k));
970 EcmaRuntimeCallInfo *info =
971 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
972 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
973 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
974 JSTaggedValue funcResult = JSFunction::Call(info);
975 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
976 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
977 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
978 }
979 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
980 key.Update(JSTaggedValue(k));
981 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
982 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
983 EcmaRuntimeCallInfo *info =
984 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
985 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
986 info->SetCallArg(kValue1.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
987 JSTaggedValue funcResult = JSFunction::Call(info);
988 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
989 }
990 k++;
991 if (!thisObjVal->IsStableJSArray(thread)) {
992 break;
993 }
994 }
995 return base::BuiltinsBase::GetTaggedBoolean(true);
996 }
997
998 template <RBMode mode, class Predicate>
IndexOfElements(JSThread * thread,Span<const JSTaggedType> elements,IndexOfOptions options,Predicate predicate)999 const JSTaggedType *JSStableArray::IndexOfElements(JSThread *thread, Span<const JSTaggedType> elements,
1000 IndexOfOptions options, Predicate predicate)
1001 {
1002 static_assert(std::is_invocable_r_v<bool, Predicate, JSTaggedType>, "Invalid call signature.");
1003 if (options.reversedOrder) {
1004 for (auto cur = elements.end() - 1; cur >= elements.begin(); --cur) {
1005 if (UNLIKELY(std::invoke(predicate, Barriers::GetTaggedValue<mode>(thread, ToUintPtr(cur))))) {
1006 return cur;
1007 }
1008 }
1009 } else {
1010 for (auto cur = elements.begin(); cur < elements.end(); ++cur) {
1011 if (UNLIKELY(std::invoke(predicate, Barriers::GetTaggedValue<mode>(thread, ToUintPtr(cur))))) {
1012 return cur;
1013 }
1014 }
1015 }
1016 return nullptr;
1017 }
1018
IndexOfUndefined(JSThread * thread,Span<const JSTaggedType> elements,IndexOfOptions options,bool isMutant)1019 const JSTaggedType *JSStableArray::IndexOfUndefined(JSThread *thread, Span<const JSTaggedType> elements,
1020 IndexOfOptions options, bool isMutant)
1021 {
1022 // For mutant arrays, only raw int32, raw double and SPECIAL_HOLE may exist.
1023 if (isMutant) {
1024 if (!options.holeAsUndefined) {
1025 return nullptr;
1026 }
1027 return IndexOfElements(thread, elements, options,
1028 [](JSTaggedType rawValue) { return rawValue == base::SPECIAL_HOLE; });
1029 }
1030 // For non-mutant arrays, taggedValue can never be SPECIAL_HOLE.
1031 if (!options.holeAsUndefined) {
1032 return IndexOfElements(thread, elements, options,
1033 [](JSTaggedType taggedValue) { return JSTaggedValue(taggedValue).IsUndefined(); });
1034 }
1035 return IndexOfElements(thread, elements, options, [](JSTaggedType taggedValue) {
1036 return JSTaggedValue(taggedValue).IsHole() || JSTaggedValue(taggedValue).IsUndefined();
1037 });
1038 }
1039
IndexOfTaggedZero(JSThread * thread,Span<const JSTaggedType> elements,IndexOfOptions options)1040 const JSTaggedType *JSStableArray::IndexOfTaggedZero(JSThread *thread, Span<const JSTaggedType> elements,
1041 IndexOfOptions options)
1042 {
1043 return IndexOfElements(thread, elements, options,
1044 [](JSTaggedType taggedValue) { return JSTaggedValue(taggedValue).IsExactlyZero(); });
1045 }
1046
1047 // Raw int32 array (isMutant = true), or tagged array (isMutant = false)
IndexOfInt(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options,bool isMutantInt32Array)1048 const JSTaggedType *JSStableArray::IndexOfInt(JSThread *thread, Span<const JSTaggedType> elements,
1049 JSTaggedValue searchElement, IndexOfOptions options,
1050 bool isMutantInt32Array)
1051 {
1052 ASSERT(!searchElement.IsUndefined());
1053 int32_t searchValue;
1054 if (searchElement.IsInt()) {
1055 searchValue = searchElement.GetInt();
1056 } else if (searchElement.WithinInt32(true)) {
1057 searchValue = static_cast<int32_t>(searchElement.GetDouble());
1058 } else {
1059 return nullptr;
1060 }
1061 if (isMutantInt32Array) {
1062 // For ElementsKind::INT: convertedValue = JSTaggedValue(static_cast<int>(rawValue))
1063 return IndexOfElements(thread, elements, options, [searchValue](JSTaggedType rawValue) {
1064 return rawValue != base::SPECIAL_HOLE && searchValue == static_cast<int32_t>(rawValue);
1065 });
1066 }
1067 if (searchValue == 0) {
1068 return IndexOfTaggedZero(thread, elements, options);
1069 }
1070 JSTaggedType taggedInt32 = JSTaggedValue(searchValue).GetRawData();
1071 JSTaggedType taggedDouble = JSTaggedValue(static_cast<double>(searchValue)).GetRawData();
1072 // Always false if taggedValue is not number
1073 return IndexOfElements(thread, elements, options, [taggedInt32, taggedDouble](JSTaggedType taggedValue) {
1074 return taggedValue == taggedInt32 || taggedValue == taggedDouble;
1075 });
1076 }
1077
1078 // Raw double array (isMutant = true), or tagged array (isMutant = false)
IndexOfDouble(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options,bool isMutantDoubleArray)1079 const JSTaggedType *JSStableArray::IndexOfDouble(JSThread *thread, Span<const JSTaggedType> elements,
1080 JSTaggedValue searchElement, IndexOfOptions options,
1081 bool isMutantDoubleArray)
1082 {
1083 ASSERT(!searchElement.IsUndefined());
1084 if (!searchElement.IsNumber()) {
1085 return nullptr;
1086 }
1087 double searchValue = searchElement.GetNumber();
1088 if (std::isnan(searchValue)) {
1089 if (options.compType != ComparisonType::SAME_VALUE_ZERO) {
1090 return nullptr;
1091 }
1092 if (isMutantDoubleArray) {
1093 // For ElementsKind::NUMBER: convertedValue = JSTaggedValue(base::bit_cast<double>(rawValue))
1094 return IndexOfElements(thread, elements, options, [](JSTaggedType rawValue) {
1095 return rawValue != base::SPECIAL_HOLE && std::isnan(base::bit_cast<double>(rawValue));
1096 });
1097 }
1098 return IndexOfElements(thread, elements, options,
1099 [](JSTaggedType taggedValue) { return JSTaggedValue(taggedValue).IsNaN(); });
1100 }
1101 if (isMutantDoubleArray) {
1102 // Including the cases of +inf, -inf, +0.0 and -0.0
1103 // We assume that bit representation of searchValue can never be SPECIAL_HOLE (which is NaN)
1104 return IndexOfElements(thread, elements, options, [searchValue](JSTaggedType rawValue) {
1105 return searchValue == base::bit_cast<double>(rawValue);
1106 });
1107 }
1108 if (searchValue == 0.0) {
1109 return IndexOfTaggedZero(thread, elements, options);
1110 }
1111 JSTaggedType taggedDouble = JSTaggedValue(searchValue).GetRawData();
1112 if (JSTaggedValue(taggedDouble).WithinInt32()) {
1113 JSTaggedType taggedInt32 = JSTaggedValue(static_cast<int32_t>(searchValue)).GetRawData();
1114 return IndexOfElements(thread, elements, options, [taggedDouble, taggedInt32](JSTaggedType taggedValue) {
1115 return taggedValue == taggedDouble || taggedValue == taggedInt32;
1116 });
1117 }
1118 return IndexOfElements(thread, elements, options,
1119 [taggedDouble](JSTaggedType taggedValue) { return taggedValue == taggedDouble; });
1120 }
1121
IndexOfString(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options)1122 const JSTaggedType *JSStableArray::IndexOfString(JSThread *thread, Span<const JSTaggedType> elements,
1123 JSTaggedValue searchElement, IndexOfOptions options)
1124 {
1125 ASSERT(!searchElement.IsUndefined());
1126 if (!searchElement.IsString()) {
1127 return nullptr;
1128 }
1129 if (g_isEnableCMCGC) {
1130 return IndexOfElements<RBMode::FAST_CMC_RB>(thread, elements, options,
1131 [searchElement, thread](JSTaggedType cur) {
1132 if (searchElement.GetRawData() == cur) {
1133 return true;
1134 }
1135 JSTaggedValue curValue(cur);
1136 if (!curValue.IsString()) {
1137 return false;
1138 }
1139 return JSTaggedValue::StringCompare<RBMode::FAST_CMC_RB>(thread,
1140 EcmaString::Cast(curValue.GetTaggedObject()),
1141 EcmaString::Cast(searchElement.GetTaggedObject()));
1142 });
1143 } else {
1144 return IndexOfElements<RBMode::FAST_NO_RB>(thread, elements, options,
1145 [searchElement, thread](JSTaggedType cur) {
1146 if (searchElement.GetRawData() == cur) {
1147 return true;
1148 }
1149 JSTaggedValue curValue(cur);
1150 if (!curValue.IsString()) {
1151 return false;
1152 }
1153 return JSTaggedValue::StringCompare<RBMode::FAST_NO_RB>(thread,
1154 EcmaString::Cast(curValue.GetTaggedObject()),
1155 EcmaString::Cast(searchElement.GetTaggedObject()));
1156 });
1157 }
1158 }
1159
IndexOfBigInt(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options)1160 const JSTaggedType *JSStableArray::IndexOfBigInt(JSThread *thread, Span<const JSTaggedType> elements,
1161 JSTaggedValue searchElement, IndexOfOptions options)
1162 {
1163 ASSERT(searchElement.IsBigInt());
1164 return IndexOfElements(thread, elements, options, [searchElement](JSTaggedType cur) {
1165 if (searchElement.GetRawData() == cur) {
1166 return true;
1167 }
1168 JSTaggedValue curValue(cur);
1169 if (!curValue.IsBigInt()) {
1170 return false;
1171 }
1172 return BigInt::Equal(curValue, searchElement);
1173 });
1174 }
1175
IndexOfObjectAddress(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options)1176 const JSTaggedType *JSStableArray::IndexOfObjectAddress(JSThread *thread, Span<const JSTaggedType> elements,
1177 JSTaggedValue searchElement, IndexOfOptions options)
1178 {
1179 // Note: searchElement may be true, false or null
1180 ASSERT(searchElement.IsObject());
1181 JSTaggedType targetAddress = searchElement.GetRawData();
1182 return IndexOfElements(thread, elements, options,
1183 [targetAddress](JSTaggedType cur) { return cur == targetAddress; });
1184 }
1185
IndexOfDispatch(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElementHandle,uint32_t from,uint32_t len,IndexOfOptions options)1186 JSTaggedValue JSStableArray::IndexOfDispatch(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1187 JSHandle<JSTaggedValue> searchElementHandle, uint32_t from, uint32_t len,
1188 IndexOfOptions options)
1189 {
1190 // Note: GC is guaranteed not to happen since no new object is created during the searching process.
1191 DISALLOW_GARBAGE_COLLECTION;
1192 const JSTaggedType *data = nullptr;
1193 JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(receiver)->GetElements(thread);
1194 bool isMutant = elementsValue.IsMutantTaggedArray();
1195 if (isMutant) {
1196 JSHandle<MutantTaggedArray> elements(thread, elementsValue);
1197 data = elements->GetData();
1198 } else {
1199 JSHandle<TaggedArray> elements(thread, elementsValue);
1200 data = elements->GetData();
1201 }
1202 Span<const JSTaggedType> range;
1203 if (options.reversedOrder) {
1204 range = {data, data + from + 1}; // lastIndexOf
1205 } else {
1206 range = {data + from, data + len}; // indexOf, includes
1207 }
1208 ElementsKind kind = JSHandle<JSObject>::Cast(receiver)->GetClass()->GetElementsKind();
1209 JSTaggedValue searchElement = searchElementHandle.GetTaggedValue();
1210
1211 const JSTaggedType *foundPos = nullptr;
1212 if (searchElement.IsUndefined()) {
1213 foundPos = IndexOfUndefined(thread, range, options, isMutant);
1214 } else if (isMutant) {
1215 LOG_DEBUGGER(DEBUG) << "IndexOfDispatch: isMutant";
1216 if (Elements::IsIntOrHoleInt(kind)) {
1217 foundPos = IndexOfInt(thread, range, searchElement, options, true); // raw int32
1218 } else {
1219 ASSERT(Elements::IsInNumbers(kind));
1220 foundPos = IndexOfDouble(thread, range, searchElement, options, true); // raw double
1221 }
1222 } else if (searchElement.IsInt() || Elements::IsIntOrHoleInt(kind)) {
1223 foundPos = IndexOfInt(thread, range, searchElement, options, false);
1224 } else if (searchElement.IsDouble() || Elements::IsNumberOrHoleNumber(kind)) {
1225 foundPos = IndexOfDouble(thread, range, searchElement, options, false);
1226 } else if (searchElement.IsString() || Elements::IsStringOrHoleString(kind)) {
1227 foundPos = IndexOfString(thread, range, searchElement, options);
1228 } else if (searchElement.IsBigInt()) {
1229 foundPos = IndexOfBigInt(thread, range, searchElement, options);
1230 } else {
1231 foundPos = IndexOfObjectAddress(thread, range, searchElement, options);
1232 }
1233 if (options.returnType == IndexOfReturnType::TAGGED_FOUND_INDEX) {
1234 return foundPos == nullptr ? JSTaggedValue(-1) : JSTaggedValue(static_cast<int32_t>(foundPos - data));
1235 } else {
1236 ASSERT(options.returnType == IndexOfReturnType::TAGGED_FOUND_OR_NOT);
1237 return JSTaggedValue(foundPos != nullptr);
1238 }
1239 }
1240
Includes(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1241 JSTaggedValue JSStableArray::Includes(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1242 JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1243 {
1244 IndexOfOptions options;
1245 options.compType = ComparisonType::SAME_VALUE_ZERO;
1246 options.returnType = IndexOfReturnType::TAGGED_FOUND_OR_NOT;
1247 options.holeAsUndefined = true;
1248 options.reversedOrder = false;
1249 return IndexOfDispatch(thread, receiver, searchElement, from, len, options);
1250 }
1251
IndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1252 JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1253 JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1254 {
1255 IndexOfOptions options;
1256 options.compType = ComparisonType::STRICT_EQUAL;
1257 options.returnType = IndexOfReturnType::TAGGED_FOUND_INDEX;
1258 options.holeAsUndefined = false;
1259 options.reversedOrder = false;
1260 return IndexOfDispatch(thread, receiver, searchElement, from, len, options);
1261 }
1262
LastIndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1263 JSTaggedValue JSStableArray::LastIndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1264 JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1265 {
1266 IndexOfOptions options;
1267 options.compType = ComparisonType::STRICT_EQUAL;
1268 options.returnType = IndexOfReturnType::TAGGED_FOUND_INDEX;
1269 options.holeAsUndefined = false;
1270 options.reversedOrder = true;
1271 return IndexOfDispatch(thread, receiver, searchElement, from, len, options);
1272 }
1273
Filter(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t & toIndex)1274 JSTaggedValue JSStableArray::Filter(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1275 EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex)
1276 {
1277 JSThread *thread = argv->GetThread();
1278 JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1279 JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1280 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1281 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1282 JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
1283 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1284 const int32_t argsLength = 3; // 3: ?kValue, k, O?
1285 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1286 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1287 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1288 while (k < len) {
1289 // Elements of thisObjHandle may change.
1290 JSTaggedValue value = ElementAccessor::Get(thread, thisObjHandle, k);
1291 if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1292 value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1293 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1294 }
1295 kValue.Update(value);
1296 if (!kValue.GetTaggedValue().IsHole()) {
1297 key.Update(JSTaggedValue(k));
1298 EcmaRuntimeCallInfo *info =
1299 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1300 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1301 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1302 JSTaggedValue callResult = JSFunction::Call(info);
1303 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1304 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
1305 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
1306 }
1307 bool boolResult = callResult.ToBoolean();
1308 if (boolResult) {
1309 toIndexHandle.Update(JSTaggedValue(toIndex));
1310 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
1311 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1312 toIndex++;
1313 }
1314 }
1315 k++;
1316 if (!thisObjVal->IsStableJSArray(thread)) {
1317 break;
1318 }
1319 }
1320 return base::BuiltinsBase::GetTaggedDouble(true);
1321 }
1322
Map(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t len)1323 JSTaggedValue JSStableArray::Map(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1324 EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len)
1325 {
1326 JSThread *thread = argv->GetThread();
1327 JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1328 JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1329 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1330 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1331 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1332 JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
1333 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1334 const int32_t argsLength = 3; // 3: ?kValue, k, O?
1335 while (k < len) {
1336 // Elements of thisObjHandle may change.
1337 JSTaggedValue value = ElementAccessor::Get(thread, thisObjHandle, k);
1338 if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1339 value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1340 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1341 }
1342 kValue.Update(value);
1343 if (!kValue.GetTaggedValue().IsHole()) {
1344 key.Update(JSTaggedValue(k));
1345 EcmaRuntimeCallInfo *info =
1346 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1347 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1348 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1349 JSTaggedValue mapResult = JSFunction::Call(info);
1350 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1351 mapResultHandle.Update(mapResult);
1352 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
1353 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1354 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
1355 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
1356 }
1357 }
1358 k++;
1359 if (!thisObjVal->IsStableJSArray(thread)) {
1360 break;
1361 }
1362 }
1363 return base::BuiltinsBase::GetTaggedDouble(true);
1364 }
1365
Reverse(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & lower,uint32_t len)1366 JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1367 int64_t &lower, uint32_t len)
1368 {
1369 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1370 if (thisObjHandle->IsJSArray()) {
1371 JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObjHandle));
1372 }
1373 ElementsKind kind = thisObjHandle->GetClass()->GetElementsKind();
1374 JSHandle<TaggedArray> elements(thread, thisObjHandle->GetElements(thread));
1375 if (thread->IsEnableMutantArray()) {
1376 if (kind == ElementsKind::INT || kind == ElementsKind::HOLE_INT) {
1377 return FastReverse(thread, elements, lower, len, ElementsKind::INT);
1378 } else if (kind == ElementsKind::NUMBER || kind == ElementsKind::HOLE_NUMBER) {
1379 return FastReverse(thread, elements, lower, len, ElementsKind::NUMBER);
1380 }
1381 }
1382 return FastReverse(thread, elements, lower, len, ElementsKind::TAGGED);
1383 }
1384
FastReverse(JSThread * thread,JSHandle<TaggedArray> elements,int64_t & lower,uint32_t len,ElementsKind kind)1385 JSTaggedValue JSStableArray::FastReverse(JSThread *thread, JSHandle<TaggedArray> elements,
1386 int64_t &lower, uint32_t len, ElementsKind kind)
1387 {
1388 JSMutableHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
1389 JSMutableHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
1390 int64_t middle = std::floor(len / 2);
1391 while (lower != middle) {
1392 if (elements->GetLength() != len) {
1393 break;
1394 }
1395 int64_t upper = static_cast<int64_t>(len) - lower - 1;
1396 lowerValueHandle.Update(ElementAccessor::FastGet(thread, elements, lower, kind));
1397 upperValueHandle.Update(ElementAccessor::FastGet(thread, elements, upper, kind));
1398 ElementAccessor::FastSet(thread, elements, lower, upperValueHandle, kind);
1399 ElementAccessor::FastSet(thread, elements, upper, lowerValueHandle, kind);
1400 lower++;
1401 }
1402 return base::BuiltinsBase::GetTaggedDouble(true);
1403 }
1404
Concat(JSThread * thread,JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & n)1405 JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle<JSObject> newArrayHandle,
1406 JSHandle<JSObject> thisObjHandle, int64_t &k, int64_t &n)
1407 {
1408 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1409 int64_t thisLen = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
1410 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1411 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
1412 while (k < thisLen) {
1413 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) != thisLen) {
1414 break;
1415 }
1416 toKey.Update(JSTaggedValue(n));
1417 JSTaggedValue kValue = ElementAccessor::Get(thread, thisObjHandle, k);
1418 if (!kValue.IsHole()) {
1419 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, JSHandle<JSTaggedValue>(thread, kValue));
1420 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1421 }
1422 n++;
1423 k++;
1424 }
1425 return base::BuiltinsBase::GetTaggedDouble(true);
1426 }
1427
1428 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::SHARED>(
1429 JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1430 uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1431 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::NON_SHARED>(
1432 JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1433 uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1434
1435 template<TypedArrayKind typedArrayKind>
FastCopyFromArrayToTypedArray(JSThread * thread,JSHandle<JSTypedArray> & targetArray,DataViewType targetType,uint64_t targetOffset,uint32_t srcLength,JSHandle<JSObject> & obj)1436 JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle<JSTypedArray> &targetArray,
1437 DataViewType targetType, uint64_t targetOffset,
1438 uint32_t srcLength, JSHandle<JSObject> &obj)
1439 {
1440 JSHandle<JSTaggedValue> targetBuffer(thread, targetArray->GetViewedArrayBufferOrByteArray(thread));
1441 // If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
1442 if (BuiltinsArrayBufferType<typedArrayKind>::Type::IsDetachedBuffer(thread, targetBuffer.GetTaggedValue())) {
1443 THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.",
1444 JSTaggedValue::Exception());
1445 }
1446 uint32_t targetLength = targetArray->GetArrayLength();
1447 uint32_t targetByteOffset = targetArray->GetByteOffset();
1448 uint32_t targetElementSize = TypedArrayHelper::GetSizeFromType(targetType);
1449 if (srcLength + targetOffset > targetLength) {
1450 THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of length and targetOffset is greater than targetLength.",
1451 JSTaggedValue::Exception());
1452 }
1453 uint32_t targetByteIndex = static_cast<uint32_t>(targetOffset * targetElementSize + targetByteOffset);
1454 ContentType contentType = targetArray->GetContentType();
1455 uint32_t elemLen = ElementAccessor::GetElementsLength(thread, obj);
1456 if (contentType == ContentType::BigInt) {
1457 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1458 JSMutableHandle<JSTaggedValue> elem(thread, JSTaggedValue::Hole());
1459 for (uint32_t i = 0; i < srcLength; i++) {
1460 if (i < elemLen) {
1461 elem.Update(ElementAccessor::Get(thread, obj, i));
1462 } else {
1463 elem.Update(JSTaggedValue::Hole());
1464 }
1465 kValue.Update(JSTaggedValue::ToBigInt(thread, elem));
1466 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1467 BuiltinsArrayBufferType<typedArrayKind>::Type::SetValueInBuffer(
1468 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kValue, true);
1469 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1470 targetByteIndex += targetElementSize;
1471 }
1472 } else {
1473 double val = 0.0;
1474 uint32_t copyLen = srcLength > elemLen ? elemLen : srcLength;
1475 for (uint32_t i = 0; i < copyLen; i++) {
1476 JSTaggedValue taggedVal = ElementAccessor::Get(thread, obj, i);
1477 if (!taggedVal.IsNumber()) {
1478 JSTaggedNumber taggedNumber = JSTaggedValue::ToNumber(thread, taggedVal);
1479 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1480 val = taggedNumber.GetNumber();
1481 } else {
1482 val = taggedVal.GetNumber();
1483 }
1484 BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1485 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1486 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1487 targetByteIndex += targetElementSize;
1488 }
1489
1490 for (uint32_t i = copyLen; i < srcLength; i++) {
1491 val = JSTaggedNumber(base::NAN_VALUE).GetNumber();
1492 BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1493 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1494 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1495 targetByteIndex += targetElementSize;
1496 }
1497 }
1498 return JSTaggedValue::Undefined();
1499 }
1500
At(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)1501 JSTaggedValue JSStableArray::At(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
1502 {
1503 JSThread *thread = argv->GetThread();
1504 uint32_t thisLen = receiver->GetArrayLength();
1505 if (thisLen == 0) {
1506 return JSTaggedValue::Undefined();
1507 }
1508 JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1509 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1510 int64_t relativeIndex = index.GetNumber();
1511 int64_t k = 0;
1512 if (relativeIndex >= 0) {
1513 k = relativeIndex;
1514 } else {
1515 k = static_cast<int64_t>(thisLen) + relativeIndex;
1516 }
1517 if (k < 0 || k >= thisLen) {
1518 return JSTaggedValue::Undefined();
1519 }
1520
1521 auto result = JSTaggedValue::Hole();
1522 result = ElementAccessor::Get(thread, JSHandle<JSObject>::Cast(receiver), k);
1523 return result.IsHole() ? JSTaggedValue::Undefined() : result;
1524 }
1525
At(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)1526 JSTaggedValue JSStableArray::At(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
1527 {
1528 JSThread *thread = argv->GetThread();
1529 uint32_t thisLen = receiver->GetArrayLength();
1530 if (thisLen == 0) {
1531 return JSTaggedValue::Undefined();
1532 }
1533 JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1534 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1535 int64_t relativeIndex = index.GetNumber();
1536 int64_t k = 0;
1537 if (relativeIndex >= 0) {
1538 k = relativeIndex;
1539 } else {
1540 k = static_cast<int64_t>(thisLen) + relativeIndex;
1541 }
1542 if (k < 0 || k >= thisLen) {
1543 return JSTaggedValue::Undefined();
1544 }
1545
1546 auto result = JSTaggedValue::Hole();
1547 result = ElementAccessor::Get(thread, JSHandle<JSObject>::Cast(receiver), k);
1548 return result.IsHole() ? JSTaggedValue::Undefined() : result;
1549 }
1550
With(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount,int64_t index,JSHandle<JSTaggedValue> value)1551 JSTaggedValue JSStableArray::With(JSThread *thread, JSHandle<JSArray> receiver,
1552 int64_t insertCount, int64_t index, JSHandle<JSTaggedValue> value)
1553 {
1554 JSHandle<JSObject> thisObjHandle(receiver);
1555 JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1556 JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1557 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1558 JSHandle<JSObject> newArrayHandle(newArray);
1559
1560 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1561 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
1562
1563 if (insertCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
1564 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1565 }
1566 ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1567 bool needTransition = true;
1568 for (uint32_t idx = 0; idx < insertCount; idx++) {
1569 if (idx == index) {
1570 ElementAccessor::Set(thread, newArrayHandle, idx, value, needTransition);
1571 } else {
1572 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, idx));
1573 if (kValue->IsHole()) {
1574 ElementAccessor::Set(thread, newArrayHandle, idx, undefinedHandle, needTransition);
1575 } else {
1576 ElementAccessor::Set(thread, newArrayHandle, idx, kValue, needTransition);
1577 }
1578 }
1579 }
1580 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1581 return newArrayHandle.GetTaggedValue();
1582 }
1583
ToSpliced(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,int64_t argc,int64_t actualStart,int64_t actualSkipCount,int64_t insertCount)1584 JSTaggedValue JSStableArray::ToSpliced(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
1585 int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount)
1586 {
1587 JSThread *thread = argv->GetThread();
1588
1589 JSHandle<JSObject> thisObjHandle(receiver);
1590 JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1591 JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1592 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1593 JSHandle<JSObject> newArrayHandle(newArray);
1594
1595 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1596 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
1597
1598 if (insertCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
1599 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1600 }
1601 ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1602 int64_t i = 0;
1603 int64_t r = actualStart + actualSkipCount;
1604 bool needTransition = true;
1605 for (int64_t idx = 0; idx < actualStart; idx++, i++) {
1606 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, idx));
1607 if (kValue->IsHole()) {
1608 ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1609 } else {
1610 ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1611 }
1612 }
1613
1614 for (uint32_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items.
1615 auto element = base::BuiltinsBase::GetCallArg(argv, pos);
1616 ElementAccessor::Set(thread, newArrayHandle, i, element, needTransition);
1617 ++i;
1618 }
1619
1620 while (i < insertCount) {
1621 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, r));
1622 if (kValue->IsHole()) {
1623 ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1624 } else {
1625 ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1626 }
1627 ++i;
1628 ++r;
1629 }
1630
1631 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1632
1633 return newArrayHandle.GetTaggedValue();
1634 }
1635
ToReversed(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount)1636 JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle<JSArray> receiver,
1637 int64_t insertCount)
1638 {
1639 JSHandle<JSObject> thisObjHandle(receiver);
1640 JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1641 JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1642 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1643 JSHandle<JSObject> newArrayHandle(newArray);
1644
1645 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1646 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
1647
1648 if (insertCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
1649 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1650 }
1651 ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1652 bool needTransition = true;
1653 for (uint32_t idx = 0; idx < insertCount; idx++) {
1654 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, idx));
1655 if (kValue->IsHole()) {
1656 ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1,
1657 undefinedHandle, needTransition);
1658 } else {
1659 ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1, kValue, needTransition);
1660 }
1661 }
1662 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1663
1664 return newArrayHandle.GetTaggedValue();
1665 }
1666
Reduce(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> accumulator,int64_t & k,int64_t & len)1667 JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1668 JSHandle<JSTaggedValue> callbackFnHandle,
1669 JSMutableHandle<JSTaggedValue> accumulator, int64_t &k, int64_t &len)
1670 {
1671 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1672 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1673 JSTaggedValue callResult = JSTaggedValue::Undefined();
1674 while (k < len) {
1675 JSTaggedValue kValue(ElementAccessor::Get(thread, thisObjHandle, k));
1676 if (!kValue.IsHole()) {
1677 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1678 const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1679 EcmaRuntimeCallInfo *info =
1680 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength);
1681 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1682 info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k),
1683 thisObjVal.GetTaggedValue());
1684 callResult = JSFunction::Call(info);
1685 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1686 if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
1687 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
1688 }
1689 accumulator.Update(callResult);
1690 }
1691 k++;
1692 if (!thisObjVal->IsStableJSArray(thread)) {
1693 break;
1694 }
1695 }
1696 return base::BuiltinsBase::GetTaggedDouble(true);
1697 }
1698
Slice(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & count)1699 JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1700 int64_t &k, int64_t &count)
1701 {
1702 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1703 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1704 int64_t len = static_cast<int64_t>(ElementAccessor::GetElementsLength(thread, thisObjHandle));
1705 int64_t oldLen;
1706 if (len > k + count) {
1707 oldLen = count;
1708 } else {
1709 oldLen = std::max<int64_t>(len - k, 0);
1710 }
1711 JSHandle<JSObject> arrayObj = factory->NewAndCopyJSArrayObject(thisObjHandle, count, oldLen, k);
1712 for (int i = 0; i < count; i++) {
1713 JSMutableHandle<JSTaggedValue> value(thread, ElementAccessor::Get(thread, arrayObj, i));
1714 if (value->IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i + k)) {
1715 value.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, i + k).GetTaggedValue());
1716 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1717 ElementAccessor::Set(thread, arrayObj, i, value, true);
1718 }
1719 }
1720 return arrayObj.GetTaggedValue();
1721 }
1722
SortIndexedProperties(JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,int64_t len,const JSHandle<JSTaggedValue> & callbackFnHandle,base::HolesType holes)1723 JSHandle<TaggedArray> JSStableArray::SortIndexedProperties(JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal,
1724 int64_t len, const JSHandle<JSTaggedValue> &callbackFnHandle,
1725 base::HolesType holes)
1726 {
1727 JSHandle<JSObject> thisObj(thread, thisObjVal.GetTaggedValue());
1728 JSHandle<TaggedArray> elements(thread, thisObj->GetElements(thread));
1729 ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1730 if (!elements->GetClass()->IsMutantTaggedArray()) {
1731 kind = ElementsKind::GENERIC;
1732 }
1733 // 1. fill elements into items.
1734 JSHandle<TaggedArray> items(thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len));
1735 bool kRead = false;
1736 int64_t tmp = 0;
1737 for (int k = 0; k < len; k++) {
1738 JSTaggedValue kValue = ElementAccessor::FastGet(thread, elements, k, kind);
1739 if (holes == base::HolesType::SKIP_HOLES) {
1740 kRead = (kValue != JSTaggedValue::Hole());
1741 } else {
1742 ASSERT(holes == base::HolesType::READ_THROUGH_HOLES);
1743 kRead = true;
1744 }
1745 if (kRead) {
1746 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
1747 items->Set(thread, tmp++, kValue);
1748 }
1749 }
1750 // 2. trim
1751 if (len > tmp) {
1752 items->Trim(thread, tmp);
1753 }
1754 // 3. Sort items using an implementation-defined sequence of calls to SortCompare.
1755 // If any such call returns an abrupt completion,
1756 // stop before performing any further calls to SortCompare and return that Completion Record.
1757 base::TimSort::Sort(thread, items, callbackFnHandle);
1758 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
1759 // 4. Return items.
1760 return items;
1761 }
1762
CopySortedListToReceiver(JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,JSHandle<TaggedArray> sortedList,uint32_t len)1763 JSTaggedValue JSStableArray::CopySortedListToReceiver(JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal,
1764 JSHandle<TaggedArray> sortedList, uint32_t len)
1765 {
1766 // 6. Let itemCount be the number of elements in sortedList.
1767 uint32_t itemCount = sortedList->GetLength();
1768
1769 // grow elements if len > newLength.
1770 JSHandle<JSObject> thisObj(thisObjVal);
1771 uint32_t newLength = std::max(JSHandle<JSArray>::Cast(thisObjVal)->GetArrayLength(), itemCount);
1772 TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements(thread).GetTaggedObject());
1773 if (newLength > ElementAccessor::GetElementsLength(thread, thisObj)) {
1774 elements = *JSObject::GrowElementsCapacity(thread, thisObj, newLength, true);
1775 }
1776
1777 JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
1778 bool needTransition = true;
1779 // 7. Let j be 0.
1780 // 8. Repeat, while j < itemCount,
1781 // a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true).
1782 // b. Set j to j + 1.
1783 for (uint32_t j = 0; j < itemCount; j++) {
1784 valueHandle.Update(sortedList->Get(thread, j));
1785 ElementAccessor::Set(thread, thisObj, j, valueHandle, needTransition);
1786 }
1787 // 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to
1788 // preserve the number of holes that were detected and excluded from the sort.
1789 // 10. Repeat, while j < len,
1790 // a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))).
1791 // b. Set j to j + 1.
1792 valueHandle.Update(JSTaggedValue::Hole());
1793 for (uint32_t j = itemCount; j < newLength; j++) {
1794 ElementAccessor::Set(thread, thisObj, j, valueHandle, needTransition);
1795 }
1796 JSHandle<JSArray>::Cast(thisObj)->SetArrayLength(thread, newLength);
1797 return thisObj.GetTaggedValue();
1798 }
1799
Sort(JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,const JSHandle<JSTaggedValue> & callbackFnHandle)1800 JSTaggedValue JSStableArray::Sort(JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal,
1801 const JSHandle<JSTaggedValue> &callbackFnHandle)
1802 {
1803 // 3. Let len be ?LengthOfArrayLike(obj).
1804 int64_t len = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
1805 // ReturnIfAbrupt(len).
1806 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1807 // If len is 0 or 1, no need to sort
1808 if (len == 0 || len == 1) {
1809 return thisObjVal.GetTaggedValue();
1810 }
1811 if (callbackFnHandle->IsUndefined()) {
1812 JSArray::SortElementsByObject(thread, JSHandle<JSObject>::Cast(thisObjVal), callbackFnHandle);
1813 return thisObjVal.GetTaggedValue();
1814 }
1815 JSHandle<TaggedArray> sortedList = JSStableArray::SortIndexedProperties(
1816 thread, thisObjVal, len, callbackFnHandle, base::HolesType::SKIP_HOLES);
1817 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1818 if (thisObjVal->IsStableJSArray(thread)) {
1819 CopySortedListToReceiver(thread, thisObjVal, sortedList, len);
1820 } else {
1821 JSArray::CopySortedListToReceiver(thread, thisObjVal, sortedList, len);
1822 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1823 }
1824 return thisObjVal.GetTaggedValue();
1825 }
1826
Fill(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & value,int64_t start,int64_t end)1827 JSTaggedValue JSStableArray::Fill(JSThread *thread, const JSHandle<JSObject> &thisObj,
1828 const JSHandle<JSTaggedValue> &value, int64_t start,
1829 int64_t end)
1830 {
1831 JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObj));
1832 uint32_t length = ElementAccessor::GetElementsLength(thread, thisObj);
1833 ElementsKind oldKind = thisObj->GetClass()->GetElementsKind();
1834 if (start == 0 && end == length) {
1835 if (oldKind != ElementsKind::GENERIC) {
1836 JSHClass::TransitToElementsKindUncheck(thread, thisObj, Elements::ToElementsKind(value.GetTaggedValue()));
1837 }
1838 } else if (JSHClass::TransitToElementsKind(thread, thisObj, value)) {
1839 ElementsKind newKind = thisObj->GetClass()->GetElementsKind();
1840 Elements::MigrateArrayWithKind(thread, thisObj, oldKind, newKind);
1841 }
1842 if (length >= end) {
1843 if (thisObj->GetElements(thread).IsMutantTaggedArray()) {
1844 ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1845 TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements(thread));
1846 JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1847 value.GetTaggedValue(), kind));
1848 for (int64_t idx = start; idx < end; idx++) {
1849 elements->Set<false>(thread, idx, migratedValue);
1850 }
1851 } else {
1852 TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements(thread));
1853 for (int64_t idx = start; idx < end; idx++) {
1854 elements->Set(thread, idx, value);
1855 }
1856 }
1857 } else {
1858 LOG_ECMA(FATAL) << "this branch is unreachable";
1859 UNREACHABLE();
1860 }
1861 if (JSHandle<JSArray>::Cast(thisObj)->GetArrayLength() < end) {
1862 JSHandle<JSArray>::Cast(thisObj)->SetArrayLength(thread, end);
1863 }
1864 return thisObj.GetTaggedValue();
1865 }
1866
HandleFindLastOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,JSMutableHandle<JSTaggedValue> & kValue,int64_t & k)1867 JSTaggedValue JSStableArray::HandleFindLastOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1868 JSHandle<JSTaggedValue> callbackFnHandle,
1869 JSHandle<JSTaggedValue> thisArgHandle,
1870 JSMutableHandle<JSTaggedValue> &kValue, int64_t &k)
1871 {
1872 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1873 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1874 const uint32_t argsLength = 3; // 3: «kValue, k, O»
1875 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
1876 while (k >= 0) {
1877 JSTaggedValue val = ElementAccessor::Get(thread, thisObjHandle, k);
1878 if (!val.IsHole()) {
1879 kValue.Update(val);
1880 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1881 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1882 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1883 kValue.Update(res);
1884 } else {
1885 kValue.Update(JSTaggedValue::Undefined());
1886 }
1887 EcmaRuntimeCallInfo *info =
1888 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1889 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1890 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
1891 callResult = JSFunction::Call(info);
1892 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1893 if (callResult.ToBoolean()) {
1894 return callResult;
1895 }
1896 k--;
1897 if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) <= k) {
1898 break;
1899 }
1900 if (!thisObjVal->IsStableJSArray(thread)) {
1901 break;
1902 }
1903 }
1904 return callResult;
1905 }
1906
HandleReduceRightOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> & accumulator,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)1907 JSTaggedValue JSStableArray::HandleReduceRightOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1908 JSHandle<JSTaggedValue> callbackFnHandle,
1909 JSMutableHandle<JSTaggedValue> &accumulator,
1910 JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
1911 {
1912 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1913 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1914 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1915 JSTaggedValue callResult = JSTaggedValue::Undefined();
1916 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1917 const int32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1918 int64_t len = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1919 while (k >= 0) {
1920 key.Update(JSTaggedValue(k));
1921 kValue.Update(ElementAccessor::Get(thread, thisObjHandle, k));
1922 if (!kValue.GetTaggedValue().IsHole()) {
1923 EcmaRuntimeCallInfo *info =
1924 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1925 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1926 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1927 key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1928 callResult = JSFunction::Call(info);
1929 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1930 accumulator.Update(callResult);
1931 } else {
1932 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key);
1933 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1934 if (exists) {
1935 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue();
1936 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1937 kValue.Update(res);
1938 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle,
1939 thisArgHandle, undefined, argsLength);
1940 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1941 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1942 key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1943 callResult = JSFunction::Call(info);
1944 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1945 accumulator.Update(callResult);
1946 }
1947 }
1948 k--;
1949 int64_t newLen = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1950 if (!thisObjVal->IsStableJSArray(thread) || newLen != len) {
1951 return base::BuiltinsBase::GetTaggedBoolean(false);
1952 }
1953 }
1954 return base::BuiltinsBase::GetTaggedBoolean(true);
1955 }
1956 } // namespace panda::ecmascript
1957