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