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_vm.h"
23 #include "ecmascript/global_env.h"
24 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
25 #include "ecmascript/js_array.h"
26 #include "ecmascript/js_tagged_value-inl.h"
27 #include "ecmascript/object_factory.h"
28 #include "ecmascript/tagged_array.h"
29
30 namespace panda::ecmascript {
31 using TypedArrayHelper = base::TypedArrayHelper;
32 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
33
Push(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)34 JSTaggedValue JSStableArray::Push(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
35 {
36 JSThread *thread = argv->GetThread();
37 uint32_t argc = argv->GetArgsNumber();
38 uint32_t oldLength = receiver->GetArrayLength();
39 uint32_t newLength = argc + oldLength;
40
41 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
42 if (newLength > elements->GetLength()) {
43 elements = *JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength);
44 }
45
46 for (uint32_t k = 0; k < argc; k++) {
47 JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
48 elements->Set(thread, oldLength + k, value.GetTaggedValue());
49 }
50 receiver->SetArrayLength(thread, newLength);
51
52 return JSTaggedValue(newLength);
53 }
54
Pop(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)55 JSTaggedValue JSStableArray::Pop(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
56 {
57 JSThread *thread = argv->GetThread();
58 uint32_t length = receiver->GetArrayLength();
59 if (length == 0) {
60 return JSTaggedValue::Undefined();
61 }
62
63 JSArray::CheckAndCopyArray(thread, receiver);
64 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
65 uint32_t capacity = elements->GetLength();
66 uint32_t index = length - 1;
67 auto result = elements->Get(index);
68 if (TaggedArray::ShouldTrim(capacity, index)) {
69 elements->Trim(thread, index);
70 } else {
71 elements->Set(thread, index, JSTaggedValue::Hole());
72 }
73 receiver->SetArrayLength(thread, index);
74 return result;
75 }
76
Splice(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount)77 JSTaggedValue JSStableArray::Splice(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
78 uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount)
79 {
80 JSThread *thread = argv->GetThread();
81 uint32_t len = receiver->GetArrayLength();
82 uint32_t argc = argv->GetArgsNumber();
83
84 JSHandle<JSObject> thisObjHandle(receiver);
85 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(actualDeleteCount));
86 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
87 JSHandle<JSObject> newArrayHandle(thread, newArray);
88
89 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
90 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
91 TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject());
92 JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
93 if (newArray.IsStableJSArray(thread)) {
94 TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
95 if (actualDeleteCount > destElements->GetLength()) {
96 destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
97 }
98
99 for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
100 if ((start + idx) >= srcElementsHandle->GetLength()) {
101 destElements->Set(thread, idx, JSTaggedValue::Hole());
102 } else {
103 destElements->Set(thread, idx, srcElementsHandle->Get(start + idx));
104 }
105 }
106 JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
107 } else {
108 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
109 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
110 uint32_t k = 0;
111 while (k < actualDeleteCount) {
112 uint32_t from = start + k;
113 fromKey.Update(JSTaggedValue(from));
114 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
115 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
116 if (exists) {
117 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
118 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
119 toKey.Update(JSTaggedValue(k));
120 if (newArrayHandle->IsJSProxy()) {
121 toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
122 }
123 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
124 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
125 }
126 k++;
127 }
128
129 JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
130 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
131 true);
132 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
133 }
134 uint32_t oldCapacity = srcElementsHandle->GetLength();
135 uint32_t newCapacity = len - actualDeleteCount + insertCount;
136 if (newCapacity > oldCapacity) {
137 srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
138 }
139 if (insertCount < actualDeleteCount) {
140 JSArray::CheckAndCopyArray(thread, receiver);
141 srcElementsHandle.Update(receiver->GetElements());
142 for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
143 auto element = JSTaggedValue::Hole();
144 if ((idx + actualDeleteCount) < srcElementsHandle->GetLength()) {
145 element = srcElementsHandle->Get(idx + actualDeleteCount);
146 }
147 element = element.IsHole() ? JSTaggedValue::Undefined() : element;
148 if ((idx + insertCount) < srcElementsHandle->GetLength()) {
149 srcElementsHandle->Set(thread, idx + insertCount, element);
150 }
151 }
152
153 if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
154 srcElementsHandle->Trim(thread, newCapacity);
155 } else {
156 for (uint32_t idx = newCapacity; idx < len; idx++) {
157 if (idx < srcElementsHandle->GetLength()) {
158 srcElementsHandle->Set(thread, idx, JSTaggedValue::Hole());
159 }
160 }
161 }
162 } else {
163 for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
164 auto element = srcElementsHandle->Get(idx + actualDeleteCount - 1);
165 element = element.IsHole() ? JSTaggedValue::Undefined() : element;
166 srcElementsHandle->Set(thread, idx + insertCount - 1, element);
167 }
168 }
169
170 for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
171 srcElementsHandle->Set(thread, idx, argv->GetCallArg(i));
172 }
173
174 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
175 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
176 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
177 return newArrayHandle.GetTaggedValue();
178 }
179
Shift(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)180 JSTaggedValue JSStableArray::Shift(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
181 {
182 JSThread *thread = argv->GetThread();
183 uint32_t length = receiver->GetArrayLength();
184 if (length == 0) {
185 return JSTaggedValue::Undefined();
186 }
187 JSArray::CheckAndCopyArray(thread, receiver);
188 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
189 auto result = elements->Get(0);
190 for (uint32_t k = 1; k < length; k++) {
191 auto kValue = elements->Get(k);
192 if (kValue.IsHole()) {
193 elements->Set(thread, k - 1, JSTaggedValue::Undefined());
194 } else {
195 elements->Set(thread, k - 1, kValue);
196 }
197 }
198 uint32_t capacity = elements->GetLength();
199 uint32_t index = length - 1;
200 if (TaggedArray::ShouldTrim(capacity, index)) {
201 elements->Trim(thread, index);
202 } else {
203 elements->Set(thread, index, JSTaggedValue::Hole());
204 }
205 receiver->SetArrayLength(thread, index);
206 return result;
207 }
208
Join(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)209 JSTaggedValue JSStableArray::Join(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
210 {
211 JSThread *thread = argv->GetThread();
212 uint32_t length = receiver->GetArrayLength();
213 JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
214 int sep = ',';
215 uint32_t sepLength = 1;
216 JSHandle<EcmaString> sepStringHandle;
217 if (!sepHandle->IsUndefined()) {
218 if (sepHandle->IsString()) {
219 sepStringHandle = JSHandle<EcmaString>::Cast(sepHandle);
220 } else {
221 sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
222 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
223 }
224 if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) {
225 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
226 sep = EcmaStringAccessor(sepStringHandle).Get(0);
227 } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) {
228 sep = JSStableArray::SeparatorFlag::MINUS_TWO;
229 sepLength = 0;
230 } else {
231 sep = JSStableArray::SeparatorFlag::MINUS_ONE;
232 sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
233 }
234 }
235 if (length == 0) {
236 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
237 return globalConst->GetEmptyString();
238 }
239 TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
240 size_t allocateLength = 0;
241 bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
242 CVector<JSHandle<EcmaString>> vec;
243 JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
244 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
245 uint32_t elementsLength = elements->GetLength();
246 uint32_t len = elementsLength > length ? length : elementsLength;
247 if (elementsLength == 0 && length != 0) {
248 len = length;
249 }
250 for (uint32_t k = 0; k < len; k++) {
251 JSTaggedValue element = JSTaggedValue::Undefined();
252 if (k < elementsLength) {
253 element = elements->Get(k);
254 }
255 if (!element.IsUndefinedOrNull() && !element.IsHole()) {
256 if (!element.IsString()) {
257 elementHandle.Update(element);
258 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
259 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
260 element = strElement.GetTaggedValue();
261 elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
262 }
263 auto nextStr = EcmaString::Cast(element.GetTaggedObject());
264 JSHandle<EcmaString> nextStrHandle(thread, nextStr);
265 vec.push_back(nextStrHandle);
266 isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
267 allocateLength += EcmaStringAccessor(nextStr).GetLength();
268 } else {
269 vec.push_back(JSHandle<EcmaString>(globalConst->GetHandledEmptyString()));
270 }
271 }
272 if (len > 0) {
273 allocateLength += sepLength * (len - 1);
274 }
275 auto newString = EcmaStringAccessor::AllocStringObject(thread->GetEcmaVM(), allocateLength, isOneByte);
276 int current = 0;
277 DISALLOW_GARBAGE_COLLECTION;
278 for (uint32_t k = 0; k < len; k++) {
279 if (k > 0) {
280 if (sep >= 0) {
281 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
282 } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
283 EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
284 allocateLength - static_cast<uint32_t>(current), sepLength);
285 }
286 current += static_cast<int>(sepLength);
287 }
288 JSHandle<EcmaString> nextStr = vec[k];
289 int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
290 EcmaStringAccessor::ReadData(newString, *nextStr, current,
291 allocateLength - static_cast<uint32_t>(current), nextLength);
292 current += nextLength;
293 }
294 ASSERT_PRINT(
295 isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
296 return JSTaggedValue(newString);
297 }
298
HandleFindIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)299 JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
300 JSHandle<JSTaggedValue> callbackFnHandle,
301 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
302 {
303 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
304 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
305 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
306 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
307 const int32_t argsLength = 3; // 3: ?kValue, k, O?
308 JSMutableHandle<TaggedArray> array(thread, thisObjHandle->GetElements());
309 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
310 while (k < len) {
311 // Elements of thisObjHandle may change.
312 array.Update(thisObjHandle->GetElements());
313 kValue.Update(array->Get(k));
314 EcmaRuntimeCallInfo *info =
315 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
316 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
317 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
318 callResult = JSFunction::Call(info);
319 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
320 if (callResult.ToBoolean()) {
321 return callResult;
322 }
323 if (array->GetLength() < len) {
324 len = array->GetLength();
325 }
326 if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) > static_cast<int64_t>(len)) {
327 array.Update(thisObjHandle->GetElements());
328 }
329 k++;
330 if (!thisObjVal->IsStableJSArray(thread)) {
331 return callResult;
332 }
333 }
334 return callResult;
335 }
336
HandleEveryOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)337 JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
338 JSHandle<JSTaggedValue> callbackFnHandle,
339 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
340 {
341 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
342 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
343 JSMutableHandle<TaggedArray> array(thread, thisObjHandle->GetElements());
344 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
345 const int32_t argsLength = 3; // 3: ?kValue, k, O?
346 JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(true);
347 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
348 while (k < len) {
349 // Elements of thisObjHandle may change.
350 array.Update(thisObjHandle->GetElements());
351 kValue.Update(array->Get(k));
352 if (!kValue.GetTaggedValue().IsHole()) {
353 EcmaRuntimeCallInfo *info =
354 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
355 info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
356 callResult = JSFunction::Call(info);
357 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
358 if (array->GetLength() < len) {
359 len = array->GetLength();
360 }
361 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
362 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
363 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
364 EcmaRuntimeCallInfo *info =
365 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
366 info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
367 callResult = JSFunction::Call(info);
368 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
369 }
370 if (!callResult.ToBoolean()) {
371 return base::BuiltinsBase::GetTaggedBoolean(false);
372 }
373 k++;
374 if (!thisObjVal->IsStableJSArray(thread)) {
375 return base::BuiltinsBase::GetTaggedBoolean(true);
376 }
377 }
378 return base::BuiltinsBase::GetTaggedBoolean(true);
379 }
380
HandleforEachOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)381 JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
382 JSHandle<JSTaggedValue> callbackFnHandle,
383 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
384 {
385 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
386 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
387 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
388 JSMutableHandle<TaggedArray> array(thread, thisObjHandle->GetElements());
389 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
390 const int32_t argsLength = 3; // 3: ?kValue, k, O?
391 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
392 if (array->GetLength() <= k) {
393 return base::BuiltinsBase::GetTaggedBoolean(false);
394 }
395 while (k < len) {
396 // Elements of thisObjHandle may change.
397 array.Update(thisObjHandle->GetElements());
398 kValue.Update(array->Get(k));
399 if (!kValue.GetTaggedValue().IsHole()) {
400 key.Update(JSTaggedValue(k));
401 EcmaRuntimeCallInfo *info =
402 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
403 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
404 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
405 JSTaggedValue funcResult = JSFunction::Call(info);
406 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
407 if (array->GetLength() < len) {
408 len = array->GetLength();
409 }
410 } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
411 key.Update(JSTaggedValue(k));
412 JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
413 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
414 EcmaRuntimeCallInfo *info =
415 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
416 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
417 info->SetCallArg(kValue1.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
418 JSTaggedValue funcResult = JSFunction::Call(info);
419 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
420 }
421 k++;
422 if (!thisObjVal->IsStableJSArray(thread)) {
423 break;
424 }
425 }
426 return base::BuiltinsBase::GetTaggedBoolean(true);
427 }
428
IndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)429 JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
430 JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
431 {
432 JSHandle<TaggedArray> elements(thread, JSHandle<JSObject>::Cast(receiver)->GetElements());
433 while (from < len) {
434 JSTaggedValue value = elements->Get(from);
435 if (!value.IsUndefined() && !value.IsHole()) {
436 if (JSTaggedValue::StrictEqual(searchElement.GetTaggedValue(), value)) {
437 return JSTaggedValue(from);
438 }
439 } else {
440 bool exist = JSTaggedValue::HasProperty(thread, receiver, from);
441 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
442 if (exist) {
443 JSHandle<JSTaggedValue> kValueHandle = JSArray::FastGetPropertyByValue(thread, receiver, from);
444 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
445 if (JSTaggedValue::StrictEqual(thread, searchElement, kValueHandle)) {
446 return JSTaggedValue(from);
447 }
448 }
449 }
450 from++;
451 }
452 return JSTaggedValue(-1);
453 }
454
Filter(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t & toIndex)455 JSTaggedValue JSStableArray::Filter(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
456 EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex)
457 {
458 JSThread *thread = argv->GetThread();
459 JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
460 JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
461 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
462 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
463 JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
464 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
465 const int32_t argsLength = 3; // 3: ?kValue, k, O?
466 uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
467 JSMutableHandle<TaggedArray> array(thread, thisObjHandle->GetElements());
468 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
469 while (k < len) {
470 // Elements of thisObjHandle may change.
471 array.Update(thisObjHandle->GetElements());
472 kValue.Update(array->Get(k));
473 if (!kValue.GetTaggedValue().IsHole()) {
474 key.Update(JSTaggedValue(k));
475 EcmaRuntimeCallInfo *info =
476 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
477 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
478 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
479 JSTaggedValue callResult = JSFunction::Call(info);
480 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
481 if (array->GetLength() < len) {
482 len = array->GetLength();
483 }
484 bool boolResult = callResult.ToBoolean();
485 if (boolResult) {
486 toIndexHandle.Update(JSTaggedValue(toIndex));
487 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
488 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
489 toIndex++;
490 }
491 }
492 k++;
493 if (!thisObjVal->IsStableJSArray(thread)) {
494 break;
495 }
496 }
497 return base::BuiltinsBase::GetTaggedDouble(true);
498 }
499
Map(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t len)500 JSTaggedValue JSStableArray::Map(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
501 EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len)
502 {
503 JSThread *thread = argv->GetThread();
504 JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
505 JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
506 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
507 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
508 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
509 JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
510 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
511 const int32_t argsLength = 3; // 3: ?kValue, k, O?
512 JSMutableHandle<TaggedArray> array(thread, thisObjHandle->GetElements());
513 while (k < len) {
514 // Elements of thisObjHandle may change.
515 array.Update(thisObjHandle->GetElements());
516 kValue.Update(array->Get(k));
517 if (!kValue.GetTaggedValue().IsHole()) {
518 key.Update(JSTaggedValue(k));
519 EcmaRuntimeCallInfo *info =
520 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
521 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
522 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
523 JSTaggedValue mapResult = JSFunction::Call(info);
524 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
525 mapResultHandle.Update(mapResult);
526 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
527 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
528 if (array->GetLength() < len) {
529 len = array->GetLength();
530 }
531 }
532 k++;
533 if (!thisObjVal->IsStableJSArray(thread)) {
534 break;
535 }
536 }
537 return base::BuiltinsBase::GetTaggedDouble(true);
538 }
539
Reverse(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> thisHandle,int64_t & lower,uint32_t len)540 JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle<JSObject> thisObjHandle,
541 JSHandle<JSTaggedValue> thisHandle, int64_t &lower, uint32_t len)
542 {
543 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
544 if (thisObjHandle->IsJSArray()) {
545 JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObjHandle));
546 }
547 JSHandle<TaggedArray> array(thread, thisObjHandle->GetElements());
548 JSMutableHandle<JSTaggedValue> lowerP(thread, JSTaggedValue::Undefined());
549 JSMutableHandle<JSTaggedValue> upperP(thread, JSTaggedValue::Undefined());
550 JSMutableHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
551 JSMutableHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
552 int64_t middle = std::floor(len / 2);
553 while (lower != middle) {
554 if (array->GetLength() != len) {
555 break;
556 }
557 int64_t upper = static_cast<int64_t>(len) - lower - 1;
558 lowerP.Update(JSTaggedValue(lower));
559 upperP.Update(JSTaggedValue(upper));
560 bool lowerExists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, lowerP));
561 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
562 if (lowerExists) {
563 lowerValueHandle.Update(array->Get(lower));
564 }
565 bool upperExists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, upperP));
566 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
567 if (upperExists) {
568 upperValueHandle.Update(array->Get(upper));
569 }
570 if (lowerExists && upperExists) {
571 array->Set(thread, lower, upperValueHandle.GetTaggedValue());
572 array->Set(thread, upper, lowerValueHandle.GetTaggedValue());
573 } else if (upperExists) {
574 array->Set(thread, lower, upperValueHandle.GetTaggedValue());
575 JSTaggedValue::SetProperty(thread, thisObjVal, lowerP, upperValueHandle, true);
576 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, upperP);
577 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
578 } else if (lowerExists) {
579 array->Set(thread, upper, lowerValueHandle.GetTaggedValue());
580 JSTaggedValue::SetProperty(thread, thisObjVal, upperP, lowerValueHandle, true);
581 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, lowerP);
582 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
583 }
584 lower++;
585 }
586 return base::BuiltinsBase::GetTaggedDouble(true);
587 }
588
Concat(JSThread * thread,JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & n)589 JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle<JSObject> newArrayHandle,
590 JSHandle<JSObject> thisObjHandle, int64_t &k, int64_t &n)
591 {
592 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
593 int64_t thisLen = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
594 JSHandle<TaggedArray> arrayFrom(thread, thisObjHandle->GetElements());
595 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
596 while (k < thisLen) {
597 if (arrayFrom->GetLength() != thisLen) {
598 break;
599 }
600 toKey.Update(JSTaggedValue(n));
601 JSTaggedValue kValue = arrayFrom->Get(k);
602 if (!kValue.IsHole()) {
603 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, JSHandle<JSTaggedValue>(thread, kValue));
604 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
605 }
606 n++;
607 k++;
608 }
609 return base::BuiltinsBase::GetTaggedDouble(true);
610 }
611
FastCopyFromArrayToTypedArray(JSThread * thread,JSHandle<JSTypedArray> & targetArray,DataViewType targetType,uint32_t targetOffset,uint32_t srcLength,JSHandle<TaggedArray> & elements)612 JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle<JSTypedArray> &targetArray,
613 DataViewType targetType, uint32_t targetOffset,
614 uint32_t srcLength, JSHandle<TaggedArray> &elements)
615 {
616 JSHandle<JSTaggedValue> targetBuffer(thread, targetArray->GetViewedArrayBuffer());
617 // If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
618 if (BuiltinsArrayBuffer::IsDetachedBuffer(targetBuffer.GetTaggedValue())) {
619 THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.",
620 JSTaggedValue::Exception());
621 }
622 uint32_t targetLength = targetArray->GetArrayLength();
623 uint32_t targetByteOffset = targetArray->GetByteOffset();
624 uint32_t targetElementSize = TypedArrayHelper::GetSizeFromType(targetType);
625 if (srcLength + targetOffset > targetLength) {
626 THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of length and targetOffset is greater than targetLength.",
627 JSTaggedValue::Exception());
628 }
629 uint32_t targetByteIndex = static_cast<uint32_t>(targetOffset * targetElementSize + targetByteOffset);
630 JSMutableHandle<JSTaggedValue> elem(thread, JSTaggedValue::Hole());
631 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
632 ContentType contentType = targetArray->GetContentType();
633 for (uint32_t i = 0; i < srcLength; i++) {
634 elem.Update(elements->Get(i));
635 if (contentType == ContentType::BigInt) {
636 kValue.Update(JSTaggedValue::ToBigInt(thread, elem));
637 } else {
638 kValue.Update(JSTaggedValue::ToNumber(thread, elem));
639 }
640 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
641 BuiltinsArrayBuffer::SetValueInBuffer(thread, targetBuffer.GetTaggedValue(), targetByteIndex,
642 targetType, kValue, true);
643 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
644 targetByteIndex += targetElementSize;
645 }
646 return JSTaggedValue::Undefined();
647 }
648 } // namespace panda::ecmascript
649