1 /*
2 * Copyright (c) 2021 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/array_helper.h"
19 #include "ecmascript/base/builtins_base.h"
20 #include "ecmascript/base/typed_array_helper-inl.h"
21 #include "ecmascript/builtins/builtins_arraybuffer.h"
22 #include "ecmascript/ecma_macros.h"
23 #include "ecmascript/ecma_vm.h"
24 #include "ecmascript/ecma_string-inl.h"
25 #include "ecmascript/element_accessor.h"
26 #include "ecmascript/element_accessor-inl.h"
27 #include "ecmascript/global_env.h"
28 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
29 #include "ecmascript/js_array.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/js_tagged_value.h"
32 #include "ecmascript/object_factory.h"
33 #include "ecmascript/tagged_array.h"
34 #include "macros.h"
35
36 namespace panda::ecmascript {
37 using TypedArrayHelper = base::TypedArrayHelper;
38 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
39
Push(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)40 JSTaggedValue JSStableArray::Push(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
41 {
42 JSThread *thread = argv->GetThread();
43 uint32_t argc = argv->GetArgsNumber();
44 uint32_t oldLength = receiver->GetArrayLength();
45 uint32_t newLength = argc + oldLength;
46 JSHandle<JSObject> thisObjHandle(receiver);
47
48 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
49 if (newLength > ElementAccessor::GetElementsLength(thisObjHandle)) {
50 elements = *JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
51 }
52 bool needTransition = true;
53 for (uint32_t k = 0; k < argc; k++) {
54 JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
55 ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value.GetTaggedValue(), needTransition);
56 }
57 receiver->SetArrayLength(thread, newLength);
58
59 return JSTaggedValue(newLength);
60 }
61
Pop(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)62 JSTaggedValue JSStableArray::Pop(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
63 {
64 JSThread *thread = argv->GetThread();
65 uint32_t length = receiver->GetArrayLength();
66 if (length == 0) {
67 return JSTaggedValue::Undefined();
68 }
69
70 JSArray::CheckAndCopyArray(thread, receiver);
71 JSHandle<JSObject> obj(receiver);
72 uint32_t capacity = ElementAccessor::GetElementsLength(obj);
73 uint32_t index = length - 1;
74 auto result = JSTaggedValue::Hole();
75 if (index < capacity) {
76 result = ElementAccessor::Get(obj, index);
77 }
78 if (!result.IsHole()) {
79 if (TaggedArray::ShouldTrim(capacity, index)) {
80 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
81 elements->Trim(thread, index);
82 } else {
83 ElementAccessor::Set(thread, obj, index, JSTaggedValue::Hole(), false);
84 }
85 } else {
86 JSHandle<JSTaggedValue> thisObjVal(receiver);
87 result = JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue();
88 }
89 receiver->SetArrayLength(thread, index);
90 return result.IsHole() ? JSTaggedValue::Undefined() : result;
91 }
92
Splice(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount,JSHandle<JSObject> newArrayHandle,uint32_t len)93 JSTaggedValue JSStableArray::Splice(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
94 uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount,
95 JSHandle<JSObject> newArrayHandle, uint32_t len)
96 {
97 JSThread *thread = argv->GetThread();
98 uint32_t argc = argv->GetArgsNumber();
99
100 JSHandle<JSObject> thisObjHandle(receiver);
101 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
102 JSArray::CheckAndCopyArray(thread, receiver);
103 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
104 TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject());
105 JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
106 bool needTransition = true;
107 if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
108 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
109 if (actualDeleteCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
110 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
111 }
112
113 for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
114 if ((start + idx) >= ElementAccessor::GetElementsLength(thisObjHandle)) {
115 ElementAccessor::Set(thread, newArrayHandle, idx, JSTaggedValue::Hole(), needTransition);
116 } else {
117 ElementAccessor::Set(thread, newArrayHandle, idx,
118 ElementAccessor::Get(thisObjHandle, start + idx), needTransition);
119 }
120 }
121 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
122 } else {
123 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
124 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
125 uint32_t k = 0;
126 while (k < actualDeleteCount) {
127 uint32_t from = start + k;
128 fromKey.Update(JSTaggedValue(from));
129 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
130 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
131 if (exists) {
132 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
133 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
134 toKey.Update(JSTaggedValue(k));
135 if (newArrayHandle->IsJSProxy()) {
136 toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
137 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
138 }
139 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
140 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
141 }
142 k++;
143 }
144
145 JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
146 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
147 true);
148 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
149 }
150 uint32_t oldCapacity = ElementAccessor::GetElementsLength(thisObjHandle);
151 uint32_t newCapacity = len - actualDeleteCount + insertCount;
152 if (newCapacity > oldCapacity) {
153 srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
154 }
155 if (insertCount < actualDeleteCount) {
156 JSArray::CheckAndCopyArray(thread, receiver);
157 srcElementsHandle.Update(receiver->GetElements());
158 for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
159 auto element = JSTaggedValue::Hole();
160 if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
161 element = ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount);
162 }
163 if ((idx + insertCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
164 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
165 }
166 }
167
168 if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
169 srcElementsHandle->Trim(thread, newCapacity);
170 } else {
171 for (uint32_t idx = newCapacity; idx < len; idx++) {
172 if (idx < ElementAccessor::GetElementsLength(thisObjHandle)) {
173 ElementAccessor::Set(thread, thisObjHandle, idx, JSTaggedValue::Hole(), needTransition);
174 }
175 }
176 }
177 } else {
178 for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
179 auto element = ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount - 1);
180 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount - 1, element, needTransition);
181 }
182 }
183
184 for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
185 ElementAccessor::Set(thread, thisObjHandle, idx, argv->GetCallArg(i), needTransition);
186 }
187
188 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
189 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
190 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
191 return newArrayHandle.GetTaggedValue();
192 }
193
Shift(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)194 JSTaggedValue JSStableArray::Shift(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
195 {
196 JSThread *thread = argv->GetThread();
197 JSHandle<JSObject> thisObjHandle(receiver);
198 uint32_t length = receiver->GetArrayLength();
199 if (length == 0) {
200 return JSTaggedValue::Undefined();
201 }
202 JSArray::CheckAndCopyArray(thread, receiver);
203 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
204 auto result = ElementAccessor::Get(thisObjHandle, 0);
205 bool needTransition = false;
206 for (uint32_t k = 1; k < length; k++) {
207 auto kValue = ElementAccessor::Get(thisObjHandle, k);
208 ElementAccessor::Set(thread, thisObjHandle, k - 1, kValue, needTransition);
209 }
210 uint32_t capacity = ElementAccessor::GetElementsLength(thisObjHandle);
211 uint32_t index = length - 1;
212 if (TaggedArray::ShouldTrim(capacity, index)) {
213 elements->Trim(thread, index);
214 } else {
215 ElementAccessor::Set(thread, thisObjHandle, index, JSTaggedValue::Hole(), needTransition);
216 }
217 receiver->SetArrayLength(thread, index);
218 return result.IsHole() ? JSTaggedValue::Undefined() : result;
219 }
220
SetSepValue(JSHandle<EcmaString> sepStringHandle,int & sep,uint32_t & sepLength)221 void JSStableArray::SetSepValue(JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength)
222 {
223 if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) {
224 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
225 sep = EcmaStringAccessor(sepStringHandle).Get(0);
226 } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) {
227 sep = JSStableArray::SeparatorFlag::MINUS_TWO;
228 sepLength = 0;
229 } else {
230 sep = JSStableArray::SeparatorFlag::MINUS_ONE;
231 sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
232 }
233 }
234
Join(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)235 JSTaggedValue JSStableArray::Join(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
236 {
237 JSThread *thread = argv->GetThread();
238 uint32_t length = receiver->GetArrayLength();
239 JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
240 int sep = ',';
241 uint32_t sepLength = 1;
242 JSHandle<EcmaString> sepStringHandle;
243 auto context = thread->GetCurrentEcmaContext();
244 JSHandle<JSTaggedValue> receiverValue = JSHandle<JSTaggedValue>::Cast(receiver);
245 if (!sepHandle->IsUndefined()) {
246 if (sepHandle->IsString()) {
247 sepStringHandle = JSHandle<EcmaString>::Cast(sepHandle);
248 } else {
249 sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
250 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
251 }
252 SetSepValue(sepStringHandle, sep, sepLength);
253 }
254 if (length == 0) {
255 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
256 context->JoinStackPopFastPath(receiverValue);
257 return globalConst->GetEmptyString();
258 }
259 JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
260 size_t allocateLength = 0;
261 bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
262 CVector<JSHandle<EcmaString>> vec;
263 JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
264 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
265 uint32_t elementsLength = ElementAccessor::GetElementsLength(obj);
266 uint32_t len = elementsLength > length ? length : elementsLength;
267 if (elementsLength == 0 && length != 0) {
268 len = length;
269 }
270 if (len <= 1) {
271 // sep unused, set isOneByte to default(true)
272 isOneByte = true;
273 }
274 for (uint32_t k = 0; k < len; k++) {
275 JSTaggedValue element = JSTaggedValue::Undefined();
276 if (k < elementsLength) {
277 element = ElementAccessor::Get(obj, k);
278 }
279 if (!element.IsUndefinedOrNull() && !element.IsHole()) {
280 if (!element.IsString()) {
281 elementHandle.Update(element);
282 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
283 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
284 element = strElement.GetTaggedValue();
285 }
286 auto nextStr = EcmaString::Cast(element.GetTaggedObject());
287 JSHandle<EcmaString> nextStrHandle(thread, nextStr);
288 vec.push_back(nextStrHandle);
289 isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
290 allocateLength += EcmaStringAccessor(nextStr).GetLength();
291 } else {
292 vec.push_back(JSHandle<EcmaString>(globalConst->GetHandledEmptyString()));
293 }
294 }
295 if (len > 0) {
296 allocateLength += sepLength * (len - 1);
297 }
298 auto newString = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), allocateLength, isOneByte);
299 int current = 0;
300 DISALLOW_GARBAGE_COLLECTION;
301 for (uint32_t k = 0; k < len; k++) {
302 if (k > 0) {
303 if (sep >= 0) {
304 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
305 } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
306 EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
307 allocateLength - static_cast<uint32_t>(current), sepLength);
308 }
309 current += static_cast<int>(sepLength);
310 }
311 JSHandle<EcmaString> nextStr = vec[k];
312 int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
313 EcmaStringAccessor::ReadData(newString, *nextStr, current,
314 allocateLength - static_cast<uint32_t>(current), nextLength);
315 current += nextLength;
316 }
317 ASSERT_PRINT(
318 isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
319 context->JoinStackPopFastPath(receiverValue);
320 return JSTaggedValue(newString);
321 }
322
HandleFindIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)323 JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
324 JSHandle<JSTaggedValue> callbackFnHandle,
325 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
326 {
327 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
328 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
329 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
330 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
331 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
332 const int32_t argsLength = 3; // 3: ?kValue, k, O?
333 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
334 while (k < len) {
335 // Elements of thisObjHandle may change.
336 JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
337 if (val.IsHole()) {
338 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
339 if (res.IsHole()) {
340 kValue.Update(JSTaggedValue::Undefined());
341 } else {
342 kValue.Update(res);
343 }
344 } else {
345 kValue.Update(val);
346 }
347 EcmaRuntimeCallInfo *info =
348 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
349 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
350 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
351 callResult = JSFunction::Call(info);
352 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
353 if (callResult.ToBoolean()) {
354 return callResult;
355 }
356 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
357 len = ElementAccessor::GetElementsLength(thisObjHandle);
358 }
359 k++;
360 if (!thisObjVal->IsStableJSArray(thread)) {
361 return callResult;
362 }
363 }
364 return callResult;
365 }
366
HandleFindLastIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)367 JSTaggedValue JSStableArray::HandleFindLastIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
368 JSHandle<JSTaggedValue> callbackFnHandle,
369 JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
370 {
371 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
372 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
373 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
374 const int32_t argsLength = 3; // 3: ?kValue, k, O?
375 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
376 while (k >= 0) {
377 // Elements of thisObjHandle may change.
378 JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
379 if (val.IsHole()) {
380 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
381 if (res.IsHole()) {
382 kValue.Update(JSTaggedValue::Undefined());
383 } else {
384 kValue.Update(res);
385 }
386 } else {
387 kValue.Update(val);
388 }
389 EcmaRuntimeCallInfo *info =
390 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
391 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
392 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
393 callResult = JSFunction::Call(info);
394 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
395 if (callResult.ToBoolean()) {
396 return callResult;
397 }
398 k--;
399 if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) - 1 < k) {
400 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
401 return callResult;
402 }
403 if (!thisObjVal->IsStableJSArray(thread)) {
404 return callResult;
405 }
406 }
407 return callResult;
408 }
409
HandleEveryOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)410 JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
411 JSHandle<JSTaggedValue> callbackFnHandle,
412 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
413 {
414 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
415 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
416 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
417 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
418 const int32_t argsLength = 3; // 3: ?kValue, k, O?
419 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(true);
420 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
421 while (k < len) {
422 // Elements of thisObjHandle may change.
423 kValue.Update(ElementAccessor::Get(thisObjHandle, k));
424 if (!kValue.GetTaggedValue().IsHole()) {
425 EcmaRuntimeCallInfo *info =
426 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
427 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
428 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
429 callResult = JSFunction::Call(info);
430 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
431 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
432 len = ElementAccessor::GetElementsLength(thisObjHandle);
433 }
434 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
435 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
436 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
437 EcmaRuntimeCallInfo *info =
438 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
439 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
440 info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
441 callResult = JSFunction::Call(info);
442 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
443 }
444 if (!callResult.ToBoolean()) {
445 return base::BuiltinsBase::GetTaggedBoolean(false);
446 }
447 k++;
448 if (!thisObjVal->IsStableJSArray(thread)) {
449 return base::BuiltinsBase::GetTaggedBoolean(true);
450 }
451 }
452 return base::BuiltinsBase::GetTaggedBoolean(true);
453 }
454
HandleforEachOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t len,uint32_t & k)455 JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
456 JSHandle<JSTaggedValue> callbackFnHandle,
457 JSHandle<JSTaggedValue> thisArgHandle, uint32_t len, uint32_t &k)
458 {
459 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
460 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
461 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
462 const int32_t argsLength = 3; // 3: ?kValue, k, O?
463 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
464 if (ElementAccessor::GetElementsLength(thisObjHandle) <= k) {
465 return base::BuiltinsBase::GetTaggedBoolean(false);
466 }
467 while (k < len) {
468 // Elements of thisObjHandle may change.
469 kValue.Update(ElementAccessor::Get(thisObjHandle, k));
470 if (!kValue.GetTaggedValue().IsHole()) {
471 key.Update(JSTaggedValue(k));
472 EcmaRuntimeCallInfo *info =
473 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
474 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
475 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
476 JSTaggedValue funcResult = JSFunction::Call(info);
477 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
478 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
479 len = ElementAccessor::GetElementsLength(thisObjHandle);
480 }
481 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
482 key.Update(JSTaggedValue(k));
483 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
484 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
485 EcmaRuntimeCallInfo *info =
486 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
487 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
488 info->SetCallArg(kValue1.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
489 JSTaggedValue funcResult = JSFunction::Call(info);
490 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
491 }
492 k++;
493 if (!thisObjVal->IsStableJSArray(thread)) {
494 break;
495 }
496 }
497 return base::BuiltinsBase::GetTaggedBoolean(true);
498 }
499
500 template <class Predicate>
FindRawData(IndexOfContext & ctx,Predicate && predicate)501 JSTaggedValue JSStableArray::FindRawData(IndexOfContext &ctx, Predicate &&predicate)
502 {
503 DISALLOW_GARBAGE_COLLECTION;
504 JSTaggedType *data = nullptr;
505 JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
506 ElementsKind kind = ElementsKind::GENERIC;
507 if (elementsValue.IsMutantTaggedArray()) {
508 JSHandle<MutantTaggedArray> elements(ctx.thread, elementsValue);
509 data = elements->GetData();
510 kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
511 } else {
512 JSHandle<TaggedArray> elements(ctx.thread, elementsValue);
513 // Note: GC is guaranteed not to happen since no new object is created during the searching process.
514 data = elements->GetData();
515 // Note: for stable arrays, elements->GetLength() returns the CAPACITY, instead of actual length!
516 }
517 JSTaggedType *first = data + ctx.fromIndex;
518 JSTaggedType *last = data + ctx.length;
519
520 JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
521 for (JSTaggedType *cur = first; cur < last; ++cur) {
522 JSTaggedValue convertedCur = JSTaggedValue(*cur);
523 if (elementsValue.IsMutantTaggedArray()) {
524 convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
525 }
526 if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
527 if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
528 return base::BuiltinsBase::GetTaggedInt64(cur - data);
529 }
530 continue;
531 }
532 // Fallback slow path
533 indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
534 bool found = base::ArrayHelper::ElementIsStrictEqualTo(
535 ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
536 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
537 if (found) {
538 return indexHandle.GetTaggedValue();
539 }
540 }
541 return JSTaggedValue(-1); // Not found
542 }
543
544 template <class Predicate>
FindLastRawData(IndexOfContext & ctx,Predicate && predicate)545 JSTaggedValue JSStableArray::FindLastRawData(IndexOfContext &ctx, Predicate &&predicate)
546 {
547 DISALLOW_GARBAGE_COLLECTION;
548 JSTaggedType *data = nullptr;
549 JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
550 ElementsKind kind = ElementsKind::GENERIC;
551 if (elementsValue.IsMutantTaggedArray()) {
552 JSHandle<MutantTaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
553 data = elements->GetData();
554 kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
555 } else {
556 JSHandle<TaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
557 // Note: GC is guaranteed not to happen since no new object is created during the searching process.
558 data = elements->GetData();
559 }
560 JSTaggedType *beforeFirst = data - 1;
561 JSTaggedType *beforeLast = data + ctx.fromIndex;
562
563 JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
564 for (JSTaggedType *cur = beforeLast; cur > beforeFirst; --cur) {
565 JSTaggedValue convertedCur = JSTaggedValue(*cur);
566 if (elementsValue.IsMutantTaggedArray()) {
567 convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
568 }
569 if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
570 if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
571 return base::BuiltinsBase::GetTaggedInt64(cur - data);
572 }
573 continue;
574 }
575 // Fallback slow path
576 indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
577 bool found = base::ArrayHelper::ElementIsStrictEqualTo(
578 ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
579 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
580 if (found) {
581 return indexHandle.GetTaggedValue();
582 }
583 }
584 return JSTaggedValue(-1); // Not found
585 }
586
587 template <class Predicate>
FindRawDataDispatch(IndexOfType type,IndexOfContext & ctx,Predicate && predicate)588 JSTaggedValue JSStableArray::FindRawDataDispatch(IndexOfType type, IndexOfContext &ctx, Predicate &&predicate)
589 {
590 switch (type) {
591 case IndexOfType::IndexOf:
592 return FindRawData(ctx, std::forward<Predicate>(predicate));
593 case IndexOfType::LastIndexOf:
594 return FindLastRawData(ctx, std::forward<Predicate>(predicate));
595 default:
596 UNREACHABLE();
597 }
598 }
599
600 // Zeros need special judge
IndexOfZero(IndexOfType type,IndexOfContext & ctx)601 JSTaggedValue JSStableArray::IndexOfZero(IndexOfType type, IndexOfContext &ctx)
602 {
603 return FindRawDataDispatch(type, ctx, [](JSTaggedType cur) {
604 return JSTaggedValue(cur).IsExactlyZero();
605 });
606 }
607
IndexOfInt32(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)608 JSTaggedValue JSStableArray::IndexOfInt32(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
609 {
610 ASSERT(searchElement.IsInt());
611 int32_t untagged = searchElement.GetInt();
612 if (untagged == 0) {
613 return IndexOfZero(type, ctx);
614 }
615 JSTaggedType targetInt32 = searchElement.GetRawData();
616 JSTaggedType targetDouble = JSTaggedValue(static_cast<double>(untagged)).GetRawData();
617 return FindRawDataDispatch(type, ctx, [targetInt32, targetDouble](JSTaggedType cur) {
618 return cur == targetInt32 || cur == targetDouble;
619 });
620 }
621
IndexOfDouble(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)622 JSTaggedValue JSStableArray::IndexOfDouble(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
623 {
624 ASSERT(searchElement.IsDouble());
625 double untagged = searchElement.GetDouble();
626 if (std::isnan(untagged)) {
627 return JSTaggedValue(-1);
628 }
629 if (untagged == 0.0) {
630 return IndexOfZero(type, ctx);
631 }
632 JSTaggedType targetDouble = searchElement.GetRawData();
633 if (searchElement.WithinInt32()) {
634 JSTaggedType targetInt32 = JSTaggedValue(static_cast<int32_t>(untagged)).GetRawData();
635 return FindRawDataDispatch(type, ctx, [targetDouble, targetInt32](JSTaggedType cur) {
636 return cur == targetDouble || cur == targetInt32;
637 });
638 } else {
639 return FindRawDataDispatch(type, ctx, [targetDouble](JSTaggedType cur) {
640 return cur == targetDouble;
641 });
642 }
643 }
644
IndexOfObjectAddress(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)645 JSTaggedValue JSStableArray::IndexOfObjectAddress(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
646 {
647 ASSERT(searchElement.IsObject());
648 JSTaggedType targetAddress = searchElement.GetRawData();
649 return FindRawDataDispatch(type, ctx, [targetAddress](JSTaggedType cur) {
650 return cur == targetAddress;
651 });
652 }
653
IndexOfString(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)654 JSTaggedValue JSStableArray::IndexOfString(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
655 {
656 ASSERT(searchElement.IsString());
657 JSTaggedType targetAddress = searchElement.GetRawData();
658 return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
659 if (targetAddress == cur) {
660 return true;
661 }
662 JSTaggedValue curValue(cur);
663 if (!curValue.IsString()) {
664 return false;
665 }
666 return JSTaggedValue::StringCompare(
667 EcmaString::Cast(curValue.GetTaggedObject()),
668 EcmaString::Cast(searchElement.GetTaggedObject()));
669 });
670 }
671
IndexOfBigInt(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)672 JSTaggedValue JSStableArray::IndexOfBigInt(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
673 {
674 ASSERT(searchElement.IsBigInt());
675 JSTaggedType targetAddress = searchElement.GetRawData();
676 return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
677 if (cur == targetAddress) {
678 return true;
679 }
680 JSTaggedValue curValue(cur);
681 if (!curValue.IsBigInt()) {
682 return false;
683 }
684 return BigInt::Equal(curValue, searchElement);
685 });
686 }
687
IndexOfDispatch(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)688 JSTaggedValue JSStableArray::IndexOfDispatch(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
689 {
690 if (searchElement.IsInt()) {
691 return IndexOfInt32(type, ctx, searchElement);
692 } else if (searchElement.IsDouble()) {
693 return IndexOfDouble(type, ctx, searchElement);
694 } else if (searchElement.IsString()) {
695 return IndexOfString(type, ctx, searchElement);
696 } else if (searchElement.IsBigInt()) {
697 return IndexOfBigInt(type, ctx, searchElement);
698 } else {
699 return IndexOfObjectAddress(type, ctx, searchElement);
700 }
701 }
702
IndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)703 JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
704 JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
705 {
706 IndexOfContext ctx;
707 ctx.thread = thread;
708 ctx.receiver = receiver;
709 ctx.searchElement = searchElement;
710 ctx.fromIndex = from;
711 ctx.length = len;
712 return IndexOfDispatch(IndexOfType::IndexOf, ctx, searchElement.GetTaggedValue());
713 }
714
LastIndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)715 JSTaggedValue JSStableArray::LastIndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
716 JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
717 {
718 IndexOfContext ctx;
719 ctx.thread = thread;
720 ctx.receiver = receiver;
721 ctx.searchElement = searchElement;
722 ctx.fromIndex = from;
723 ctx.length = len;
724 return IndexOfDispatch(IndexOfType::LastIndexOf, ctx, searchElement.GetTaggedValue());
725 }
726
Filter(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t & toIndex)727 JSTaggedValue JSStableArray::Filter(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
728 EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex)
729 {
730 JSThread *thread = argv->GetThread();
731 JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
732 JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
733 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
734 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
735 JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
736 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
737 const int32_t argsLength = 3; // 3: ?kValue, k, O?
738 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
739 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
740 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
741 while (k < len) {
742 // Elements of thisObjHandle may change.
743 JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
744 if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
745 value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
746 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
747 }
748 kValue.Update(value);
749 if (!kValue.GetTaggedValue().IsHole()) {
750 key.Update(JSTaggedValue(k));
751 EcmaRuntimeCallInfo *info =
752 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
753 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
754 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
755 JSTaggedValue callResult = JSFunction::Call(info);
756 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
757 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
758 len = ElementAccessor::GetElementsLength(thisObjHandle);
759 }
760 bool boolResult = callResult.ToBoolean();
761 if (boolResult) {
762 toIndexHandle.Update(JSTaggedValue(toIndex));
763 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
764 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
765 toIndex++;
766 }
767 }
768 k++;
769 if (!thisObjVal->IsStableJSArray(thread)) {
770 break;
771 }
772 }
773 return base::BuiltinsBase::GetTaggedDouble(true);
774 }
775
Map(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t len)776 JSTaggedValue JSStableArray::Map(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
777 EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len)
778 {
779 JSThread *thread = argv->GetThread();
780 JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
781 JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
782 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
783 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
784 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
785 JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
786 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
787 const int32_t argsLength = 3; // 3: ?kValue, k, O?
788 while (k < len) {
789 // Elements of thisObjHandle may change.
790 JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
791 if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
792 value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
793 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
794 }
795 kValue.Update(value);
796 if (!kValue.GetTaggedValue().IsHole()) {
797 key.Update(JSTaggedValue(k));
798 EcmaRuntimeCallInfo *info =
799 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
800 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
801 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
802 JSTaggedValue mapResult = JSFunction::Call(info);
803 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
804 mapResultHandle.Update(mapResult);
805 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
806 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
807 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
808 len = ElementAccessor::GetElementsLength(thisObjHandle);
809 }
810 }
811 k++;
812 if (!thisObjVal->IsStableJSArray(thread)) {
813 break;
814 }
815 }
816 return base::BuiltinsBase::GetTaggedDouble(true);
817 }
818
Reverse(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & lower,uint32_t len)819 JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle<JSObject> thisObjHandle,
820 int64_t &lower, uint32_t len)
821 {
822 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
823 if (thisObjHandle->IsJSArray()) {
824 JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObjHandle));
825 }
826 JSMutableHandle<JSTaggedValue> lowerP(thread, JSTaggedValue::Undefined());
827 JSMutableHandle<JSTaggedValue> upperP(thread, JSTaggedValue::Undefined());
828 JSMutableHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
829 JSMutableHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
830 int64_t middle = std::floor(len / 2);
831 bool needTransition = true;
832 while (lower != middle) {
833 if (ElementAccessor::GetElementsLength(thisObjHandle) != len) {
834 break;
835 }
836 int64_t upper = static_cast<int64_t>(len) - lower - 1;
837 lowerP.Update(JSTaggedValue(lower));
838 upperP.Update(JSTaggedValue(upper));
839 lowerValueHandle.Update(ElementAccessor::Get(thisObjHandle, lower));
840 upperValueHandle.Update(ElementAccessor::Get(thisObjHandle, upper));
841 ElementAccessor::Set(thread, thisObjHandle, lower, upperValueHandle.GetTaggedValue(), needTransition);
842 ElementAccessor::Set(thread, thisObjHandle, upper, lowerValueHandle.GetTaggedValue(), needTransition);
843 lower++;
844 }
845 return base::BuiltinsBase::GetTaggedDouble(true);
846 }
847
Concat(JSThread * thread,JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & n)848 JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle<JSObject> newArrayHandle,
849 JSHandle<JSObject> thisObjHandle, int64_t &k, int64_t &n)
850 {
851 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
852 int64_t thisLen = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
853 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
854 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
855 while (k < thisLen) {
856 if (ElementAccessor::GetElementsLength(thisObjHandle) != thisLen) {
857 break;
858 }
859 toKey.Update(JSTaggedValue(n));
860 JSTaggedValue kValue = ElementAccessor::Get(thisObjHandle, k);
861 if (!kValue.IsHole()) {
862 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, JSHandle<JSTaggedValue>(thread, kValue));
863 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
864 }
865 n++;
866 k++;
867 }
868 return base::BuiltinsBase::GetTaggedDouble(true);
869 }
870
FastCopyFromArrayToTypedArray(JSThread * thread,JSHandle<JSTypedArray> & targetArray,DataViewType targetType,uint64_t targetOffset,uint32_t srcLength,JSHandle<JSObject> & obj)871 JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle<JSTypedArray> &targetArray,
872 DataViewType targetType, uint64_t targetOffset,
873 uint32_t srcLength, JSHandle<JSObject> &obj)
874 {
875 JSHandle<JSTaggedValue> targetBuffer(thread, targetArray->GetViewedArrayBufferOrByteArray());
876 // If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
877 if (BuiltinsArrayBuffer::IsDetachedBuffer(targetBuffer.GetTaggedValue())) {
878 THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.",
879 JSTaggedValue::Exception());
880 }
881 uint32_t targetLength = targetArray->GetArrayLength();
882 uint32_t targetByteOffset = targetArray->GetByteOffset();
883 uint32_t targetElementSize = TypedArrayHelper::GetSizeFromType(targetType);
884 if (srcLength + targetOffset > targetLength) {
885 THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of length and targetOffset is greater than targetLength.",
886 JSTaggedValue::Exception());
887 }
888 uint32_t targetByteIndex = static_cast<uint32_t>(targetOffset * targetElementSize + targetByteOffset);
889 ContentType contentType = targetArray->GetContentType();
890 uint32_t elemLen = ElementAccessor::GetElementsLength(obj);
891 if (contentType == ContentType::BigInt) {
892 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
893 JSMutableHandle<JSTaggedValue> elem(thread, JSTaggedValue::Hole());
894 for (uint32_t i = 0; i < srcLength; i++) {
895 if (i < elemLen) {
896 elem.Update(ElementAccessor::Get(obj, i));
897 } else {
898 elem.Update(JSTaggedValue::Hole());
899 }
900 kValue.Update(JSTaggedValue::ToBigInt(thread, elem));
901 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
902 BuiltinsArrayBuffer::SetValueInBuffer(thread, targetBuffer.GetTaggedValue(), targetByteIndex,
903 targetType, kValue, true);
904 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
905 targetByteIndex += targetElementSize;
906 }
907 } else {
908 double val = 0.0;
909 uint32_t copyLen = srcLength > elemLen ? elemLen : srcLength;
910 for (uint32_t i = 0; i < copyLen; i++) {
911 JSTaggedValue taggedVal = ElementAccessor::Get(obj, i);
912 if (!taggedVal.IsNumber()) {
913 JSTaggedNumber taggedNumber = JSTaggedValue::ToNumber(thread, taggedVal);
914 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
915 val = taggedNumber.GetNumber();
916 } else {
917 val = taggedVal.GetNumber();
918 }
919 BuiltinsArrayBuffer::FastSetValueInBuffer(thread, targetBuffer.GetTaggedValue(), targetByteIndex,
920 targetType, val, true);
921 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
922 targetByteIndex += targetElementSize;
923 }
924
925 for (uint32_t i = copyLen; i < srcLength; i++) {
926 val = JSTaggedNumber(base::NAN_VALUE).GetNumber();
927 BuiltinsArrayBuffer::FastSetValueInBuffer(thread, targetBuffer.GetTaggedValue(), targetByteIndex,
928 targetType, val, true);
929 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
930 targetByteIndex += targetElementSize;
931 }
932 }
933 return JSTaggedValue::Undefined();
934 }
935
At(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)936 JSTaggedValue JSStableArray::At(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
937 {
938 JSThread *thread = argv->GetThread();
939 uint32_t thisLen = receiver->GetArrayLength();
940 if (thisLen == 0) {
941 return JSTaggedValue::Undefined();
942 }
943 JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
944 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
945 int64_t relativeIndex = index.GetNumber();
946 int64_t k = 0;
947 if (relativeIndex >= 0) {
948 k = relativeIndex;
949 } else {
950 k = static_cast<int64_t>(thisLen) + relativeIndex;
951 }
952 if (k < 0 || k >= thisLen) {
953 return JSTaggedValue::Undefined();
954 }
955
956 auto result = JSTaggedValue::Hole();
957 result = ElementAccessor::Get(JSHandle<JSObject>::Cast(receiver), k);
958 return result.IsHole() ? JSTaggedValue::Undefined() : result;
959 }
960
With(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount,int64_t index,JSHandle<JSTaggedValue> value)961 JSTaggedValue JSStableArray::With(JSThread *thread, JSHandle<JSArray> receiver,
962 int64_t insertCount, int64_t index, JSHandle<JSTaggedValue> value)
963 {
964 JSHandle<JSObject> thisObjHandle(receiver);
965 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle,
966 JSTaggedNumber(static_cast<uint32_t>(insertCount)));
967 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
968 JSHandle<JSObject> newArrayHandle(thread, newArray);
969
970 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
971 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
972
973 if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
974 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
975 }
976
977 bool needTransition = true;
978 for (uint32_t idx = 0; idx < insertCount; idx++) {
979 if (idx == index) {
980 ElementAccessor::Set(thread, newArrayHandle, idx, value.GetTaggedValue(), needTransition);
981 } else {
982 auto kValue = ElementAccessor::Get(thisObjHandle, idx);
983 if (kValue.IsHole()) {
984 ElementAccessor::Set(thread, newArrayHandle, idx, JSTaggedValue::Undefined(), needTransition);
985 } else {
986 ElementAccessor::Set(thread, newArrayHandle, idx, kValue, needTransition);
987 }
988 }
989 }
990 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
991 return newArrayHandle.GetTaggedValue();
992 }
993
ToSpliced(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,int64_t argc,int64_t actualStart,int64_t actualSkipCount,int64_t insertCount)994 JSTaggedValue JSStableArray::ToSpliced(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
995 int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount)
996 {
997 JSThread *thread = argv->GetThread();
998
999 JSHandle<JSObject> thisObjHandle(receiver);
1000 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle,
1001 JSTaggedNumber(static_cast<uint32_t>(insertCount)));
1002 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1003 JSHandle<JSObject> newArrayHandle(thread, newArray);
1004
1005 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1006 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1007
1008 if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1009 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1010 }
1011
1012 int64_t i = 0;
1013 int64_t r = actualStart + actualSkipCount;
1014 bool needTransition = true;
1015 for (int64_t idx = 0; idx < actualStart; idx++, i++) {
1016 auto kValue = ElementAccessor::Get(thisObjHandle, idx);
1017 if (kValue.IsHole()) {
1018 ElementAccessor::Set(thread, newArrayHandle, i, JSTaggedValue::Undefined(), needTransition);
1019 } else {
1020 ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1021 }
1022 }
1023
1024 for (uint32_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items.
1025 auto element = base::BuiltinsBase::GetCallArg(argv, pos);
1026 ElementAccessor::Set(thread, newArrayHandle, i, element.GetTaggedValue(), needTransition);
1027 ++i;
1028 }
1029
1030 while (i < insertCount) {
1031 auto kValue = ElementAccessor::Get(thisObjHandle, r);
1032 if (kValue.IsHole()) {
1033 ElementAccessor::Set(thread, newArrayHandle, i, JSTaggedValue::Undefined(), needTransition);
1034 } else {
1035 ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1036 }
1037 ++i;
1038 ++r;
1039 }
1040
1041 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1042
1043 return newArrayHandle.GetTaggedValue();
1044 }
1045
ToReversed(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount)1046 JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle<JSArray> receiver,
1047 int64_t insertCount)
1048 {
1049 JSHandle<JSObject> thisObjHandle(receiver);
1050 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle,
1051 JSTaggedNumber(static_cast<uint32_t>(insertCount)));
1052 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1053 JSHandle<JSObject> newArrayHandle(thread, newArray);
1054
1055 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1056 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1057
1058 if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1059 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1060 }
1061
1062 bool needTransition = true;
1063 for (uint32_t idx = 0; idx < insertCount; idx++) {
1064 auto kValue = ElementAccessor::Get(thisObjHandle, idx);
1065 if (kValue.IsHole()) {
1066 ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1,
1067 JSTaggedValue::Undefined(), needTransition);
1068 } else {
1069 ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1, kValue, needTransition);
1070 }
1071 }
1072 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1073
1074 return newArrayHandle.GetTaggedValue();
1075 }
1076
Reduce(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> accumulator,int64_t & k,int64_t & len)1077 JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1078 JSHandle<JSTaggedValue> callbackFnHandle,
1079 JSMutableHandle<JSTaggedValue> accumulator, int64_t &k, int64_t &len)
1080 {
1081 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1082 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1083 JSTaggedValue callResult = JSTaggedValue::Undefined();
1084 while (k < len) {
1085 JSTaggedValue kValue(ElementAccessor::Get(thisObjHandle, k));
1086 if (!kValue.IsHole()) {
1087 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1088 const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1089 EcmaRuntimeCallInfo *info =
1090 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength);
1091 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1092 info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k),
1093 thisObjVal.GetTaggedValue());
1094 callResult = JSFunction::Call(info);
1095 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1096 if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1097 len = ElementAccessor::GetElementsLength(thisObjHandle);
1098 }
1099 accumulator.Update(callResult);
1100 }
1101 k++;
1102 if (!thisObjVal->IsStableJSArray(thread)) {
1103 break;
1104 }
1105 }
1106 return base::BuiltinsBase::GetTaggedDouble(true);
1107 }
1108
Slice(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & count)1109 JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1110 int64_t &k, int64_t &count)
1111 {
1112 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1113 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1114 int64_t len = static_cast<int64_t>(ElementAccessor::GetElementsLength(thisObjHandle));
1115 int64_t oldLen;
1116 if (len > k + count) {
1117 oldLen = count;
1118 } else {
1119 oldLen = len - k;
1120 }
1121 JSHandle<JSObject> arrayObj = factory->NewAndCopyJSArrayObject(thisObjHandle, count, oldLen, k);
1122 for (int i = 0; i < count; i++) {
1123 JSTaggedValue value = ElementAccessor::Get(arrayObj, i);
1124 if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i + k)) {
1125 value = JSArray::FastGetPropertyByValue(thread, thisObjVal, i + k).GetTaggedValue();
1126 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1127 ElementAccessor::Set(thread, arrayObj, i, value, true);
1128 }
1129 }
1130 return arrayObj.GetTaggedValue();
1131 }
1132
Sort(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & callbackFnHandle)1133 JSTaggedValue JSStableArray::Sort(JSThread *thread, const JSHandle<JSObject> &thisObj,
1134 const JSHandle<JSTaggedValue> &callbackFnHandle)
1135 {
1136 JSArray::SortElementsByObject(thread, thisObj, callbackFnHandle);
1137 return thisObj.GetTaggedValue();
1138 }
1139
Fill(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & value,int64_t start,int64_t end,int64_t len)1140 JSTaggedValue JSStableArray::Fill(JSThread *thread, const JSHandle<JSObject> &thisObj,
1141 const JSHandle<JSTaggedValue> &value, int64_t start,
1142 int64_t end, int64_t len)
1143 {
1144 JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObj));
1145 uint32_t length = ElementAccessor::GetElementsLength(thisObj);
1146 ElementsKind oldKind = thisObj->GetClass()->GetElementsKind();
1147 if (JSHClass::TransitToElementsKind(thread, thisObj, value)) {
1148 ElementsKind newKind = thisObj->GetClass()->GetElementsKind();
1149 Elements::MigrateArrayWithKind(thread, thisObj, oldKind, newKind);
1150 }
1151 if (length >= end) {
1152 if (thisObj->GetElements().IsMutantTaggedArray()) {
1153 ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1154 TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1155 JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1156 value.GetTaggedValue(), kind));
1157 for (int64_t idx = start; idx < end; idx++) {
1158 elements->Set<false>(thread, idx, migratedValue);
1159 }
1160 } else {
1161 TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1162 for (int64_t idx = start; idx < end; idx++) {
1163 elements->Set(thread, idx, value);
1164 }
1165 }
1166 return thisObj.GetTaggedValue();
1167 } else {
1168 if (thisObj->GetElements().IsMutantTaggedArray()) {
1169 ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1170 JSHandle<MutantTaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewMutantTaggedArray(len);
1171 JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1172 value.GetTaggedValue(), kind));
1173 for (int64_t idx = start; idx < end; idx++) {
1174 newElements->Set<false>(thread, idx, migratedValue);
1175 }
1176 thisObj->SetElements(thread, newElements);
1177 } else {
1178 JSHandle<TaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len);
1179 for (int64_t idx = start; idx < end; idx++) {
1180 newElements->Set(thread, idx, value);
1181 }
1182 thisObj->SetElements(thread, newElements);
1183 }
1184 return thisObj.GetTaggedValue();
1185 }
1186 }
1187 } // namespace panda::ecmascript
1188