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
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 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
38 if (newLength > ElementAccessor::GetElementsLength(thisObjHandle)) {
39 elements = *JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
40 }
41 bool needTransition = true;
42 for (uint32_t k = 0; k < argc; k++) {
43 JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
44 ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value, needTransition);
45 }
46 receiver->SetArrayLength(thread, newLength);
47
48 return JSTaggedValue(newLength);
49 }
50
Push(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)51 JSTaggedValue JSStableArray::Push(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
52 {
53 JSThread *thread = argv->GetThread();
54 uint32_t argc = argv->GetArgsNumber();
55 uint32_t oldLength = receiver->GetArrayLength();
56 uint32_t newLength = argc + oldLength;
57 JSHandle<JSObject> thisObjHandle(receiver);
58
59 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
60 if (newLength > ElementAccessor::GetElementsLength(thisObjHandle)) {
61 elements = *JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
62 }
63 bool needTransition = true;
64 for (uint32_t k = 0; k < argc; k++) {
65 JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
66 ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value, needTransition);
67 }
68 receiver->SetArrayLength(thread, newLength);
69
70 return JSTaggedValue(newLength);
71 }
72
Pop(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)73 JSTaggedValue JSStableArray::Pop(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
74 {
75 JSThread *thread = argv->GetThread();
76 uint32_t length = receiver->GetArrayLength();
77 if (length == 0) {
78 return JSTaggedValue::Undefined();
79 }
80 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
81 JSSharedArray::CheckAndCopyArray(thread, receiver);
82 JSHandle<JSObject> obj(receiver);
83 uint32_t capacity = ElementAccessor::GetElementsLength(obj);
84 uint32_t index = length - 1;
85 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Hole());
86 if (index < capacity) {
87 result.Update(ElementAccessor::Get(obj, index));
88 }
89 if (!result->IsHole()) {
90 if (TaggedArray::ShouldTrim(capacity, index)) {
91 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
92 elements->Trim(thread, index);
93 } else {
94 ElementAccessor::Set(thread, obj, index, holeHandle, false);
95 }
96 } else {
97 JSHandle<JSTaggedValue> thisObjVal(receiver);
98 result.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue());
99 }
100 receiver->SetArrayLength(thread, index);
101 return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
102 }
103
Pop(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)104 JSTaggedValue JSStableArray::Pop(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
105 {
106 JSThread *thread = argv->GetThread();
107 uint32_t length = receiver->GetArrayLength();
108 if (length == 0) {
109 return JSTaggedValue::Undefined();
110 }
111 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
112 JSArray::CheckAndCopyArray(thread, receiver);
113 JSHandle<JSObject> obj(receiver);
114 uint32_t capacity = ElementAccessor::GetElementsLength(obj);
115 uint32_t index = length - 1;
116 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Hole());
117 if (index < capacity) {
118 result.Update(ElementAccessor::Get(obj, index));
119 }
120 if (!result->IsHole()) {
121 if (TaggedArray::ShouldTrim(capacity, index)) {
122 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
123 elements->Trim(thread, index);
124 } else {
125 ElementAccessor::Set(thread, obj, index, holeHandle, false);
126 }
127 } else {
128 JSHandle<JSTaggedValue> thisObjVal(receiver);
129 result.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue());
130 }
131 receiver->SetArrayLength(thread, index);
132 return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
133 }
134
HandleArray(JSHandle<JSObject> & newArrayHandle,uint32_t & actualDeleteCount,JSThread * thread,uint32_t & start,JSHandle<JSObject> & thisObjHandle,JSHandle<JSTaggedValue> & holeHandle)135 void JSStableArray::HandleArray(JSHandle<JSObject> &newArrayHandle, uint32_t &actualDeleteCount,
136 JSThread *thread, uint32_t &start, JSHandle<JSObject> &thisObjHandle,
137 JSHandle<JSTaggedValue> &holeHandle)
138 {
139 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
140 if (actualDeleteCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
141 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
142 }
143
144 for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
145 if ((start + idx) >= ElementAccessor::GetElementsLength(thisObjHandle)) {
146 ElementAccessor::Set(thread, newArrayHandle, idx, holeHandle, true);
147 } else {
148 JSHandle<JSTaggedValue> valueHandle(thread, ElementAccessor::Get(thisObjHandle, start + idx));
149 ElementAccessor::Set(thread, newArrayHandle, idx, valueHandle, true);
150 }
151 }
152 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
153 }
154
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)155 JSTaggedValue JSStableArray::UpdateArrayCapacity(JSHandle<JSObject> &thisObjHandle, uint32_t &len,
156 uint32_t &insertCount, uint32_t &actualDeleteCount,
157 JSHandle<JSArray> &receiver, uint32_t &start,
158 JSThread *thread, bool &needTransition,
159 JSHandle<JSTaggedValue> &holeHandle,
160 EcmaRuntimeCallInfo *argv, JSHandle<JSTaggedValue> &thisObjVal,
161 JSHandle<JSTaggedValue> &lengthKey)
162 {
163 uint32_t oldCapacity = ElementAccessor::GetElementsLength(thisObjHandle);
164 ASSERT(len + insertCount >= actualDeleteCount);
165 uint32_t newCapacity = len - actualDeleteCount + insertCount;
166 TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject());
167 JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
168 uint32_t argc = argv->GetArgsNumber();
169 if (newCapacity > oldCapacity) {
170 srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
171 }
172 if (insertCount < actualDeleteCount) {
173 JSArray::CheckAndCopyArray(thread, receiver);
174 srcElementsHandle.Update(receiver->GetElements());
175 for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
176 JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Hole());
177 if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
178 element.Update(ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount));
179 }
180 if ((idx + insertCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
181 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
182 }
183 }
184
185 if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
186 srcElementsHandle->Trim(thread, newCapacity);
187 } else {
188 for (uint32_t idx = newCapacity; idx < len; idx++) {
189 if (idx < ElementAccessor::GetElementsLength(thisObjHandle)) {
190 ElementAccessor::Set(thread, thisObjHandle, idx, holeHandle, needTransition);
191 }
192 }
193 }
194 } else {
195 ASSERT(len >= actualDeleteCount);
196 for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
197 JSHandle<JSTaggedValue> element(thread, ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount - 1));
198 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount - 1, element, needTransition);
199 }
200 }
201
202 for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
203 ElementAccessor::Set(thread, thisObjHandle, idx, argv->GetCallArg(i), needTransition);
204 }
205
206 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
207 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
208 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
209 return JSTaggedValue::Undefined();
210 }
211
Splice(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount,JSHandle<JSObject> newArrayHandle,uint32_t len)212 JSTaggedValue JSStableArray::Splice(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
213 uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount,
214 JSHandle<JSObject> newArrayHandle, uint32_t len)
215 {
216 JSThread *thread = argv->GetThread();
217
218 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
219 JSHandle<JSObject> thisObjHandle(receiver);
220 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
221 JSArray::CheckAndCopyArray(thread, receiver);
222 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
223 bool needTransition = true;
224 if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
225 HandleArray(newArrayHandle, actualDeleteCount, thread, start, thisObjHandle, holeHandle);
226 } else {
227 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
228 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
229 uint32_t k = 0;
230 while (k < actualDeleteCount) {
231 uint32_t from = start + k;
232 fromKey.Update(JSTaggedValue(from));
233 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
234 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
235 if (exists) {
236 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
237 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
238 toKey.Update(JSTaggedValue(k));
239 if (newArrayHandle->IsJSProxy()) {
240 toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
241 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
242 }
243 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
244 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
245 }
246 k++;
247 }
248
249 JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
250 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
251 true);
252 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
253 }
254 UpdateArrayCapacity(thisObjHandle, len, insertCount, actualDeleteCount, receiver, start,
255 thread, needTransition, holeHandle, argv, thisObjVal, lengthKey);
256 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
257 return newArrayHandle.GetTaggedValue();
258 }
259
Splice(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount,JSHandle<JSObject> newArrayHandle,uint32_t len)260 JSTaggedValue JSStableArray::Splice(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv,
261 uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount,
262 JSHandle<JSObject> newArrayHandle, uint32_t len)
263 {
264 JSThread *thread = argv->GetThread();
265 uint32_t argc = argv->GetArgsNumber();
266
267 JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
268 JSHandle<JSObject> thisObjHandle(receiver);
269 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
270 JSSharedArray::CheckAndCopyArray(thread, receiver);
271 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
272 TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject());
273 JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
274 bool needTransition = true;
275 if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
276 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
277 if (actualDeleteCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
278 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
279 }
280
281 for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
282 if ((start + idx) >= ElementAccessor::GetElementsLength(thisObjHandle)) {
283 ElementAccessor::Set(thread, newArrayHandle, idx, holeHandle, needTransition);
284 } else {
285 JSHandle<JSTaggedValue> valueHandle(thread, ElementAccessor::Get(thisObjHandle, start + idx));
286 ElementAccessor::Set(thread, newArrayHandle, idx, valueHandle, needTransition);
287 }
288 }
289 JSHandle<JSSharedArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
290 } else {
291 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
292 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
293 uint32_t k = 0;
294 while (k < actualDeleteCount) {
295 uint32_t from = start + k;
296 fromKey.Update(JSTaggedValue(from));
297 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
298 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
299 if (exists) {
300 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
301 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
302 toKey.Update(JSTaggedValue(k));
303 if (newArrayHandle->IsJSProxy()) {
304 toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
305 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
306 }
307 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
308 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
309 }
310 k++;
311 }
312
313 JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
314 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
315 true);
316 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
317 }
318 uint32_t oldCapacity = ElementAccessor::GetElementsLength(thisObjHandle);
319 ASSERT(len + insertCount >= actualDeleteCount);
320 uint32_t newCapacity = len - actualDeleteCount + insertCount;
321 if (newCapacity > oldCapacity) {
322 srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
323 }
324 if (insertCount < actualDeleteCount) {
325 JSSharedArray::CheckAndCopyArray(thread, receiver);
326 srcElementsHandle.Update(receiver->GetElements());
327 for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
328 JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Hole());
329 if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
330 element.Update(ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount));
331 }
332 if ((idx + insertCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
333 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
334 }
335 }
336
337 if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
338 srcElementsHandle->Trim(thread, newCapacity);
339 } else {
340 for (uint32_t idx = newCapacity; idx < len; idx++) {
341 if (idx < ElementAccessor::GetElementsLength(thisObjHandle)) {
342 ElementAccessor::Set(thread, thisObjHandle, idx, holeHandle, needTransition);
343 }
344 }
345 }
346 } else {
347 ASSERT(len >= actualDeleteCount);
348 for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
349 JSHandle<JSTaggedValue> element(thread, ElementAccessor::Get(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(thisObjHandle, 0));
376 bool needTransition = false;
377 for (uint32_t k = 1; k < length; k++) {
378 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(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(thisObjHandle, 0));
404 bool needTransition = false;
405 for (uint32_t k = 1; k < length; k++) {
406 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(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
SetSepValue(JSHandle<EcmaString> sepStringHandle,int & sep,uint32_t & sepLength)420 void JSStableArray::SetSepValue(JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength)
421 {
422 if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) {
423 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
424 sep = EcmaStringAccessor(sepStringHandle).Get(0);
425 } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) {
426 sep = JSStableArray::SeparatorFlag::MINUS_TWO;
427 sepLength = 0;
428 } else {
429 sep = JSStableArray::SeparatorFlag::MINUS_ONE;
430 sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
431 }
432 }
433
WorthUseTreeString(int sep,size_t allocateLength,uint32_t len)434 bool JSStableArray::WorthUseTreeString(int sep, size_t allocateLength, uint32_t len)
435 {
436 if (allocateLength >= TREE_STRING_THRESHOLD) {
437 size_t treeStringElementNum = (sep == MINUS_TWO) ? (len - 1) : (2 * (len - 1));
438 // if sep is MINUS_TWO, means all the elements in treeString is len -1;
439 // otherwise, the num of elements is (len-1)(string in vector) + (len -1)(num of seps)
440 if (treeStringElementNum * TreeEcmaString::SIZE <= allocateLength) {
441 // heuristic: if tree string uses less memory than linestring, it is worth.
442 // In other words, we hope tree string can work for the large strings join.
443 return true;
444 }
445 }
446 return false;
447 }
448
JoinUseTreeString(const JSThread * thread,const JSHandle<JSTaggedValue> receiverValue,const JSHandle<EcmaString> sepStringHandle,const int sep,CVector<JSHandle<EcmaString>> & vec)449 JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread* thread, const JSHandle<JSTaggedValue> receiverValue,
450 const JSHandle<EcmaString> sepStringHandle, const int sep,
451 CVector<JSHandle<EcmaString>>& vec)
452 {
453 // Do not concat the elements one by one, it will make the tree string unbalanced. Concat each element with its
454 // right neighbor first level by level, then the tree string will be balanced as possible.
455 if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
456 auto last = std::prev(vec.end());
457 for (auto iter = vec.begin(); iter != last; ++iter) {
458 *iter = JSHandle<EcmaString>(
459 thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), *iter, sepStringHandle));
460 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
461 }
462 }
463 size_t elemNum = vec.size();
464 while (elemNum > 1) {
465 size_t newNum = (elemNum + 1) / NUM_2;
466 for (size_t i = 0; i < elemNum / NUM_2; ++i) {
467 vec[i] = JSHandle<EcmaString>(
468 thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), vec[NUM_2 * i], vec[NUM_2 * i + 1]));
469 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
470 }
471 if (elemNum % NUM_2 == 1) {
472 vec[newNum - 1] = vec[elemNum - 1];
473 }
474 elemNum = newNum;
475 }
476 thread->GetCurrentEcmaContext()->JoinStackPopFastPath(receiverValue);
477 return vec.front().GetTaggedValue();
478 }
479
Join(JSThread * thread,JSHandle<JSArray> receiver,JSHandle<EcmaString> sepStringHandle,int64_t length)480 JSTaggedValue JSStableArray::Join(JSThread *thread, JSHandle<JSArray> receiver,
481 JSHandle<EcmaString> sepStringHandle, int64_t length)
482 {
483 int sep = ',';
484 uint32_t sepLength = 1;
485 auto context = thread->GetCurrentEcmaContext();
486 JSHandle<JSTaggedValue> receiverValue = JSHandle<JSTaggedValue>::Cast(receiver);
487 SetSepValue(sepStringHandle, sep, sepLength);
488 auto factory = thread->GetEcmaVM()->GetFactory();
489 bool noCircular = context->JoinStackPushFastPath(receiverValue);
490 if (!noCircular) {
491 return factory->GetEmptyString().GetTaggedValue();
492 }
493 if (length == 0) {
494 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
495 context->JoinStackPopFastPath(receiverValue);
496 return globalConst->GetEmptyString();
497 }
498 JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
499 uint64_t allocateLength = 0;
500 bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
501 JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
502 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
503 uint32_t elementsLength = ElementAccessor::GetElementsLength(obj);
504 uint32_t len = elementsLength > length ? length : elementsLength;
505 if (elementsLength == 0 && length != 0) {
506 len = length;
507 }
508 if (len <= 1) {
509 // sep unused, set isOneByte to default(true)
510 isOneByte = true;
511 }
512 CVector<JSHandle<EcmaString>> vec;
513 vec.reserve(len);
514 for (uint32_t k = 0; k < len; k++) {
515 JSTaggedValue element = JSTaggedValue::Undefined();
516 if (k < elementsLength) {
517 element = ElementAccessor::Get(obj, k);
518 if (element.IsHole() && JSTaggedValue::HasProperty(thread, receiverValue, k)) {
519 element = JSArray::FastGetPropertyByValue(thread, receiverValue, k).GetTaggedValue();
520 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
521 }
522 }
523 if (!element.IsUndefinedOrNull() && !element.IsHole()) {
524 if (!element.IsString()) {
525 elementHandle.Update(element);
526 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
527 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
528 element = strElement.GetTaggedValue();
529 }
530 auto nextStr = EcmaString::Cast(element.GetTaggedObject());
531 vec.emplace_back(thread, nextStr);
532 isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
533 allocateLength += EcmaStringAccessor(nextStr).GetLength();
534 } else {
535 vec.emplace_back(globalConst->GetHandledEmptyString());
536 }
537 }
538 if (len > 0) {
539 allocateLength += static_cast<uint64_t>(sepLength) * (len - 1);
540 }
541 if (allocateLength > EcmaString::MAX_STRING_LENGTH) {
542 context->JoinStackPopFastPath(receiverValue);
543 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
544 }
545 if (WorthUseTreeString(sep, allocateLength, len)) {
546 return JoinUseTreeString(thread, receiverValue, sepStringHandle, sep, vec);
547 }
548 auto newString =
549 EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
550 int current = 0;
551 DISALLOW_GARBAGE_COLLECTION;
552 for (uint32_t k = 0; k < len; k++) {
553 if (k > 0) {
554 if (sep >= 0) {
555 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
556 } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
557 EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
558 allocateLength - static_cast<uint32_t>(current), sepLength);
559 }
560 current += static_cast<int>(sepLength);
561 }
562 JSHandle<EcmaString> nextStr = vec[k];
563 int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
564 EcmaStringAccessor::ReadData(newString, *nextStr, current,
565 allocateLength - static_cast<uint32_t>(current), nextLength);
566 current += nextLength;
567 }
568 ASSERT_PRINT(
569 isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
570 context->JoinStackPopFastPath(receiverValue);
571 return JSTaggedValue(newString);
572 }
573
Join(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)574 JSTaggedValue JSStableArray::Join(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
575 {
576 JSThread *thread = argv->GetThread();
577 uint32_t length = receiver->GetArrayLength();
578 JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
579 int sep = ',';
580 uint32_t sepLength = 1;
581 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
582 JSHandle<EcmaString> sepStringHandle = JSHandle<EcmaString>::Cast(globalConst->GetHandledCommaString());
583 auto context = thread->GetCurrentEcmaContext();
584 JSHandle<JSTaggedValue> receiverValue = JSHandle<JSTaggedValue>::Cast(receiver);
585 if (!sepHandle->IsUndefined()) {
586 if (sepHandle->IsString()) {
587 sepStringHandle = JSHandle<EcmaString>::Cast(sepHandle);
588 } else {
589 sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
590 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
591 }
592 SetSepValue(sepStringHandle, sep, sepLength);
593 }
594 if (length == 0) {
595 context->JoinStackPopFastPath(receiverValue);
596 return globalConst->GetEmptyString();
597 }
598 JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
599 size_t allocateLength = 0;
600 bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
601 JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
602 uint32_t elementsLength = ElementAccessor::GetElementsLength(obj);
603 uint32_t len = elementsLength > length ? length : elementsLength;
604 if (elementsLength == 0 && length != 0) {
605 len = length;
606 }
607 if (len <= 1) {
608 // sep unused, set isOneByte to default(true)
609 isOneByte = true;
610 }
611 CVector<JSHandle<EcmaString>> vec;
612 vec.reserve(len);
613 for (uint32_t k = 0; k < len; k++) {
614 JSTaggedValue element = JSTaggedValue::Undefined();
615 if (k < elementsLength) {
616 element = ElementAccessor::Get(obj, k);
617 }
618 if (!element.IsUndefinedOrNull() && !element.IsHole()) {
619 if (!element.IsString()) {
620 elementHandle.Update(element);
621 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
622 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
623 element = strElement.GetTaggedValue();
624 }
625 auto nextStr = EcmaString::Cast(element.GetTaggedObject());
626 vec.emplace_back(thread, nextStr);
627 isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
628 allocateLength += EcmaStringAccessor(nextStr).GetLength();
629 } else {
630 vec.emplace_back(globalConst->GetHandledEmptyString());
631 }
632 }
633 if (len > 0) {
634 allocateLength += sepLength * (len - 1);
635 }
636 if (WorthUseTreeString(sep, allocateLength, len)) {
637 return JoinUseTreeString(thread, receiverValue, sepStringHandle, sep, vec);
638 }
639 auto newString = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), allocateLength, isOneByte);
640 int current = 0;
641 DISALLOW_GARBAGE_COLLECTION;
642 for (uint32_t k = 0; k < len; k++) {
643 if (k > 0) {
644 if (sep >= 0) {
645 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
646 } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
647 EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
648 allocateLength - static_cast<uint32_t>(current), sepLength);
649 }
650 current += static_cast<int>(sepLength);
651 }
652 JSHandle<EcmaString> nextStr = vec[k];
653 int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
654 EcmaStringAccessor::ReadData(newString, *nextStr, current,
655 allocateLength - static_cast<uint32_t>(current), nextLength);
656 current += nextLength;
657 }
658 ASSERT_PRINT(
659 isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
660 context->JoinStackPopFastPath(receiverValue);
661 return JSTaggedValue(newString);
662 }
663
HandleFindIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)664 JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
665 JSHandle<JSTaggedValue> callbackFnHandle,
666 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
667 {
668 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
669 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
670 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
671 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
672 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
673 const int32_t argsLength = 3; // 3: ?kValue, k, O?
674 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
675 while (k < len) {
676 // Elements of thisObjHandle may change.
677 JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
678 if (val.IsHole()) {
679 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
680 if (res.IsHole()) {
681 kValue.Update(JSTaggedValue::Undefined());
682 } else {
683 kValue.Update(res);
684 }
685 } else {
686 kValue.Update(val);
687 }
688 EcmaRuntimeCallInfo *info =
689 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
690 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
691 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
692 callResult = JSFunction::Call(info);
693 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
694 if (callResult.ToBoolean()) {
695 return callResult;
696 }
697 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
698 len = ElementAccessor::GetElementsLength(thisObjHandle);
699 }
700 k++;
701 if (!thisObjVal->IsStableJSArray(thread)) {
702 return callResult;
703 }
704 }
705 return callResult;
706 }
707
HandleFindLastIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)708 JSTaggedValue JSStableArray::HandleFindLastIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
709 JSHandle<JSTaggedValue> callbackFnHandle,
710 JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
711 {
712 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
713 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
714 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
715 const int32_t argsLength = 3; // 3: ?kValue, k, O?
716 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
717 while (k >= 0) {
718 // Elements of thisObjHandle may change.
719 JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
720 if (val.IsHole()) {
721 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
722 if (res.IsHole()) {
723 kValue.Update(JSTaggedValue::Undefined());
724 } else {
725 kValue.Update(res);
726 }
727 } else {
728 kValue.Update(val);
729 }
730 EcmaRuntimeCallInfo *info =
731 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
732 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
733 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
734 callResult = JSFunction::Call(info);
735 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
736 if (callResult.ToBoolean()) {
737 return callResult;
738 }
739 k--;
740 if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) - 1 < k) {
741 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
742 return callResult;
743 }
744 if (!thisObjVal->IsStableJSArray(thread)) {
745 return callResult;
746 }
747 }
748 return callResult;
749 }
750
HandleEveryOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)751 JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
752 JSHandle<JSTaggedValue> callbackFnHandle,
753 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
754 {
755 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
756 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
757 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
758 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
759 const int32_t argsLength = 3; // 3: ?kValue, k, O?
760 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(true);
761 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
762 while (k < len) {
763 // Elements of thisObjHandle may change.
764 kValue.Update(ElementAccessor::Get(thisObjHandle, k));
765 if (!kValue.GetTaggedValue().IsHole()) {
766 EcmaRuntimeCallInfo *info =
767 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
768 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
769 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
770 callResult = JSFunction::Call(info);
771 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
772 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
773 len = ElementAccessor::GetElementsLength(thisObjHandle);
774 }
775 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
776 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
777 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
778 EcmaRuntimeCallInfo *info =
779 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
780 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
781 info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
782 callResult = JSFunction::Call(info);
783 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
784 }
785 if (!callResult.ToBoolean()) {
786 return base::BuiltinsBase::GetTaggedBoolean(false);
787 }
788 k++;
789 if (!thisObjVal->IsStableJSArray(thread)) {
790 return base::BuiltinsBase::GetTaggedBoolean(true);
791 }
792 }
793 return base::BuiltinsBase::GetTaggedBoolean(true);
794 }
795
HandleSomeOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)796 JSTaggedValue JSStableArray::HandleSomeOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
797 JSHandle<JSTaggedValue> callbackFnHandle,
798 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
799 {
800 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
801 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
802 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
803 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
804 const int32_t argsLength = 3; // 3: ?kValue, k, O?
805 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
806 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
807 while (k < len) {
808 // Elements of thisObjHandle may change.
809 kValue.Update(ElementAccessor::Get(thisObjHandle, k));
810 if (!kValue.GetTaggedValue().IsHole()) {
811 EcmaRuntimeCallInfo *info =
812 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
813 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
814 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
815 callResult = JSFunction::Call(info);
816 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
817 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
818 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
819 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
820 EcmaRuntimeCallInfo *info =
821 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
822 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
823 info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
824 callResult = JSFunction::Call(info);
825 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
826 }
827 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
828 len = ElementAccessor::GetElementsLength(thisObjHandle);
829 }
830 if (callResult.ToBoolean()) {
831 return base::BuiltinsBase::GetTaggedBoolean(true);
832 }
833 k++;
834 if (!thisObjVal->IsStableJSArray(thread)) {
835 return base::BuiltinsBase::GetTaggedBoolean(false);
836 }
837 }
838 return base::BuiltinsBase::GetTaggedBoolean(false);
839 }
840
HandleforEachOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t len,uint32_t & k)841 JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
842 JSHandle<JSTaggedValue> callbackFnHandle,
843 JSHandle<JSTaggedValue> thisArgHandle, uint32_t len, uint32_t &k)
844 {
845 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
846 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
847 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
848 const int32_t argsLength = 3; // 3: ?kValue, k, O?
849 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
850 if (ElementAccessor::GetElementsLength(thisObjHandle) <= k) {
851 return base::BuiltinsBase::GetTaggedBoolean(false);
852 }
853 while (k < len) {
854 // Elements of thisObjHandle may change.
855 kValue.Update(ElementAccessor::Get(thisObjHandle, k));
856 if (!kValue.GetTaggedValue().IsHole()) {
857 key.Update(JSTaggedValue(k));
858 EcmaRuntimeCallInfo *info =
859 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
860 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
861 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
862 JSTaggedValue funcResult = JSFunction::Call(info);
863 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
864 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
865 len = ElementAccessor::GetElementsLength(thisObjHandle);
866 }
867 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
868 key.Update(JSTaggedValue(k));
869 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
870 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
871 EcmaRuntimeCallInfo *info =
872 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
873 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
874 info->SetCallArg(kValue1.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
875 JSTaggedValue funcResult = JSFunction::Call(info);
876 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
877 }
878 k++;
879 if (!thisObjVal->IsStableJSArray(thread)) {
880 break;
881 }
882 }
883 return base::BuiltinsBase::GetTaggedBoolean(true);
884 }
885
886 template <class Predicate>
FindRawData(IndexOfContext & ctx,Predicate && predicate)887 JSTaggedValue JSStableArray::FindRawData(IndexOfContext &ctx, Predicate &&predicate)
888 {
889 DISALLOW_GARBAGE_COLLECTION;
890 JSTaggedType *data = nullptr;
891 JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
892 ElementsKind kind = ElementsKind::GENERIC;
893 if (elementsValue.IsMutantTaggedArray()) {
894 JSHandle<MutantTaggedArray> elements(ctx.thread, elementsValue);
895 data = elements->GetData();
896 kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
897 } else {
898 JSHandle<TaggedArray> elements(ctx.thread, elementsValue);
899 // Note: GC is guaranteed not to happen since no new object is created during the searching process.
900 data = elements->GetData();
901 // Note: for stable arrays, elements->GetLength() returns the CAPACITY, instead of actual length!
902 }
903 JSTaggedType *first = data + ctx.fromIndex;
904 JSTaggedType *last = data + ctx.length;
905
906 JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
907 for (JSTaggedType *cur = first; cur < last; ++cur) {
908 JSTaggedValue convertedCur = JSTaggedValue(*cur);
909 if (elementsValue.IsMutantTaggedArray()) {
910 convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
911 }
912 if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
913 if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
914 return base::BuiltinsBase::GetTaggedInt64(cur - data);
915 }
916 continue;
917 }
918 // Fallback slow path
919 indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
920 bool found = base::ArrayHelper::ElementIsStrictEqualTo(
921 ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
922 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
923 if (found) {
924 return indexHandle.GetTaggedValue();
925 }
926 }
927 return JSTaggedValue(-1); // Not found
928 }
929
930 template <class Predicate>
FindLastRawData(IndexOfContext & ctx,Predicate && predicate)931 JSTaggedValue JSStableArray::FindLastRawData(IndexOfContext &ctx, Predicate &&predicate)
932 {
933 DISALLOW_GARBAGE_COLLECTION;
934 JSTaggedType *data = nullptr;
935 JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
936 ElementsKind kind = ElementsKind::GENERIC;
937 if (elementsValue.IsMutantTaggedArray()) {
938 JSHandle<MutantTaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
939 data = elements->GetData();
940 kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
941 } else {
942 JSHandle<TaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
943 // Note: GC is guaranteed not to happen since no new object is created during the searching process.
944 data = elements->GetData();
945 }
946 JSTaggedType *beforeFirst = data - 1;
947 JSTaggedType *beforeLast = data + ctx.fromIndex;
948
949 JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
950 for (JSTaggedType *cur = beforeLast; cur > beforeFirst; --cur) {
951 JSTaggedValue convertedCur = JSTaggedValue(*cur);
952 if (elementsValue.IsMutantTaggedArray()) {
953 convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
954 }
955 if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
956 if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
957 return base::BuiltinsBase::GetTaggedInt64(cur - data);
958 }
959 continue;
960 }
961 // Fallback slow path
962 indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
963 bool found = base::ArrayHelper::ElementIsStrictEqualTo(
964 ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
965 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
966 if (found) {
967 return indexHandle.GetTaggedValue();
968 }
969 }
970 return JSTaggedValue(-1); // Not found
971 }
972
973 template <class Predicate>
FindRawDataDispatch(IndexOfType type,IndexOfContext & ctx,Predicate && predicate)974 JSTaggedValue JSStableArray::FindRawDataDispatch(IndexOfType type, IndexOfContext &ctx, Predicate &&predicate)
975 {
976 switch (type) {
977 case IndexOfType::IndexOf:
978 return FindRawData(ctx, std::forward<Predicate>(predicate));
979 case IndexOfType::LastIndexOf:
980 return FindLastRawData(ctx, std::forward<Predicate>(predicate));
981 default:
982 UNREACHABLE();
983 }
984 }
985
986 // Zeros need special judge
IndexOfZero(IndexOfType type,IndexOfContext & ctx)987 JSTaggedValue JSStableArray::IndexOfZero(IndexOfType type, IndexOfContext &ctx)
988 {
989 return FindRawDataDispatch(type, ctx, [](JSTaggedType cur) {
990 return JSTaggedValue(cur).IsExactlyZero();
991 });
992 }
993
IndexOfInt32(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)994 JSTaggedValue JSStableArray::IndexOfInt32(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
995 {
996 ASSERT(searchElement.IsInt());
997 int32_t untagged = searchElement.GetInt();
998 if (untagged == 0) {
999 return IndexOfZero(type, ctx);
1000 }
1001 JSTaggedType targetInt32 = searchElement.GetRawData();
1002 JSTaggedType targetDouble = JSTaggedValue(static_cast<double>(untagged)).GetRawData();
1003 return FindRawDataDispatch(type, ctx, [targetInt32, targetDouble](JSTaggedType cur) {
1004 return cur == targetInt32 || cur == targetDouble;
1005 });
1006 }
1007
IndexOfDouble(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1008 JSTaggedValue JSStableArray::IndexOfDouble(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1009 {
1010 ASSERT(searchElement.IsDouble());
1011 double untagged = searchElement.GetDouble();
1012 if (std::isnan(untagged)) {
1013 return JSTaggedValue(-1);
1014 }
1015 if (untagged == 0.0) {
1016 return IndexOfZero(type, ctx);
1017 }
1018 JSTaggedType targetDouble = searchElement.GetRawData();
1019 if (searchElement.WithinInt32()) {
1020 JSTaggedType targetInt32 = JSTaggedValue(static_cast<int32_t>(untagged)).GetRawData();
1021 return FindRawDataDispatch(type, ctx, [targetDouble, targetInt32](JSTaggedType cur) {
1022 return cur == targetDouble || cur == targetInt32;
1023 });
1024 } else {
1025 return FindRawDataDispatch(type, ctx, [targetDouble](JSTaggedType cur) {
1026 return cur == targetDouble;
1027 });
1028 }
1029 }
1030
IndexOfObjectAddress(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1031 JSTaggedValue JSStableArray::IndexOfObjectAddress(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1032 {
1033 ASSERT(searchElement.IsObject());
1034 JSTaggedType targetAddress = searchElement.GetRawData();
1035 return FindRawDataDispatch(type, ctx, [targetAddress](JSTaggedType cur) {
1036 return cur == targetAddress;
1037 });
1038 }
1039
IndexOfString(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1040 JSTaggedValue JSStableArray::IndexOfString(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1041 {
1042 ASSERT(searchElement.IsString());
1043 JSTaggedType targetAddress = searchElement.GetRawData();
1044 return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
1045 if (targetAddress == cur) {
1046 return true;
1047 }
1048 JSTaggedValue curValue(cur);
1049 if (!curValue.IsString()) {
1050 return false;
1051 }
1052 return JSTaggedValue::StringCompare(
1053 EcmaString::Cast(curValue.GetTaggedObject()),
1054 EcmaString::Cast(searchElement.GetTaggedObject()));
1055 });
1056 }
1057
IndexOfBigInt(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1058 JSTaggedValue JSStableArray::IndexOfBigInt(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1059 {
1060 ASSERT(searchElement.IsBigInt());
1061 JSTaggedType targetAddress = searchElement.GetRawData();
1062 return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
1063 if (cur == targetAddress) {
1064 return true;
1065 }
1066 JSTaggedValue curValue(cur);
1067 if (!curValue.IsBigInt()) {
1068 return false;
1069 }
1070 return BigInt::Equal(curValue, searchElement);
1071 });
1072 }
1073
IndexOfDispatch(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1074 JSTaggedValue JSStableArray::IndexOfDispatch(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1075 {
1076 if (searchElement.IsInt()) {
1077 return IndexOfInt32(type, ctx, searchElement);
1078 } else if (searchElement.IsDouble()) {
1079 return IndexOfDouble(type, ctx, searchElement);
1080 } else if (searchElement.IsString()) {
1081 return IndexOfString(type, ctx, searchElement);
1082 } else if (searchElement.IsBigInt()) {
1083 return IndexOfBigInt(type, ctx, searchElement);
1084 } else {
1085 return IndexOfObjectAddress(type, ctx, searchElement);
1086 }
1087 }
1088
IndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1089 JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1090 JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1091 {
1092 IndexOfContext ctx;
1093 ctx.thread = thread;
1094 ctx.receiver = receiver;
1095 ctx.searchElement = searchElement;
1096 ctx.fromIndex = from;
1097 ctx.length = len;
1098 return IndexOfDispatch(IndexOfType::IndexOf, ctx, searchElement.GetTaggedValue());
1099 }
1100
LastIndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1101 JSTaggedValue JSStableArray::LastIndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1102 JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1103 {
1104 IndexOfContext ctx;
1105 ctx.thread = thread;
1106 ctx.receiver = receiver;
1107 ctx.searchElement = searchElement;
1108 ctx.fromIndex = from;
1109 ctx.length = len;
1110 return IndexOfDispatch(IndexOfType::LastIndexOf, ctx, searchElement.GetTaggedValue());
1111 }
1112
Filter(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t & toIndex)1113 JSTaggedValue JSStableArray::Filter(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1114 EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex)
1115 {
1116 JSThread *thread = argv->GetThread();
1117 JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1118 JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1119 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1120 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1121 JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
1122 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1123 const int32_t argsLength = 3; // 3: ?kValue, k, O?
1124 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1125 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1126 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1127 while (k < len) {
1128 // Elements of thisObjHandle may change.
1129 JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
1130 if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1131 value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1132 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1133 }
1134 kValue.Update(value);
1135 if (!kValue.GetTaggedValue().IsHole()) {
1136 key.Update(JSTaggedValue(k));
1137 EcmaRuntimeCallInfo *info =
1138 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1139 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1140 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1141 JSTaggedValue callResult = JSFunction::Call(info);
1142 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1143 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1144 len = ElementAccessor::GetElementsLength(thisObjHandle);
1145 }
1146 bool boolResult = callResult.ToBoolean();
1147 if (boolResult) {
1148 toIndexHandle.Update(JSTaggedValue(toIndex));
1149 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
1150 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1151 toIndex++;
1152 }
1153 }
1154 k++;
1155 if (!thisObjVal->IsStableJSArray(thread)) {
1156 break;
1157 }
1158 }
1159 return base::BuiltinsBase::GetTaggedDouble(true);
1160 }
1161
Map(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t len)1162 JSTaggedValue JSStableArray::Map(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1163 EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len)
1164 {
1165 JSThread *thread = argv->GetThread();
1166 JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1167 JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1168 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1169 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1170 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1171 JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
1172 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1173 const int32_t argsLength = 3; // 3: ?kValue, k, O?
1174 while (k < len) {
1175 // Elements of thisObjHandle may change.
1176 JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
1177 if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1178 value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1179 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1180 }
1181 kValue.Update(value);
1182 if (!kValue.GetTaggedValue().IsHole()) {
1183 key.Update(JSTaggedValue(k));
1184 EcmaRuntimeCallInfo *info =
1185 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1186 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1187 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1188 JSTaggedValue mapResult = JSFunction::Call(info);
1189 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1190 mapResultHandle.Update(mapResult);
1191 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
1192 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1193 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1194 len = ElementAccessor::GetElementsLength(thisObjHandle);
1195 }
1196 }
1197 k++;
1198 if (!thisObjVal->IsStableJSArray(thread)) {
1199 break;
1200 }
1201 }
1202 return base::BuiltinsBase::GetTaggedDouble(true);
1203 }
1204
Reverse(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & lower,uint32_t len)1205 JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1206 int64_t &lower, uint32_t len)
1207 {
1208 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1209 if (thisObjHandle->IsJSArray()) {
1210 JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObjHandle));
1211 }
1212 ElementsKind kind = thisObjHandle->GetClass()->GetElementsKind();
1213 JSHandle<TaggedArray> elements(thread, thisObjHandle->GetElements());
1214 bool enableElementsKind = thread->GetEcmaVM()->IsEnableElementsKind();
1215 if (enableElementsKind) {
1216 if (kind == ElementsKind::INT || kind == ElementsKind::HOLE_INT) {
1217 return FastReverse(thread, elements, lower, len, ElementsKind::INT);
1218 } else if (kind == ElementsKind::NUMBER || kind == ElementsKind::HOLE_NUMBER) {
1219 return FastReverse(thread, elements, lower, len, ElementsKind::NUMBER);
1220 }
1221 }
1222 return FastReverse(thread, elements, lower, len, ElementsKind::TAGGED);
1223 }
1224
FastReverse(JSThread * thread,JSHandle<TaggedArray> elements,int64_t & lower,uint32_t len,ElementsKind kind)1225 JSTaggedValue JSStableArray::FastReverse(JSThread *thread, JSHandle<TaggedArray> elements,
1226 int64_t &lower, uint32_t len, ElementsKind kind)
1227 {
1228 JSMutableHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
1229 JSMutableHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
1230 int64_t middle = std::floor(len / 2);
1231 while (lower != middle) {
1232 if (elements->GetLength() != len) {
1233 break;
1234 }
1235 int64_t upper = static_cast<int64_t>(len) - lower - 1;
1236 lowerValueHandle.Update(ElementAccessor::FastGet(elements, lower, kind));
1237 upperValueHandle.Update(ElementAccessor::FastGet(elements, upper, kind));
1238 ElementAccessor::FastSet(thread, elements, lower, upperValueHandle, kind);
1239 ElementAccessor::FastSet(thread, elements, upper, lowerValueHandle, kind);
1240 lower++;
1241 }
1242 return base::BuiltinsBase::GetTaggedDouble(true);
1243 }
1244
Concat(JSThread * thread,JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & n)1245 JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle<JSObject> newArrayHandle,
1246 JSHandle<JSObject> thisObjHandle, int64_t &k, int64_t &n)
1247 {
1248 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1249 int64_t thisLen = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
1250 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1251 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
1252 while (k < thisLen) {
1253 if (ElementAccessor::GetElementsLength(thisObjHandle) != thisLen) {
1254 break;
1255 }
1256 toKey.Update(JSTaggedValue(n));
1257 JSTaggedValue kValue = ElementAccessor::Get(thisObjHandle, k);
1258 if (!kValue.IsHole()) {
1259 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, JSHandle<JSTaggedValue>(thread, kValue));
1260 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1261 }
1262 n++;
1263 k++;
1264 }
1265 return base::BuiltinsBase::GetTaggedDouble(true);
1266 }
1267
1268 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::SHARED>(
1269 JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1270 uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1271 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::NON_SHARED>(
1272 JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1273 uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1274
1275 template<TypedArrayKind typedArrayKind>
FastCopyFromArrayToTypedArray(JSThread * thread,JSHandle<JSTypedArray> & targetArray,DataViewType targetType,uint64_t targetOffset,uint32_t srcLength,JSHandle<JSObject> & obj)1276 JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle<JSTypedArray> &targetArray,
1277 DataViewType targetType, uint64_t targetOffset,
1278 uint32_t srcLength, JSHandle<JSObject> &obj)
1279 {
1280 JSHandle<JSTaggedValue> targetBuffer(thread, targetArray->GetViewedArrayBufferOrByteArray());
1281 // If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
1282 if (BuiltinsArrayBufferType<typedArrayKind>::Type::IsDetachedBuffer(targetBuffer.GetTaggedValue())) {
1283 THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.",
1284 JSTaggedValue::Exception());
1285 }
1286 uint32_t targetLength = targetArray->GetArrayLength();
1287 uint32_t targetByteOffset = targetArray->GetByteOffset();
1288 uint32_t targetElementSize = TypedArrayHelper::GetSizeFromType(targetType);
1289 if (srcLength + targetOffset > targetLength) {
1290 THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of length and targetOffset is greater than targetLength.",
1291 JSTaggedValue::Exception());
1292 }
1293 uint32_t targetByteIndex = static_cast<uint32_t>(targetOffset * targetElementSize + targetByteOffset);
1294 ContentType contentType = targetArray->GetContentType();
1295 uint32_t elemLen = ElementAccessor::GetElementsLength(obj);
1296 if (contentType == ContentType::BigInt) {
1297 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1298 JSMutableHandle<JSTaggedValue> elem(thread, JSTaggedValue::Hole());
1299 for (uint32_t i = 0; i < srcLength; i++) {
1300 if (i < elemLen) {
1301 elem.Update(ElementAccessor::Get(obj, i));
1302 } else {
1303 elem.Update(JSTaggedValue::Hole());
1304 }
1305 kValue.Update(JSTaggedValue::ToBigInt(thread, elem));
1306 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1307 BuiltinsArrayBufferType<typedArrayKind>::Type::SetValueInBuffer(
1308 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kValue, true);
1309 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1310 targetByteIndex += targetElementSize;
1311 }
1312 } else {
1313 double val = 0.0;
1314 uint32_t copyLen = srcLength > elemLen ? elemLen : srcLength;
1315 for (uint32_t i = 0; i < copyLen; i++) {
1316 JSTaggedValue taggedVal = ElementAccessor::Get(obj, i);
1317 if (!taggedVal.IsNumber()) {
1318 JSTaggedNumber taggedNumber = JSTaggedValue::ToNumber(thread, taggedVal);
1319 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1320 val = taggedNumber.GetNumber();
1321 } else {
1322 val = taggedVal.GetNumber();
1323 }
1324 BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1325 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1326 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1327 targetByteIndex += targetElementSize;
1328 }
1329
1330 for (uint32_t i = copyLen; i < srcLength; i++) {
1331 val = JSTaggedNumber(base::NAN_VALUE).GetNumber();
1332 BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1333 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1334 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1335 targetByteIndex += targetElementSize;
1336 }
1337 }
1338 return JSTaggedValue::Undefined();
1339 }
1340
At(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)1341 JSTaggedValue JSStableArray::At(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
1342 {
1343 JSThread *thread = argv->GetThread();
1344 uint32_t thisLen = receiver->GetArrayLength();
1345 if (thisLen == 0) {
1346 return JSTaggedValue::Undefined();
1347 }
1348 JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1349 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1350 int64_t relativeIndex = index.GetNumber();
1351 int64_t k = 0;
1352 if (relativeIndex >= 0) {
1353 k = relativeIndex;
1354 } else {
1355 k = static_cast<int64_t>(thisLen) + relativeIndex;
1356 }
1357 if (k < 0 || k >= thisLen) {
1358 return JSTaggedValue::Undefined();
1359 }
1360
1361 auto result = JSTaggedValue::Hole();
1362 result = ElementAccessor::Get(JSHandle<JSObject>::Cast(receiver), k);
1363 return result.IsHole() ? JSTaggedValue::Undefined() : result;
1364 }
1365
At(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)1366 JSTaggedValue JSStableArray::At(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
1367 {
1368 JSThread *thread = argv->GetThread();
1369 uint32_t thisLen = receiver->GetArrayLength();
1370 if (thisLen == 0) {
1371 return JSTaggedValue::Undefined();
1372 }
1373 JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1374 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1375 int64_t relativeIndex = index.GetNumber();
1376 int64_t k = 0;
1377 if (relativeIndex >= 0) {
1378 k = relativeIndex;
1379 } else {
1380 k = static_cast<int64_t>(thisLen) + relativeIndex;
1381 }
1382 if (k < 0 || k >= thisLen) {
1383 return JSTaggedValue::Undefined();
1384 }
1385
1386 auto result = JSTaggedValue::Hole();
1387 result = ElementAccessor::Get(JSHandle<JSObject>::Cast(receiver), k);
1388 return result.IsHole() ? JSTaggedValue::Undefined() : result;
1389 }
1390
With(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount,int64_t index,JSHandle<JSTaggedValue> value)1391 JSTaggedValue JSStableArray::With(JSThread *thread, JSHandle<JSArray> receiver,
1392 int64_t insertCount, int64_t index, JSHandle<JSTaggedValue> value)
1393 {
1394 JSHandle<JSObject> thisObjHandle(receiver);
1395 JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1396 JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1397 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1398 JSHandle<JSObject> newArrayHandle(newArray);
1399
1400 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1401 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1402
1403 if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1404 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1405 }
1406 ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1407 bool needTransition = true;
1408 for (uint32_t idx = 0; idx < insertCount; idx++) {
1409 if (idx == index) {
1410 ElementAccessor::Set(thread, newArrayHandle, idx, value, needTransition);
1411 } else {
1412 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1413 if (kValue->IsHole()) {
1414 ElementAccessor::Set(thread, newArrayHandle, idx, undefinedHandle, needTransition);
1415 } else {
1416 ElementAccessor::Set(thread, newArrayHandle, idx, kValue, needTransition);
1417 }
1418 }
1419 }
1420 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1421 return newArrayHandle.GetTaggedValue();
1422 }
1423
ToSpliced(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,int64_t argc,int64_t actualStart,int64_t actualSkipCount,int64_t insertCount)1424 JSTaggedValue JSStableArray::ToSpliced(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
1425 int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount)
1426 {
1427 JSThread *thread = argv->GetThread();
1428
1429 JSHandle<JSObject> thisObjHandle(receiver);
1430 JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1431 JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1432 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1433 JSHandle<JSObject> newArrayHandle(newArray);
1434
1435 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1436 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1437
1438 if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1439 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1440 }
1441 ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1442 int64_t i = 0;
1443 int64_t r = actualStart + actualSkipCount;
1444 bool needTransition = true;
1445 for (int64_t idx = 0; idx < actualStart; idx++, i++) {
1446 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1447 if (kValue->IsHole()) {
1448 ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1449 } else {
1450 ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1451 }
1452 }
1453
1454 for (uint32_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items.
1455 auto element = base::BuiltinsBase::GetCallArg(argv, pos);
1456 ElementAccessor::Set(thread, newArrayHandle, i, element, needTransition);
1457 ++i;
1458 }
1459
1460 while (i < insertCount) {
1461 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, r));
1462 if (kValue->IsHole()) {
1463 ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1464 } else {
1465 ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1466 }
1467 ++i;
1468 ++r;
1469 }
1470
1471 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1472
1473 return newArrayHandle.GetTaggedValue();
1474 }
1475
ToReversed(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount)1476 JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle<JSArray> receiver,
1477 int64_t insertCount)
1478 {
1479 JSHandle<JSObject> thisObjHandle(receiver);
1480 JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1481 JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1482 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1483 JSHandle<JSObject> newArrayHandle(newArray);
1484
1485 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1486 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1487
1488 if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1489 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1490 }
1491 ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1492 bool needTransition = true;
1493 for (uint32_t idx = 0; idx < insertCount; idx++) {
1494 JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1495 if (kValue->IsHole()) {
1496 ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1,
1497 undefinedHandle, needTransition);
1498 } else {
1499 ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1, kValue, needTransition);
1500 }
1501 }
1502 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1503
1504 return newArrayHandle.GetTaggedValue();
1505 }
1506
Reduce(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> accumulator,int64_t & k,int64_t & len)1507 JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1508 JSHandle<JSTaggedValue> callbackFnHandle,
1509 JSMutableHandle<JSTaggedValue> accumulator, int64_t &k, int64_t &len)
1510 {
1511 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1512 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1513 JSTaggedValue callResult = JSTaggedValue::Undefined();
1514 while (k < len) {
1515 JSTaggedValue kValue(ElementAccessor::Get(thisObjHandle, k));
1516 if (!kValue.IsHole()) {
1517 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1518 const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1519 EcmaRuntimeCallInfo *info =
1520 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength);
1521 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1522 info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k),
1523 thisObjVal.GetTaggedValue());
1524 callResult = JSFunction::Call(info);
1525 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1526 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1527 len = ElementAccessor::GetElementsLength(thisObjHandle);
1528 }
1529 accumulator.Update(callResult);
1530 }
1531 k++;
1532 if (!thisObjVal->IsStableJSArray(thread)) {
1533 break;
1534 }
1535 }
1536 return base::BuiltinsBase::GetTaggedDouble(true);
1537 }
1538
Slice(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & count)1539 JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1540 int64_t &k, int64_t &count)
1541 {
1542 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1543 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1544 int64_t len = static_cast<int64_t>(ElementAccessor::GetElementsLength(thisObjHandle));
1545 int64_t oldLen;
1546 if (len > k + count) {
1547 oldLen = count;
1548 } else {
1549 oldLen = std::max<int64_t>(len - k, 0);
1550 }
1551 JSHandle<JSObject> arrayObj = factory->NewAndCopyJSArrayObject(thisObjHandle, count, oldLen, k);
1552 for (int i = 0; i < count; i++) {
1553 JSMutableHandle<JSTaggedValue> value(thread, ElementAccessor::Get(arrayObj, i));
1554 if (value->IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i + k)) {
1555 value.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, i + k).GetTaggedValue());
1556 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1557 ElementAccessor::Set(thread, arrayObj, i, value, true);
1558 }
1559 }
1560 return arrayObj.GetTaggedValue();
1561 }
1562
Sort(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & callbackFnHandle)1563 JSTaggedValue JSStableArray::Sort(JSThread *thread, const JSHandle<JSObject> &thisObj,
1564 const JSHandle<JSTaggedValue> &callbackFnHandle)
1565 {
1566 JSArray::SortElementsByObject(thread, thisObj, callbackFnHandle);
1567 return thisObj.GetTaggedValue();
1568 }
1569
Fill(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & value,int64_t start,int64_t end,int64_t len)1570 JSTaggedValue JSStableArray::Fill(JSThread *thread, const JSHandle<JSObject> &thisObj,
1571 const JSHandle<JSTaggedValue> &value, int64_t start,
1572 int64_t end, int64_t len)
1573 {
1574 JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObj));
1575 uint32_t length = ElementAccessor::GetElementsLength(thisObj);
1576 ElementsKind oldKind = thisObj->GetClass()->GetElementsKind();
1577 if (JSHClass::TransitToElementsKind(thread, thisObj, value)) {
1578 ElementsKind newKind = thisObj->GetClass()->GetElementsKind();
1579 Elements::MigrateArrayWithKind(thread, thisObj, oldKind, newKind);
1580 }
1581 if (length >= end) {
1582 if (thisObj->GetElements().IsMutantTaggedArray()) {
1583 ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1584 TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1585 JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1586 value.GetTaggedValue(), kind));
1587 for (int64_t idx = start; idx < end; idx++) {
1588 elements->Set<false>(thread, idx, migratedValue);
1589 }
1590 } else {
1591 TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1592 for (int64_t idx = start; idx < end; idx++) {
1593 elements->Set(thread, idx, value);
1594 }
1595 }
1596 return thisObj.GetTaggedValue();
1597 } else {
1598 if (thisObj->GetElements().IsMutantTaggedArray()) {
1599 ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1600 JSHandle<MutantTaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewMutantTaggedArray(len);
1601 JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1602 value.GetTaggedValue(), kind));
1603 for (int64_t idx = start; idx < end; idx++) {
1604 newElements->Set<false>(thread, idx, migratedValue);
1605 }
1606 thisObj->SetElements(thread, newElements);
1607 } else {
1608 JSHandle<TaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len);
1609 for (int64_t idx = start; idx < end; idx++) {
1610 newElements->Set(thread, idx, value);
1611 }
1612 thisObj->SetElements(thread, newElements);
1613 }
1614 return thisObj.GetTaggedValue();
1615 }
1616 }
1617
HandleFindLastOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,JSMutableHandle<JSTaggedValue> & kValue,int64_t & k)1618 JSTaggedValue JSStableArray::HandleFindLastOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1619 JSHandle<JSTaggedValue> callbackFnHandle,
1620 JSHandle<JSTaggedValue> thisArgHandle,
1621 JSMutableHandle<JSTaggedValue> &kValue, int64_t &k)
1622 {
1623 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1624 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1625 const uint32_t argsLength = 3; // 3: «kValue, k, O»
1626 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
1627 while (k >= 0) {
1628 JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
1629 if (!val.IsHole()) {
1630 kValue.Update(val);
1631 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1632 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1633 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1634 kValue.Update(res);
1635 } else {
1636 kValue.Update(JSTaggedValue::Undefined());
1637 }
1638 EcmaRuntimeCallInfo *info =
1639 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1640 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1641 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
1642 callResult = JSFunction::Call(info);
1643 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1644 if (callResult.ToBoolean()) {
1645 return callResult;
1646 }
1647 k--;
1648 ASSERT(ElementAccessor::GetElementsLength(thisObjHandle) > 0);
1649 if (ElementAccessor::GetElementsLength(thisObjHandle) - 1 < k) {
1650 break;
1651 }
1652 if (!thisObjVal->IsStableJSArray(thread)) {
1653 break;
1654 }
1655 }
1656 return callResult;
1657 }
1658
HandleReduceRightOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> & accumulator,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)1659 JSTaggedValue JSStableArray::HandleReduceRightOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1660 JSHandle<JSTaggedValue> callbackFnHandle,
1661 JSMutableHandle<JSTaggedValue> &accumulator,
1662 JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
1663 {
1664 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1665 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1666 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1667 JSTaggedValue callResult = JSTaggedValue::Undefined();
1668 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1669 const int32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1670 int64_t len = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1671 while (k >= 0) {
1672 key.Update(JSTaggedValue(k));
1673 kValue.Update(ElementAccessor::Get(thisObjHandle, k));
1674 if (!kValue.GetTaggedValue().IsHole()) {
1675 EcmaRuntimeCallInfo *info =
1676 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1677 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1678 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1679 key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1680 callResult = JSFunction::Call(info);
1681 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1682 accumulator.Update(callResult);
1683 } else {
1684 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key);
1685 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1686 if (exists) {
1687 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue();
1688 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1689 kValue.Update(res);
1690 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle,
1691 thisArgHandle, undefined, argsLength);
1692 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1693 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1694 key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1695 callResult = JSFunction::Call(info);
1696 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1697 accumulator.Update(callResult);
1698 }
1699 }
1700 k--;
1701 int64_t newLen = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1702 if (!thisObjVal->IsStableJSArray(thread) || newLen != len) {
1703 return base::BuiltinsBase::GetTaggedBoolean(false);
1704 }
1705 }
1706 return base::BuiltinsBase::GetTaggedBoolean(true);
1707 }
1708 } // namespace panda::ecmascript
1709