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