• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 #ifndef PANDA_PLUGINS_ETS_RUNTIME_FFI_CLASSES_ETS_STRING_H_
17 #define PANDA_PLUGINS_ETS_RUNTIME_FFI_CLASSES_ETS_STRING_H_
18 
19 #include "runtime/include/runtime.h"
20 #include "runtime/include/coretypes/string-inl.h"
21 #include "plugins/ets/runtime/types/ets_array.h"
22 #include "plugins/ets/runtime/types/ets_box_primitive.h"
23 #include "plugins/ets/runtime/types/ets_object.h"
24 #include "plugins/ets/runtime/napi/ets_napi.h"
25 #include <cmath>
26 
27 namespace ark::ets {
28 
29 // Private inheritance, because need to disallow implicit conversion to core type
30 class EtsString : private coretypes::String {
31 public:
CreateFromMUtf8(const char * mutf8)32     static EtsString *CreateFromMUtf8(const char *mutf8)
33     {
34         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
35         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
36         if (mutf8 == nullptr) {
37             ThrowNullPointerException(ctx, ManagedThread::GetCurrent());
38             return nullptr;
39         }
40         const auto *data = reinterpret_cast<const uint8_t *>(mutf8);
41         coretypes::String *s = coretypes::String::CreateFromMUtf8(data, ctx, Runtime::GetCurrent()->GetPandaVM());
42         return reinterpret_cast<EtsString *>(s);
43     }
44 
CreateFromMUtf8(const char * mutf8,uint32_t utf16Length)45     static EtsString *CreateFromMUtf8(const char *mutf8, uint32_t utf16Length)
46     {
47         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
48         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
49         if (utf16Length == 0) {
50             return reinterpret_cast<EtsString *>(
51                 coretypes::String::CreateEmptyString(ctx, Runtime::GetCurrent()->GetPandaVM()));
52         }
53         if (mutf8 == nullptr) {
54             ThrowNullPointerException(ctx, ManagedThread::GetCurrent());
55             return nullptr;
56         }
57         const auto *data = reinterpret_cast<const uint8_t *>(mutf8);
58         coretypes::String *s =
59             coretypes::String::CreateFromMUtf8(data, utf16Length, ctx, Runtime::GetCurrent()->GetPandaVM());
60         return reinterpret_cast<EtsString *>(s);
61     }
62 
CreateFromUtf8(const char * utf8,uint32_t length)63     static EtsString *CreateFromUtf8(const char *utf8, uint32_t length)
64     {
65         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
66         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
67         if (length == 0) {
68             return reinterpret_cast<EtsString *>(
69                 coretypes::String::CreateEmptyString(ctx, Runtime::GetCurrent()->GetPandaVM()));
70         }
71         if (utf8 == nullptr) {
72             ThrowNullPointerException(ctx, ManagedThread::GetCurrent());
73             return nullptr;
74         }
75         const auto *data = reinterpret_cast<const uint8_t *>(utf8);
76         return reinterpret_cast<EtsString *>(
77             coretypes::String::CreateFromUtf8(data, length, ctx, Runtime::GetCurrent()->GetPandaVM()));
78     }
79 
CreateFromAscii(const char * str,uint32_t length)80     static EtsString *CreateFromAscii(const char *str, uint32_t length)
81     {
82         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
83         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
84         const auto *data = reinterpret_cast<const uint8_t *>(str);
85         return reinterpret_cast<EtsString *>(
86             coretypes::String::CreateFromMUtf8(data, length, length, true, ctx, Runtime::GetCurrent()->GetPandaVM()));
87     }
88 
CreateFromUtf16(const ets_char * utf16,ets_int length)89     static EtsString *CreateFromUtf16(const ets_char *utf16, ets_int length)
90     {
91         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
92         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
93         if (length == 0) {
94             return reinterpret_cast<EtsString *>(
95                 coretypes::String::CreateEmptyString(ctx, Runtime::GetCurrent()->GetPandaVM()));
96         }
97         if (utf16 == nullptr) {
98             ThrowNullPointerException(ctx, ManagedThread::GetCurrent());
99             return nullptr;
100         }
101         coretypes::String *s =
102             coretypes::String::CreateFromUtf16(utf16, length, ctx, Runtime::GetCurrent()->GetPandaVM());
103         return reinterpret_cast<EtsString *>(s);
104     }
105 
106     using CharCodeArray = EtsTypedObjectArray<EtsBoxPrimitive<EtsDouble>>;
107 
CreateNewStringFromCharCode(CharCodeArray * charCodes)108     static EtsString *CreateNewStringFromCharCode(CharCodeArray *charCodes)
109     {
110         const size_t length = charCodes->GetLength();
111         if (length == 0) {
112             return CreateNewEmptyString();
113         }
114 
115         // allocator may trig gc and move the 'charCodes' array, need to hold it
116         EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
117         [[maybe_unused]] EtsHandleScope scope(coroutine);
118         EtsHandle arrayHandle(coroutine, charCodes);
119 
120         auto isCompressed = [](CharCodeArray *codes) -> bool {
121             if (!Runtime::GetOptions().IsRuntimeCompressedStringsEnabled()) {
122                 return false;
123             }
124 
125             for (size_t i = 0; i < codes->GetLength(); ++i) {
126                 if (!IsASCIICharacter(CodeToChar(codes->Get(i)->GetValue()))) {
127                     return false;
128                 }
129             }
130             return true;
131         };
132 
133         auto copyCharsIntoString = [](CharCodeArray *codes, auto *dstData) {
134             Span<std::remove_pointer_t<decltype(dstData)>> to(dstData, codes->GetLength());
135             for (size_t i = 0; i < codes->GetLength(); ++i) {
136                 to[i] = CodeToChar(codes->Get(i)->GetValue());
137             }
138         };
139 
140         bool compress = isCompressed(arrayHandle.GetPtr());
141         EtsString *s = AllocateNonInitializedString(length, compress);
142         if (compress) {
143             copyCharsIntoString(arrayHandle.GetPtr(), s->GetDataMUtf8());
144         } else {
145             copyCharsIntoString(arrayHandle.GetPtr(), s->GetDataUtf16());
146         }
147         return s;
148     }
149 
CreateNewStringFromCharCode(EtsDouble charCode)150     static EtsString *CreateNewStringFromCharCode(EtsDouble charCode)
151     {
152         constexpr size_t SINGLE_CHAR_LENGTH = 1U;
153         uint16_t character = CodeToChar(charCode);
154         bool compress = Runtime::GetOptions().IsRuntimeCompressedStringsEnabled() && IsASCIICharacter(character);
155         EtsString *s = AllocateNonInitializedString(SINGLE_CHAR_LENGTH, compress);
156         ASSERT(s != nullptr);
157         auto putCharacterIntoString = [character](auto *dstData) {
158             Span<std::remove_pointer_t<decltype(dstData)>> to(dstData, SINGLE_CHAR_LENGTH);
159             to[0] = character;
160         };
161         if (compress) {
162             putCharacterIntoString(s->GetDataMUtf8());
163         } else {
164             putCharacterIntoString(s->GetDataUtf16());
165         }
166         return s;
167     }
168 
CreateNewStringFromChars(uint32_t offset,uint32_t length,EtsArray * chararray)169     static EtsString *CreateNewStringFromChars(uint32_t offset, uint32_t length, EtsArray *chararray)
170     {
171         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
172         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
173         return reinterpret_cast<EtsString *>(coretypes::String::CreateNewStringFromChars(
174             offset, length, reinterpret_cast<coretypes::Array *>(chararray), ctx, Runtime::GetCurrent()->GetPandaVM()));
175     }
176 
CreateNewStringFromBytes(EtsArray * bytearray,ets_int high,ets_int offset,ets_int length)177     static EtsString *CreateNewStringFromBytes(EtsArray *bytearray, ets_int high, ets_int offset, ets_int length)
178     {
179         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
180         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
181         if (length == 0) {
182             return reinterpret_cast<EtsString *>(
183                 coretypes::String::CreateEmptyString(ctx, Runtime::GetCurrent()->GetPandaVM()));
184         }
185         if (bytearray == nullptr) {
186             ThrowNullPointerException(ctx, ManagedThread::GetCurrent());
187             return nullptr;
188         }
189         if (offset < 0 || length < 0 || bytearray->GetLength() < static_cast<uint32_t>(offset + length)) {
190             ThrowArrayIndexOutOfBoundsException(static_cast<uint32_t>(offset + length), bytearray->GetLength(), ctx,
191                                                 ManagedThread::GetCurrent());
192             return nullptr;
193         }
194         return reinterpret_cast<EtsString *>(coretypes::String::CreateNewStringFromBytes(
195             offset, length, static_cast<uint32_t>(high), reinterpret_cast<coretypes::Array *>(bytearray), ctx,
196             Runtime::GetCurrent()->GetPandaVM()));
197     }
198 
CreateNewStringFromString(EtsString * etsString)199     static EtsString *CreateNewStringFromString(EtsString *etsString)
200     {
201         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
202         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
203         if (etsString == nullptr) {
204             ThrowNullPointerException(ctx, ManagedThread::GetCurrent());
205             return nullptr;
206         }
207         coretypes::String *string = etsString->GetCoreType();
208         return reinterpret_cast<EtsString *>(
209             coretypes::String::CreateFromString(string, ctx, Runtime::GetCurrent()->GetPandaVM()));
210     }
211 
CreateNewEmptyString()212     static EtsString *CreateNewEmptyString()
213     {
214         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
215         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
216         return reinterpret_cast<EtsString *>(
217             coretypes::String::CreateEmptyString(ctx, Runtime::GetCurrent()->GetPandaVM()));
218     }
219 
Resolve(const uint8_t * mutf8,uint32_t length)220     static EtsString *Resolve(const uint8_t *mutf8, uint32_t length)
221     {
222         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
223         return reinterpret_cast<EtsString *>(
224             Runtime::GetCurrent()->ResolveString(Runtime::GetCurrent()->GetPandaVM(), mutf8, length, ctx));
225     }
226 
Concat(EtsString * etsString1,EtsString * etsString2)227     static EtsString *Concat(EtsString *etsString1, EtsString *etsString2)
228     {
229         ASSERT(etsString1 != nullptr);
230         ASSERT(etsString2 != nullptr);
231         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
232         ASSERT(etsString1 != nullptr);
233         ASSERT(etsString2 != nullptr);
234         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
235         coretypes::String *string3 = coretypes::String::Concat(etsString1->GetCoreType(), etsString2->GetCoreType(),
236                                                                ctx, Runtime::GetCurrent()->GetPandaVM());
237         return reinterpret_cast<EtsString *>(string3);
238     }
239 
TrimLeft()240     EtsString *TrimLeft()
241     {
242         if (IsEmpty() || !utf::IsWhiteSpaceChar(At(0))) {
243             return this;
244         }
245         const auto numChars = GetLength();
246         auto notWhiteSpaceIndex = numChars;
247         for (int i = 1; i < numChars; ++i) {
248             if (!utf::IsWhiteSpaceChar(At(i))) {
249                 notWhiteSpaceIndex = i;
250                 break;
251             }
252         }
253         auto len = numChars - notWhiteSpaceIndex;
254         return FastSubString(this, static_cast<uint32_t>(notWhiteSpaceIndex), static_cast<uint32_t>(len));
255     }
256 
TrimRight()257     EtsString *TrimRight()
258     {
259         if (IsEmpty()) {
260             return this;
261         }
262         auto last = GetLength() - 1;
263         if (!utf::IsWhiteSpaceChar(At(last))) {
264             return this;
265         }
266         int notWhiteSpaceIndex = -1;
267         for (int i = last - 1; i >= 0; --i) {
268             if (!utf::IsWhiteSpaceChar(At(i))) {
269                 notWhiteSpaceIndex = i;
270                 break;
271             }
272         }
273         auto len = notWhiteSpaceIndex + 1;
274         return EtsString::FastSubString(this, 0, static_cast<uint32_t>(len));
275     }
276 
Trim()277     EtsString *Trim()
278     {
279         if (IsEmpty()) {
280             return this;
281         }
282         int left = 0;
283         int right = GetLength() - 1;
284         while (utf::IsWhiteSpaceChar(At(right))) {
285             if (right == left) {
286                 return EtsString::CreateNewEmptyString();
287             }
288             --right;
289         }
290         while (left < right && utf::IsWhiteSpaceChar(At(left))) {
291             ++left;
292         }
293         return EtsString::FastSubString(this, left, static_cast<uint32_t>(right - left + 1));
294     }
295 
StartsWith(EtsString * prefix,EtsInt fromIndex)296     EtsBoolean StartsWith(EtsString *prefix, EtsInt fromIndex)
297     {
298         ASSERT(prefix != nullptr);
299         if (fromIndex < 0) {
300             fromIndex = 0;
301         }
302         auto prefixLen = prefix->GetLength();
303         ASSERT(prefixLen >= 0);
304         if (fromIndex > GetLength() - prefixLen) {
305             return ToEtsBoolean(prefix->IsEmpty());
306         }
307         auto *thisCoreType = GetCoreType();
308         auto *prefCoreType = prefix->GetCoreType();
309         for (EtsInt i = 0; i < prefixLen; ++i) {
310             if (thisCoreType->At<false>(fromIndex + i) != prefCoreType->At<false>(i)) {
311                 return ToEtsBoolean(false);
312             }
313         }
314         return ToEtsBoolean(true);
315     }
316 
EndsWith(EtsString * suffix,EtsInt endIndex)317     EtsBoolean EndsWith(EtsString *suffix, EtsInt endIndex)
318     {
319         ASSERT(suffix != nullptr);
320         if (suffix->IsEmpty()) {
321             return ToEtsBoolean(true);
322         }
323         auto strLen = GetLength();
324         if (strLen == 0) {
325             return ToEtsBoolean(false);
326         }
327         if (endIndex <= 0) {
328             return ToEtsBoolean(false);
329         }
330         if (endIndex > strLen) {
331             endIndex = strLen;
332         }
333         ASSERT(endIndex > 0);
334         auto suffixLen = suffix->GetLength();
335         auto fromIndex = endIndex - suffixLen;
336         if (fromIndex < 0) {
337             return ToEtsBoolean(false);
338         }
339         auto *thisCoreType = GetCoreType();
340         auto *suffCoreType = suffix->GetCoreType();
341         for (EtsInt i = 0; i < suffixLen; ++i) {
342             if (thisCoreType->At<false>(fromIndex + i) != suffCoreType->At<false>(i)) {
343                 return ToEtsBoolean(false);
344             }
345         }
346         return ToEtsBoolean(true);
347     }
348 
Compare(EtsString * rhs)349     int32_t Compare(EtsString *rhs)
350     {
351         return GetCoreType()->Compare(rhs->GetCoreType());
352     }
353 
At(int32_t index)354     uint16_t At(int32_t index)
355     {
356         return GetCoreType()->At(index);
357     }
358 
DoReplace(EtsString * src,ets_char oldC,ets_char newC)359     static EtsString *DoReplace(EtsString *src, ets_char oldC, ets_char newC)
360     {
361         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
362         if (src->GetLength() == 0) {
363             return reinterpret_cast<EtsString *>(
364                 coretypes::String::CreateEmptyString(ctx, Runtime::GetCurrent()->GetPandaVM()));
365         }
366         coretypes::String *result = coretypes::String::DoReplace(reinterpret_cast<coretypes::String *>(src), oldC, newC,
367                                                                  ctx, Runtime::GetCurrent()->GetPandaVM());
368         return reinterpret_cast<EtsString *>(result);
369     }
370 
FastSubString(EtsString * src,uint32_t start,uint32_t length)371     static EtsString *FastSubString(EtsString *src, uint32_t start, uint32_t length)
372     {
373         ASSERT(src != nullptr);
374         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
375         coretypes::String *string1 = src->GetCoreType();
376         coretypes::String *string2 =
377             coretypes::String::FastSubString(string1, start, length, ctx, Runtime::GetCurrent()->GetPandaVM());
378         return reinterpret_cast<EtsString *>(string2);
379     }
380 
ToCharArray()381     EtsArray *ToCharArray()
382     {
383         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
384         auto *ret = GetCoreType()->ToCharArray(ctx);
385         if (UNLIKELY(ret == nullptr)) {
386             return nullptr;
387         }
388         return reinterpret_cast<EtsArray *>(ret);
389     }
390 
IsUtf16()391     bool IsUtf16()
392     {
393         return GetCoreType()->IsUtf16();
394     }
395 
GetLength()396     ets_int GetLength()
397     {
398         return GetCoreType()->GetLength();
399     }
400 
CopyDataMUtf8(void * buf,size_t maxLength,bool isCString)401     size_t CopyDataMUtf8(void *buf, size_t maxLength, bool isCString)
402     {
403         return GetCoreType()->CopyDataMUtf8(reinterpret_cast<uint8_t *>(buf), maxLength, isCString);
404     }
405 
CopyDataRegionUtf8(void * buf,size_t start,size_t length,size_t maxLength)406     size_t CopyDataRegionUtf8(void *buf, size_t start, size_t length, size_t maxLength)
407     {
408         return GetCoreType()->CopyDataRegionUtf8(reinterpret_cast<uint8_t *>(buf), start, length, maxLength);
409     }
410 
CopyDataRegionMUtf8(void * buf,size_t start,size_t length,size_t maxLength)411     size_t CopyDataRegionMUtf8(void *buf, size_t start, size_t length, size_t maxLength)
412     {
413         return GetCoreType()->CopyDataRegionMUtf8(reinterpret_cast<uint8_t *>(buf), start, length, maxLength);
414     }
415 
CopyDataUtf16(void * buf,uint32_t maxLength)416     uint32_t CopyDataUtf16(void *buf, uint32_t maxLength)
417     {
418         return GetCoreType()->CopyDataUtf16(reinterpret_cast<uint16_t *>(buf), maxLength);
419     }
420 
CopyDataRegionUtf16(void * buf,uint32_t start,uint32_t length,uint32_t maxLength)421     uint32_t CopyDataRegionUtf16(void *buf, uint32_t start, uint32_t length, uint32_t maxLength)
422     {
423         return GetCoreType()->CopyDataRegionUtf16(reinterpret_cast<uint16_t *>(buf), start, length, maxLength);
424     }
425 
ConvertToStringView(PandaVector<uint8_t> * buf)426     std::string_view ConvertToStringView(PandaVector<uint8_t> *buf)
427     {
428         if (IsUtf16()) {
429             size_t len = utf::Utf16ToUtf8Size(GetDataUtf16(), GetUtf16Length(), false) - 1;
430             buf->reserve(len);
431             utf::ConvertRegionUtf16ToUtf8(GetDataUtf16(), buf->data(), GetUtf16Length(), len, 0, false);
432             return {utf::Mutf8AsCString(buf->data()), len};
433         }
434         return {utf::Mutf8AsCString(GetDataMUtf8()), static_cast<size_t>(GetLength())};
435     }
436 
GetMUtf8Length()437     uint32_t GetMUtf8Length()
438     {
439         return GetCoreType()->GetMUtf8Length();
440     }
441 
GetUtf16Length()442     uint32_t GetUtf16Length()
443     {
444         return GetCoreType()->GetUtf16Length();
445     }
446 
GetUtf8Length()447     uint32_t GetUtf8Length()
448     {
449         return GetCoreType()->GetUtf8Length();
450     }
451 
IsEqual(const char * str)452     bool IsEqual(const char *str)
453     {
454         auto *mutf8Str = utf::CStringAsMutf8(str);
455         return coretypes::String::StringsAreEqualMUtf8(GetCoreType(), mutf8Str, utf::MUtf8ToUtf16Size(mutf8Str));
456     }
457 
GetMutf8()458     PandaString GetMutf8()
459     {
460         size_t len = GetMUtf8Length();
461         PandaString out;
462         out.resize(len - 1);
463         if (!IsUtf16()) {
464             // CopyData expects one byte preserved for zero at the end. For utf16 it will preserve itself
465             --len;
466         }
467         CopyDataMUtf8(out.data(), len, false);
468         return out;
469     }
470 
GetUtf8()471     PandaString GetUtf8()
472     {
473         size_t len = GetUtf8Length();
474         PandaString out;
475         out.resize(len);
476         CopyDataRegionUtf8(out.data(), 0, GetCoreType()->GetLength(), len);
477         return out;
478     }
479 
GetDataUtf8()480     uint8_t *GetDataUtf8()
481     {
482         return GetCoreType()->GetDataUtf8();
483     }
484 
GetDataMUtf8()485     uint8_t *GetDataMUtf8()
486     {
487         return GetCoreType()->GetDataMUtf8();
488     }
489 
GetDataUtf16()490     uint16_t *GetDataUtf16()
491     {
492         return GetCoreType()->GetDataUtf16();
493     }
494 
IsEmpty()495     bool IsEmpty()
496     {
497         return GetCoreType()->IsEmpty();
498     }
499 
StringsAreEqual(EtsObject * obj)500     bool StringsAreEqual(EtsObject *obj)
501     {
502         return coretypes::String::StringsAreEqual(GetCoreType(), FromEtsObject(obj)->GetCoreType());
503     }
504 
GetCoreType()505     coretypes::String *GetCoreType()
506     {
507         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
508         return reinterpret_cast<coretypes::String *>(this);
509     }
510 
AsObjectHeader()511     ObjectHeader *AsObjectHeader()
512     {
513         return reinterpret_cast<ObjectHeader *>(this);
514     }
515 
AsObject()516     EtsObject *AsObject()
517     {
518         return reinterpret_cast<EtsObject *>(this);
519     }
520 
AsObject()521     const EtsObject *AsObject() const
522     {
523         return reinterpret_cast<const EtsObject *>(this);
524     }
525 
FromCoreType(coretypes::String * str)526     static EtsString *FromCoreType(coretypes::String *str)
527     {
528         return reinterpret_cast<EtsString *>(str);
529     }
530 
FromEtsObject(EtsObject * obj)531     static EtsString *FromEtsObject(EtsObject *obj)
532     {
533         ASSERT(obj->GetClass()->GetRuntimeClass() == Runtime::GetCurrent()
534                                                          ->GetClassLinker()
535                                                          ->GetExtension(panda_file::SourceLang::ETS)
536                                                          ->GetClassRoot(ClassRoot::STRING));
537         return reinterpret_cast<EtsString *>(obj);
538     }
539 
540     EtsString() = delete;
541     ~EtsString() = delete;
542 
543     NO_COPY_SEMANTIC(EtsString);
544     NO_MOVE_SEMANTIC(EtsString);
545 
546 private:
547     friend EtsString *StringBuilderToString(ObjectHeader *sb);
548 
AllocateNonInitializedString(uint32_t length,bool compressed)549     static EtsString *AllocateNonInitializedString(uint32_t length, bool compressed)
550     {
551         ASSERT(length != 0);
552         ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
553         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
554         return reinterpret_cast<EtsString *>(
555             coretypes::String::AllocStringObject(length, compressed, ctx, Runtime::GetCurrent()->GetPandaVM()));
556     }
557 
CodeToChar(double code)558     static uint16_t CodeToChar(double code)
559     {
560         constexpr double UTF16_CHAR_DIVIDER = 0x10000;
561         return static_cast<uint16_t>(static_cast<int64_t>(std::fmod(code, UTF16_CHAR_DIVIDER)));
562     }
563 };
564 
565 }  // namespace ark::ets
566 
567 #endif  // PANDA_PLUGINS_ETS_RUNTIME_FFI_CLASSES_ETS_STRING_H_
568