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