• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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