• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-2025 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 <cstddef>
17 #include "cross_values.h"
18 #include "intrinsics.h"
19 #include "plugins/ets/runtime/ets_platform_types.h"
20 #include "plugins/ets/runtime/ets_stubs.h"
21 #include "plugins/ets/runtime/ets_stubs-inl.h"
22 #include "plugins/ets/runtime/types/ets_base_enum.h"
23 #include "plugins/ets/runtime/types/ets_escompat_array.h"
24 
25 namespace ark::ets::intrinsics {
26 
EtsEscompatArrayGet(ObjectHeader * arrayHeader,int32_t index)27 EtsObject *EtsEscompatArrayGet(ObjectHeader *arrayHeader, int32_t index)
28 {
29     ASSERT(arrayHeader != nullptr);
30     auto *array = EtsArrayObject<EtsObject>::FromEtsObject(EtsObject::FromCoreType(arrayHeader));
31     auto actualLength = array->GetActualLength();
32     if (UNLIKELY(static_cast<uint32_t>(index) >= actualLength)) {
33         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR,
34                           "Out of bounds");
35         return nullptr;
36     }
37     return array->GetData()->Get(index);
38 }
39 
EtsEscompatArraySet(ObjectHeader * arrayHeader,int32_t index,EtsObject * value)40 void EtsEscompatArraySet(ObjectHeader *arrayHeader, int32_t index, EtsObject *value)
41 {
42     ASSERT(arrayHeader != nullptr);
43     auto *array = EtsArrayObject<EtsObject>::FromEtsObject(EtsObject::FromCoreType(arrayHeader));
44     auto actualLength = array->GetActualLength();
45     if (UNLIKELY(static_cast<uint32_t>(index) >= actualLength)) {
46         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR,
47                           "Out of bounds");
48         return;
49     }
50     array->GetData()->Set(index, value);
51 }
52 
53 template <typename Func>
CreateTypeMap(const EtsPlatformTypes * ptypes)54 constexpr auto CreateTypeMap(const EtsPlatformTypes *ptypes)
55 {
56     return std::array {std::pair {ptypes->coreDouble, &Func::template Call<EtsDouble>},
57                        std::pair {ptypes->coreInt, &Func::template Call<EtsInt>},
58                        std::pair {ptypes->coreByte, &Func::template Call<EtsByte>},
59                        std::pair {ptypes->coreShort, &Func::template Call<EtsShort>},
60                        std::pair {ptypes->coreLong, &Func::template Call<EtsLong>},
61                        std::pair {ptypes->coreFloat, &Func::template Call<EtsFloat>},
62                        std::pair {ptypes->coreChar, &Func::template Call<EtsChar>},
63                        std::pair {ptypes->coreBoolean, &Func::template Call<EtsBoolean>}};
64 }
65 
NormalizeArrayIndex(EtsInt index,EtsInt actualLength)66 EtsInt NormalizeArrayIndex(EtsInt index, EtsInt actualLength)
67 {
68     if (index < -actualLength) {
69         return 0;
70     }
71 
72     if (index < 0) {
73         return actualLength + index;
74     }
75 
76     if (index > actualLength) {
77         return actualLength;
78     }
79 
80     return index;
81 }
82 
EtsEscompatArrayIndexOfString(EtsObjectArray * buffer,EtsObject * value,EtsInt fromIndex,EtsInt actualLength)83 EtsDouble EtsEscompatArrayIndexOfString(EtsObjectArray *buffer, EtsObject *value, EtsInt fromIndex, EtsInt actualLength)
84 {
85     auto valueString = coretypes::String::Cast(value->GetCoreType());
86     for (EtsInt index = fromIndex; index < actualLength; index++) {
87         auto element = buffer->Get(index);
88         if (element != nullptr && element->IsStringClass() &&
89             valueString->Compare(coretypes::String::Cast(element->GetCoreType())) == 0) {
90             return index;
91         }
92     }
93     return -1;
94 }
95 
EtsEscompatArrayLastIndexOfString(EtsObjectArray * buffer,EtsObject * value,EtsInt fromIndex)96 EtsDouble EtsEscompatArrayLastIndexOfString(EtsObjectArray *buffer, EtsObject *value, EtsInt fromIndex)
97 {
98     auto valueString = coretypes::String::Cast(value->GetCoreType());
99     for (EtsInt index = fromIndex; index >= 0; index--) {
100         auto element = buffer->Get(index);
101         if (element != nullptr && element->IsStringClass() &&
102             valueString->Compare(coretypes::String::Cast(element->GetCoreType())) == 0) {
103             return index;
104         }
105     }
106     return -1;
107 }
108 
EtsEscompatArrayIndexOfEnum(EtsObjectArray * buffer,EtsCoroutine * coro,EtsObject * value,EtsInt fromIndex,EtsInt actualLength)109 EtsDouble EtsEscompatArrayIndexOfEnum(EtsObjectArray *buffer, EtsCoroutine *coro, EtsObject *value, EtsInt fromIndex,
110                                       EtsInt actualLength)
111 {
112     auto *valueEnum = EtsBaseEnum::FromEtsObject(value)->GetValue();
113     for (EtsInt index = fromIndex; index < actualLength; index++) {
114         auto element = buffer->Get(index);
115         auto elementClass = element->GetClass();
116         if (elementClass->IsEtsEnum()) {
117             auto *elementEnum = EtsBaseEnum::FromEtsObject(element)->GetValue();
118             if (EtsReferenceEquals(coro, valueEnum, elementEnum)) {
119                 return index;
120             }
121         }
122     }
123     return -1;
124 }
125 
EtsEscompatArrayLastIndexOfEnum(EtsObjectArray * buffer,EtsCoroutine * coro,EtsObject * value,EtsInt fromIndex)126 EtsDouble EtsEscompatArrayLastIndexOfEnum(EtsObjectArray *buffer, EtsCoroutine *coro, EtsObject *value,
127                                           EtsInt fromIndex)
128 {
129     auto *valueEnum = EtsBaseEnum::FromEtsObject(value)->GetValue();
130     for (EtsInt index = fromIndex; index >= 0; index--) {
131         auto element = buffer->Get(index);
132         auto elementClass = element->GetClass();
133         if (elementClass->IsEtsEnum()) {
134             auto *elementEnum = EtsBaseEnum::FromEtsObject(element)->GetValue();
135             if (EtsReferenceEquals(coro, valueEnum, elementEnum)) {
136                 return index;
137             }
138         }
139     }
140     return -1;
141 }
142 
EtsEscompatArrayIndexOfNull(EtsObjectArray * buffer,EtsCoroutine * coroutine,EtsInt fromIndex,EtsInt actualLength)143 EtsDouble EtsEscompatArrayIndexOfNull(EtsObjectArray *buffer, EtsCoroutine *coroutine, EtsInt fromIndex,
144                                       EtsInt actualLength)
145 {
146     for (EtsInt index = fromIndex; index < actualLength; index++) {
147         auto element = buffer->Get(index);
148         if (element == EtsObject::FromCoreType(coroutine->GetNullValue())) {
149             return index;
150         }
151     }
152     return -1;
153 }
154 
EtsEscompatArrayIndexOfUndefined(EtsObjectArray * buffer,EtsInt fromIndex,EtsInt actualLength)155 EtsDouble EtsEscompatArrayIndexOfUndefined(EtsObjectArray *buffer, EtsInt fromIndex, EtsInt actualLength)
156 {
157     for (EtsInt index = fromIndex; index < actualLength; index++) {
158         auto element = buffer->Get(index);
159         if (element == nullptr) {
160             return index;
161         }
162     }
163     return -1;
164 }
165 
EtsEscompatArrayLastIndexOfUndefined(EtsObjectArray * buffer,EtsInt fromIndex)166 EtsDouble EtsEscompatArrayLastIndexOfUndefined(EtsObjectArray *buffer, EtsInt fromIndex)
167 {
168     for (EtsInt index = fromIndex; index >= 0; index--) {
169         auto element = buffer->Get(index);
170         if (element == nullptr) {
171             return index;
172         }
173     }
174     return -1;
175 }
176 
EtsEscompatArrayLastIndexOfNull(EtsObjectArray * buffer,EtsCoroutine * coroutine,EtsInt fromIndex)177 EtsDouble EtsEscompatArrayLastIndexOfNull(EtsObjectArray *buffer, EtsCoroutine *coroutine, EtsInt fromIndex)
178 {
179     for (EtsInt index = fromIndex; index >= 0; index--) {
180         auto element = buffer->Get(index);
181         if (element == EtsObject::FromCoreType(coroutine->GetNullValue())) {
182             return index;
183         }
184     }
185     return -1;
186 }
187 
EtsEscompatArrayIndexOfBigInt(EtsObjectArray * buffer,EtsObject * value,EtsInt fromIndex,EtsInt actualLength)188 EtsDouble EtsEscompatArrayIndexOfBigInt(EtsObjectArray *buffer, EtsObject *value, EtsInt fromIndex, EtsInt actualLength)
189 {
190     auto valueBigInt = EtsBigInt::FromEtsObject(value);
191     for (EtsInt index = fromIndex; index < actualLength; index++) {
192         auto element = buffer->Get(index);
193         auto elementClass = element == nullptr ? nullptr : element->GetClass();
194         if (elementClass != nullptr && elementClass->IsBigInt() &&
195             EtsBigIntEquality(valueBigInt, EtsBigInt::FromEtsObject(value))) {
196             return index;
197         }
198     }
199     return -1;
200 }
201 
EtsEscompatArrayLastIndexOfBigInt(EtsObjectArray * buffer,EtsObject * value,EtsInt fromIndex)202 EtsDouble EtsEscompatArrayLastIndexOfBigInt(EtsObjectArray *buffer, EtsObject *value, EtsInt fromIndex)
203 {
204     auto valueBigInt = EtsBigInt::FromEtsObject(value);
205     for (EtsInt index = fromIndex; index >= 0; index--) {
206         auto element = buffer->Get(index);
207         auto elementClass = element == nullptr ? nullptr : element->GetClass();
208         if (elementClass != nullptr && elementClass->IsBigInt() &&
209             EtsBigIntEquality(valueBigInt, EtsBigInt::FromEtsObject(value))) {
210             return index;
211         }
212     }
213     return -1;
214 }
215 
216 template <typename T>
GetNumericValue(EtsObject * element)217 static auto GetNumericValue(EtsObject *element)
218 {
219     return EtsBoxPrimitive<T>::FromCoreType(element)->GetValue();
220 }
221 
222 template <typename T>
EtsEscompatArrayIndexOfNumeric(EtsObjectArray * buffer,EtsClass * valueCls,EtsObject * value,EtsInt fromIndex,EtsInt actualLength)223 EtsDouble EtsEscompatArrayIndexOfNumeric(EtsObjectArray *buffer, EtsClass *valueCls, EtsObject *value, EtsInt fromIndex,
224                                          EtsInt actualLength)
225 {
226     auto valTyped = GetNumericValue<T>(value);
227     if (std::isnan(valTyped)) {
228         return -1;
229     }
230 
231     for (EtsInt index = fromIndex; index < actualLength; index++) {
232         auto element = buffer->Get(index);
233         if (element != nullptr && element->GetClass() == valueCls && valTyped == GetNumericValue<T>(element)) {
234             return index;
235         }
236     }
237     return -1;
238 }
239 
240 template <typename T>
EtsEscompatArrayLastIndexOfNumeric(EtsObjectArray * buffer,EtsClass * valueCls,EtsObject * value,EtsInt fromIndex)241 EtsDouble EtsEscompatArrayLastIndexOfNumeric(EtsObjectArray *buffer, EtsClass *valueCls, EtsObject *value,
242                                              EtsInt fromIndex)
243 {
244     auto valTyped = GetNumericValue<T>(value);
245     if (std::isnan(valTyped)) {
246         return -1;
247     }
248 
249     for (EtsInt index = fromIndex; index >= 0; index--) {
250         auto element = buffer->Get(index);
251         if (element != nullptr && element->GetClass() == valueCls && valTyped == GetNumericValue<T>(element)) {
252             return index;
253         }
254     }
255     return -1;
256 }
257 
258 template <typename T>
DispatchIndexOf(EtsObjectArray * buf,EtsClass * cls,EtsObject * val,EtsInt idx,EtsInt len)259 static EtsDouble DispatchIndexOf(EtsObjectArray *buf, EtsClass *cls, EtsObject *val, EtsInt idx, EtsInt len)
260 {
261     return EtsEscompatArrayIndexOfNumeric<T>(buf, cls, val, idx, len);
262 }
263 
264 template <typename T>
DispatchLastIndexOf(EtsObjectArray * buf,EtsClass * cls,EtsObject * val,EtsInt idx)265 static EtsDouble DispatchLastIndexOf(EtsObjectArray *buf, EtsClass *cls, EtsObject *val, EtsInt idx)
266 {
267     return EtsEscompatArrayLastIndexOfNumeric<T>(buf, cls, val, idx);
268 }
269 
270 struct IndexOfDispatcher {
271     template <typename T>
Callark::ets::intrinsics::IndexOfDispatcher272     static EtsDouble Call(EtsObjectArray *buf, EtsClass *cls, EtsObject *val, EtsInt idx, EtsInt len)
273     {
274         return DispatchIndexOf<T>(buf, cls, val, idx, len);
275     }
276 };
277 
278 struct LastIndexOfDispatcher {
279     template <typename T>
Callark::ets::intrinsics::LastIndexOfDispatcher280     static EtsDouble Call(EtsObjectArray *buf, EtsClass *cls, EtsObject *val, EtsInt idx)
281     {
282         return DispatchLastIndexOf<T>(buf, cls, val, idx);
283     }
284 };
285 
EtsEscompatArrayIndexOfCommon(EtsObjectArray * buffer,EtsObject * value,EtsInt fromIndex,EtsInt actualLength)286 EtsDouble EtsEscompatArrayIndexOfCommon(EtsObjectArray *buffer, EtsObject *value, EtsInt fromIndex, EtsInt actualLength)
287 {
288     for (EtsInt index = fromIndex; index < actualLength; index++) {
289         auto element = buffer->Get(index);
290         if (element == value) {
291             return index;
292         }
293     }
294     return -1;
295 }
296 
EtsEscompatArrayLastIndexOfCommon(EtsObjectArray * buffer,EtsObject * value,EtsInt fromIndex)297 EtsDouble EtsEscompatArrayLastIndexOfCommon(EtsObjectArray *buffer, EtsObject *value, EtsInt fromIndex)
298 {
299     for (EtsInt index = fromIndex; index >= 0; index--) {
300         auto element = buffer->Get(index);
301         if (element == value) {
302             return index;
303         }
304     }
305     return -1;
306 }
307 
EtsEscompatArrayInternalIndexOfImpl(ObjectHeader * arrayHeader,EtsObject * value,EtsInt fromIndex)308 EtsInt EtsEscompatArrayInternalIndexOfImpl(ObjectHeader *arrayHeader, EtsObject *value, EtsInt fromIndex)
309 {
310     auto actualLength = ObjectAccessor::GetPrimitive<EtsInt>(
311         arrayHeader, cross_values::GetEscompatArrayActualLengthOffset(RUNTIME_ARCH));
312     fromIndex = NormalizeArrayIndex(fromIndex, actualLength);
313     EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
314     const EtsPlatformTypes *ptypes = PlatformTypes(coroutine);
315     ObjectHeader *bufferObjectHeader =
316         ObjectAccessor::GetObject(arrayHeader, cross_values::GetEscompatArrayBufferOffset(RUNTIME_ARCH));
317     EtsObjectArray *buffer = EtsObjectArray::FromCoreType(bufferObjectHeader);
318 
319     if (value == nullptr) {
320         return EtsEscompatArrayIndexOfUndefined(buffer, fromIndex, actualLength);
321     }
322 
323     auto valueCls = value->GetClass();
324     static const auto TYPE_MAP = CreateTypeMap<IndexOfDispatcher>(ptypes);
325     for (const auto &[type, handler] : TYPE_MAP) {
326         if (valueCls == type) {
327             return handler(buffer, valueCls, value, fromIndex, actualLength);
328         }
329     }
330 
331     if (valueCls->IsBigInt()) {
332         return EtsEscompatArrayIndexOfBigInt(buffer, value, fromIndex, actualLength);
333     }
334 
335     if (valueCls->IsStringClass()) {
336         return EtsEscompatArrayIndexOfString(buffer, value, fromIndex, actualLength);
337     }
338 
339     if (valueCls->IsEtsEnum()) {
340         return EtsEscompatArrayIndexOfEnum(buffer, coroutine, value, fromIndex, actualLength);
341     }
342 
343     if (value == EtsObject::FromCoreType(coroutine->GetNullValue())) {
344         return EtsEscompatArrayIndexOfNull(buffer, coroutine, fromIndex, actualLength);
345     }
346 
347     return EtsEscompatArrayIndexOfCommon(buffer, value, fromIndex, actualLength);
348 }
349 
EtsEscompatArrayInternalIndexOf(ObjectHeader * arrayHeader,EtsObject * value,EtsInt fromIndex)350 extern "C" EtsInt EtsEscompatArrayInternalIndexOf(ObjectHeader *arrayHeader, EtsObject *value, EtsInt fromIndex)
351 {
352     return EtsEscompatArrayInternalIndexOfImpl(arrayHeader, value, fromIndex);
353 }
354 
EtsEscompatArrayIndexOf(ObjectHeader * arrayHeader,EtsObject * value)355 extern "C" EtsDouble EtsEscompatArrayIndexOf(ObjectHeader *arrayHeader, EtsObject *value)
356 {
357     return EtsEscompatArrayInternalIndexOfImpl(arrayHeader, value, 0);
358 }
359 
EtsEscompatArrayInternalLastIndexOfImpl(ObjectHeader * arrayHeader,EtsObject * value,EtsInt fromIndex)360 EtsInt EtsEscompatArrayInternalLastIndexOfImpl(ObjectHeader *arrayHeader, EtsObject *value, EtsInt fromIndex)
361 {
362     auto actualLength = ObjectAccessor::GetPrimitive<EtsInt>(
363         arrayHeader, cross_values::GetEscompatArrayActualLengthOffset(RUNTIME_ARCH));
364     if (actualLength == 0) {
365         return -1;
366     }
367 
368     EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
369     const EtsPlatformTypes *ptypes = PlatformTypes(coroutine);
370     ObjectHeader *bufferObjectHeader =
371         ObjectAccessor::GetObject(arrayHeader, cross_values::GetEscompatArrayBufferOffset(RUNTIME_ARCH));
372     EtsObjectArray *buffer = EtsObjectArray::FromCoreType(bufferObjectHeader);
373     EtsInt startIndex = 0;
374 
375     if (fromIndex >= 0) {
376         startIndex = std::min(actualLength - 1, fromIndex);
377     } else {
378         startIndex = actualLength + fromIndex;
379     }
380 
381     if (value == nullptr) {
382         return EtsEscompatArrayLastIndexOfUndefined(buffer, startIndex);
383     }
384 
385     auto valueCls = value->GetClass();
386     static const auto TYPE_MAP = CreateTypeMap<LastIndexOfDispatcher>(ptypes);
387     for (const auto &[type, handler] : TYPE_MAP) {
388         if (valueCls == type) {
389             return handler(buffer, valueCls, value, startIndex);
390         }
391     }
392 
393     if (valueCls->IsBigInt()) {
394         return EtsEscompatArrayLastIndexOfBigInt(buffer, value, startIndex);
395     }
396 
397     if (valueCls->IsStringClass()) {
398         return EtsEscompatArrayLastIndexOfString(buffer, value, startIndex);
399     }
400 
401     if (valueCls->IsEtsEnum()) {
402         return EtsEscompatArrayLastIndexOfEnum(buffer, coroutine, value, startIndex);
403     }
404 
405     if (value == EtsObject::FromCoreType(coroutine->GetNullValue())) {
406         return EtsEscompatArrayLastIndexOfNull(buffer, coroutine, startIndex);
407     }
408 
409     return EtsEscompatArrayLastIndexOfCommon(buffer, value, startIndex);
410 }
411 
EtsEscompatArrayInternalLastIndexOf(ObjectHeader * arrayHeader,EtsObject * value,EtsInt fromIndex)412 extern "C" EtsInt EtsEscompatArrayInternalLastIndexOf(ObjectHeader *arrayHeader, EtsObject *value, EtsInt fromIndex)
413 {
414     return EtsEscompatArrayInternalLastIndexOfImpl(arrayHeader, value, fromIndex);
415 }
416 
EtsEscompatArrayLastIndexOf(ObjectHeader * arrayHeader,EtsObject * value)417 extern "C" EtsDouble EtsEscompatArrayLastIndexOf(ObjectHeader *arrayHeader, EtsObject *value)
418 {
419     auto actualLength = ObjectAccessor::GetPrimitive<EtsInt>(
420         arrayHeader, cross_values::GetEscompatArrayActualLengthOffset(RUNTIME_ARCH));
421     return EtsEscompatArrayInternalLastIndexOfImpl(arrayHeader, value, actualLength - 1);
422 }
423 
NormalizeIndex(int32_t idx,int64_t len)424 static uint32_t NormalizeIndex(int32_t idx, int64_t len)
425 {
426     if (idx < 0) {
427         return idx < -len ? 0 : idx + len;
428     }
429     return idx > len ? len : idx;
430 }
431 
EtsEscompatArrayFill(ObjectHeader * arrayHeader,EtsObject * value,int32_t start,int32_t end)432 extern "C" ObjectHeader *EtsEscompatArrayFill(ObjectHeader *arrayHeader, EtsObject *value, int32_t start, int32_t end)
433 {
434     ASSERT(arrayHeader != nullptr);
435     auto *array = EtsArrayObject<EtsObject>::FromEtsObject(EtsObject::FromCoreType(arrayHeader));
436     auto actualLength = static_cast<int64_t>(array->GetActualLength());
437     auto startInd = NormalizeIndex(start, actualLength);
438     auto endInd = NormalizeIndex(end, actualLength);
439     array->GetData()->Fill(value, startInd, endInd);
440     return arrayHeader;
441 }
442 }  // namespace ark::ets::intrinsics
443