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 #ifndef ECMASCRIPT_STRING_H 17 #define ECMASCRIPT_STRING_H 18 19 #include <cstddef> 20 #include <cstdint> 21 #include <cstring> 22 23 #include "ecmascript/base/utf_helper.h" 24 #include "ecmascript/common.h" 25 #include "ecmascript/ecma_macros.h" 26 #include "ecmascript/js_hclass.h" 27 #include "ecmascript/js_tagged_value.h" 28 #include "ecmascript/mem/barriers.h" 29 #include "ecmascript/mem/space.h" 30 #include "ecmascript/mem/tagged_object.h" 31 32 #include "libpandabase/macros.h" 33 #include "securec.h" 34 #include "unicode/locid.h" 35 36 namespace panda { 37 namespace ecmascript { 38 template<typename T> 39 class JSHandle; 40 class JSPandaFile; 41 class EcmaVM; 42 class LineEcmaString; 43 class ConstantString; 44 class TreeEcmaString; 45 46 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 47 #define ECMA_STRING_CHECK_LENGTH_AND_TRHOW(vm, length) \ 48 if ((length) >= MAX_STRING_LENGTH) { \ 49 THROW_RANGE_ERROR_AND_RETURN((vm)->GetJSThread(), "Invalid string length", nullptr); \ 50 } 51 52 class EcmaString : public TaggedObject { 53 public: 54 CAST_CHECK(EcmaString, IsString); 55 56 static constexpr uint32_t STRING_COMPRESSED_BIT = 0x1; 57 static constexpr uint32_t STRING_INTERN_BIT = 0x2; 58 static constexpr size_t MAX_STRING_LENGTH = 0x40000000U; // 30 bits for string length, 2 bits for special meaning 59 60 static constexpr size_t MIX_LENGTH_OFFSET = TaggedObjectSize(); 61 // In last bit of mix_length we store if this string is compressed or not. 62 ACCESSORS_PRIMITIVE_FIELD(MixLength, uint32_t, MIX_LENGTH_OFFSET, HASHCODE_OFFSET) 63 ACCESSORS_PRIMITIVE_FIELD(RawHashcode, uint32_t, HASHCODE_OFFSET, SIZE) 64 65 enum CompressedStatus { 66 STRING_COMPRESSED, 67 STRING_UNCOMPRESSED, 68 }; 69 70 enum TrimMode : uint8_t { 71 TRIM, 72 TRIM_START, 73 TRIM_END, 74 }; 75 76 private: 77 friend class EcmaStringAccessor; 78 friend class LineEcmaString; 79 friend class ConstantString; 80 friend class TreeEcmaString; 81 friend class NameDictionary; 82 83 static constexpr int SMALL_STRING_SIZE = 128; 84 85 static EcmaString *CreateEmptyString(const EcmaVM *vm); 86 static EcmaString *CreateFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, 87 bool canBeCompress, MemSpaceType type = MemSpaceType::SEMI_SPACE, bool isConstantString = false, 88 uint32_t idOffset = 0); 89 static EcmaString *CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, 90 bool canBeCompress, MemSpaceType type = MemSpaceType::SEMI_SPACE); 91 static EcmaString *CreateLineString(const EcmaVM *vm, size_t length, bool compressed); 92 static EcmaString *CreateLineStringNoGC(const EcmaVM *vm, size_t length, bool compressed); 93 static EcmaString *CreateLineStringWithSpaceType(const EcmaVM *vm, 94 size_t length, bool compressed, MemSpaceType type); 95 static EcmaString *CreateTreeString(const EcmaVM *vm, 96 const JSHandle<EcmaString> &left, const JSHandle<EcmaString> &right, uint32_t length, bool compressed); 97 static EcmaString *CreateConstantString(const EcmaVM *vm, const uint8_t *utf8Data, 98 size_t length, bool compressed, MemSpaceType type = MemSpaceType::SEMI_SPACE, uint32_t idOffset = 0); 99 static EcmaString *Concat(const EcmaVM *vm, const JSHandle<EcmaString> &left, 100 const JSHandle<EcmaString> &right, MemSpaceType type = MemSpaceType::SEMI_SPACE); 101 static EcmaString *CopyStringToOldSpace(const EcmaVM *vm, const JSHandle<EcmaString> &original, 102 uint32_t length, bool compressed); 103 static EcmaString *FastSubString(const EcmaVM *vm, 104 const JSHandle<EcmaString> &src, uint32_t start, uint32_t length); 105 // require src is LineString 106 // not change src data structure 107 static inline EcmaString *FastSubUtf8String(const EcmaVM *vm, 108 const JSHandle<EcmaString> &src, uint32_t start, uint32_t length); 109 // require src is LineString 110 // not change src data structure 111 static inline EcmaString *FastSubUtf16String(const EcmaVM *vm, 112 const JSHandle<EcmaString> &src, uint32_t start, uint32_t length); 113 IsUtf8()114 bool IsUtf8() const 115 { 116 return (GetMixLength() & STRING_COMPRESSED_BIT) == STRING_COMPRESSED; 117 } 118 IsUtf16()119 bool IsUtf16() const 120 { 121 return (GetMixLength() & STRING_COMPRESSED_BIT) == STRING_UNCOMPRESSED; 122 } 123 124 // require is LineString 125 inline uint16_t *GetData() const; 126 inline const uint8_t *GetDataUtf8() const; 127 inline const uint16_t *GetDataUtf16() const; 128 129 // require is LineString 130 inline uint8_t *GetDataUtf8Writable(); 131 inline uint16_t *GetDataUtf16Writable(); 132 GetLength()133 uint32_t GetLength() const 134 { 135 return GetMixLength() >> 2U; 136 } 137 138 void SetLength(uint32_t length, bool compressed = false) 139 { 140 ASSERT(length < MAX_STRING_LENGTH); 141 // Use 0u for compressed/utf8 expression 142 SetMixLength((length << 2U) | (compressed ? STRING_COMPRESSED : STRING_UNCOMPRESSED)); 143 } 144 145 inline size_t GetUtf8Length(bool modify = true) const; 146 SetIsInternString()147 void SetIsInternString() 148 { 149 SetMixLength(GetMixLength() | STRING_INTERN_BIT); 150 } 151 IsInternString()152 bool IsInternString() const 153 { 154 return (GetMixLength() & STRING_INTERN_BIT) != 0; 155 } 156 ClearInternStringFlag()157 void ClearInternStringFlag() 158 { 159 SetMixLength(GetMixLength() & ~STRING_INTERN_BIT); 160 } 161 TryGetHashCode(uint32_t * hash)162 bool TryGetHashCode(uint32_t *hash) 163 { 164 uint32_t hashcode = GetRawHashcode(); 165 if (hashcode == 0 && GetLength() != 0) { 166 return false; 167 } 168 *hash = hashcode; 169 return true; 170 } 171 172 // not change this data structure. 173 // if string is not flat, this func has low efficiency. GetHashcode()174 uint32_t PUBLIC_API GetHashcode() 175 { 176 uint32_t hashcode = GetRawHashcode(); 177 // GetLength() == 0 means it's an empty array.No need to computeHashCode again when hashseed is 0. 178 if (hashcode == 0 && GetLength() != 0) { 179 hashcode = ComputeHashcode(0); 180 SetRawHashcode(hashcode); 181 } 182 return hashcode; 183 } 184 185 // not change this data structure. 186 // if string is not flat, this func has low efficiency. 187 uint32_t PUBLIC_API ComputeHashcode(uint32_t hashSeed) const; 188 189 static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress); 190 static uint32_t ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length); 191 192 template<bool verify = true> 193 uint16_t At(int32_t index) const; 194 195 // require is LineString 196 void WriteData(uint32_t index, uint16_t src); 197 198 // can change left and right data structure 199 static int32_t Compare(const EcmaVM *vm, const JSHandle<EcmaString> &left, const JSHandle<EcmaString> &right); 200 201 // Check that two spans are equal. Should have the same length. 202 /* static */ 203 template<typename T> StringsAreEquals(Span<const T> & str1,Span<const T> & str2)204 static bool StringsAreEquals(Span<const T> &str1, Span<const T> &str2) 205 { 206 ASSERT(str1.Size() <= str2.Size()); 207 size_t size = str1.Size(); 208 if (size < SMALL_STRING_SIZE) { 209 for (size_t i = 0; i < size; i++) { 210 if (str1[i] != str2[i]) { 211 return false; 212 } 213 } 214 return true; 215 } 216 return memcmp(str1.data(), str2.data(), size * sizeof(T)) == 0; 217 } 218 219 // Converts utf8Data to utf16 and compare it with given utf16_data. 220 static bool IsUtf8EqualsUtf16(const uint8_t *utf8Data, size_t utf8Len, const uint16_t *utf16Data, 221 uint32_t utf16Len); 222 // Compares string1 + string2 by bytes, It doesn't check canonical unicode equivalence. 223 bool EqualToSplicedString(const EcmaString *str1, const EcmaString *str2); 224 // Compares strings by bytes, It doesn't check canonical unicode equivalence. 225 static bool StringsAreEqual(const EcmaVM *vm, const JSHandle<EcmaString> &str1, const JSHandle<EcmaString> &str2); 226 // Compares strings by bytes, It doesn't check canonical unicode equivalence. 227 static bool StringsAreEqual(EcmaString *str1, EcmaString *str2); 228 // Two strings have the same type of utf encoding format. 229 static bool StringsAreEqualSameUtfEncoding(EcmaString *str1, EcmaString *str2); 230 // Compares strings by bytes, It doesn't check canonical unicode equivalence. 231 // not change str1 data structure. 232 // if str1 is not flat, this func has low efficiency. 233 static bool StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len, 234 bool canBeCompress); 235 // Compares strings by bytes, It doesn't check canonical unicode equivalence. 236 // not change str1 data structure. 237 // if str1 is not flat, this func has low efficiency. 238 static bool StringsAreEqualUtf16(const EcmaString *str1, const uint16_t *utf16Data, uint32_t utf16Len); 239 240 // can change receiver and search data structure 241 static int32_t IndexOf(const EcmaVM *vm, 242 const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0); 243 244 // can change receiver and search data structure 245 static int32_t LastIndexOf(const EcmaVM *vm, 246 const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0); 247 248 inline size_t CopyDataUtf8(uint8_t *buf, size_t maxLength, bool modify = true) const 249 { 250 if (maxLength == 0) { 251 return 1; // maxLength was -1 at napi 252 } 253 size_t length = GetLength(); 254 if (length > maxLength) { 255 return 0; 256 } 257 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 258 buf[maxLength - 1] = '\0'; 259 // Put comparison here so that internal usage and napi can use the same CopyDataRegionUtf8 260 return CopyDataRegionUtf8(buf, 0, length, maxLength, modify) + 1; // add place for zero in the end 261 } 262 263 // It allows user to copy into buffer even if maxLength < length 264 inline size_t WriteUtf8(uint8_t *buf, size_t maxLength, bool isWriteBuffer = false) const 265 { 266 if (maxLength == 0) { 267 return 1; // maxLength was -1 at napi 268 } 269 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 270 buf[maxLength - 1] = '\0'; 271 return CopyDataRegionUtf8(buf, 0, GetLength(), maxLength, true, isWriteBuffer) + 1; 272 } 273 CopyDataToUtf16(uint16_t * buf,uint32_t length,uint32_t bufLength)274 size_t CopyDataToUtf16(uint16_t *buf, uint32_t length, uint32_t bufLength) const 275 { 276 if (IsUtf16()) { 277 CVector<uint16_t> tmpBuf; 278 const uint16_t *data = EcmaString::GetUtf16DataFlat(this, tmpBuf); 279 if (length > bufLength) { 280 if (memcpy_s(buf, bufLength * sizeof(uint16_t), data, bufLength * sizeof(uint16_t)) != EOK) { 281 LOG_FULL(FATAL) << "memcpy_s failed when length > bufLength"; 282 UNREACHABLE(); 283 } 284 return bufLength; 285 } 286 if (memcpy_s(buf, bufLength * sizeof(uint16_t), data, length * sizeof(uint16_t)) != EOK) { 287 LOG_FULL(FATAL) << "memcpy_s failed"; 288 UNREACHABLE(); 289 } 290 return length; 291 } 292 CVector<uint8_t> tmpBuf; 293 const uint8_t *data = EcmaString::GetUtf8DataFlat(this, tmpBuf); 294 if (length > bufLength) { 295 return base::utf_helper::ConvertRegionUtf8ToUtf16(data, buf, bufLength, bufLength, 0); 296 } 297 return base::utf_helper::ConvertRegionUtf8ToUtf16(data, buf, length, bufLength, 0); 298 } 299 300 // It allows user to copy into buffer even if maxLength < length WriteUtf16(uint16_t * buf,uint32_t targetLength,uint32_t bufLength)301 inline size_t WriteUtf16(uint16_t *buf, uint32_t targetLength, uint32_t bufLength) const 302 { 303 if (bufLength == 0) { 304 return 0; 305 } 306 // Returns a number representing a valid backrest length. 307 return CopyDataToUtf16(buf, targetLength, bufLength); 308 } 309 WriteOneByte(uint8_t * buf,size_t maxLength)310 size_t WriteOneByte(uint8_t *buf, size_t maxLength) const 311 { 312 if (maxLength == 0) { 313 return 0; 314 } 315 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 316 buf[maxLength - 1] = '\0'; 317 uint32_t length = GetLength(); 318 if (!IsUtf16()) { 319 CVector<uint8_t> tmpBuf; 320 const uint8_t *data = GetUtf8DataFlat(this, tmpBuf); 321 if (length > maxLength) { 322 length = maxLength; 323 } 324 if (memcpy_s(buf, maxLength, data, length) != EOK) { 325 LOG_FULL(FATAL) << "memcpy_s failed when write one byte"; 326 UNREACHABLE(); 327 } 328 return length; 329 } 330 331 CVector<uint16_t> tmpBuf; 332 const uint16_t *data = GetUtf16DataFlat(this, tmpBuf); 333 if (length > maxLength) { 334 return base::utf_helper::ConvertRegionUtf16ToLatin1(data, buf, maxLength, maxLength); 335 } 336 return base::utf_helper::ConvertRegionUtf16ToLatin1(data, buf, length, maxLength); 337 } 338 339 size_t CopyDataRegionUtf8(uint8_t *buf, size_t start, size_t length, size_t maxLength, 340 bool modify = true, bool isWriteBuffer = false) const 341 { 342 uint32_t len = GetLength(); 343 if (start + length > len) { 344 return 0; 345 } 346 if (!IsUtf16()) { 347 if (length > std::numeric_limits<size_t>::max() / 2 - 1) { // 2: half 348 LOG_FULL(FATAL) << " length is higher than half of size_t::max"; 349 UNREACHABLE(); 350 } 351 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 352 // Only memcpy_s maxLength number of chars into buffer if length > maxLength 353 CVector<uint8_t> tmpBuf; 354 const uint8_t *data = GetUtf8DataFlat(this, tmpBuf); 355 if (length > maxLength) { 356 if (memcpy_s(buf, maxLength, data + start, maxLength) != EOK) { 357 LOG_FULL(FATAL) << "memcpy_s failed when length > maxlength"; 358 UNREACHABLE(); 359 } 360 return maxLength; 361 } 362 if (memcpy_s(buf, maxLength, data + start, length) != EOK) { 363 LOG_FULL(FATAL) << "memcpy_s failed when length <= maxlength"; 364 UNREACHABLE(); 365 } 366 return length; 367 } 368 CVector<uint16_t> tmpBuf; 369 const uint16_t *data = GetUtf16DataFlat(this, tmpBuf); 370 if (length > maxLength) { 371 return base::utf_helper::ConvertRegionUtf16ToUtf8(data, buf, maxLength, maxLength, start, 372 modify, isWriteBuffer); 373 } 374 return base::utf_helper::ConvertRegionUtf16ToUtf8(data, buf, length, maxLength, start, 375 modify, isWriteBuffer); 376 } 377 CopyDataUtf16(uint16_t * buf,uint32_t maxLength)378 inline uint32_t CopyDataUtf16(uint16_t *buf, uint32_t maxLength) const 379 { 380 return CopyDataRegionUtf16(buf, 0, GetLength(), maxLength); 381 } 382 CopyDataRegionUtf16(uint16_t * buf,uint32_t start,uint32_t length,uint32_t maxLength)383 uint32_t CopyDataRegionUtf16(uint16_t *buf, uint32_t start, uint32_t length, uint32_t maxLength) const 384 { 385 if (length > maxLength) { 386 return 0; 387 } 388 uint32_t len = GetLength(); 389 if (start + length > len) { 390 return 0; 391 } 392 if (IsUtf16()) { 393 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 394 CVector<uint16_t> tmpBuf; 395 const uint16_t *data = EcmaString::GetUtf16DataFlat(this, tmpBuf); 396 if (memcpy_s(buf, maxLength * sizeof(uint16_t), data + start, length * sizeof(uint16_t)) != EOK) { 397 LOG_FULL(FATAL) << "memcpy_s failed"; 398 UNREACHABLE(); 399 } 400 return length; 401 } 402 CVector<uint8_t> tmpBuf; 403 const uint8_t *data = EcmaString::GetUtf8DataFlat(this, tmpBuf); 404 return base::utf_helper::ConvertRegionUtf8ToUtf16(data, buf, len, maxLength, start); 405 } 406 407 std::u16string ToU16String(uint32_t len = 0); 408 ToOneByteDataForced()409 std::unique_ptr<uint8_t[]> ToOneByteDataForced() 410 { 411 uint8_t *buf = nullptr; 412 auto length = GetLength(); 413 if (IsUtf16()) { 414 auto size = length * sizeof(uint16_t); 415 buf = new uint8_t[size](); 416 CopyDataUtf16(reinterpret_cast<uint16_t *>(buf), length); 417 } else { 418 buf = new uint8_t[length + 1](); 419 CopyDataUtf8(buf, length + 1); 420 } 421 return std::unique_ptr<uint8_t[]>(buf); 422 } 423 424 Span<const uint8_t> ToUtf8Span(CVector<uint8_t> &buf, bool modify = true) 425 { 426 Span<const uint8_t> str; 427 uint32_t strLen = GetLength(); 428 if (UNLIKELY(IsUtf16())) { 429 CVector<uint16_t> tmpBuf; 430 const uint16_t *data = EcmaString::GetUtf16DataFlat(this, tmpBuf); 431 size_t len = base::utf_helper::Utf16ToUtf8Size(data, strLen, modify) - 1; 432 buf.reserve(len); 433 len = base::utf_helper::ConvertRegionUtf16ToUtf8(data, buf.data(), strLen, len, 0, modify); 434 str = Span<const uint8_t>(buf.data(), len); 435 } else { 436 const uint8_t *data = EcmaString::GetUtf8DataFlat(this, buf); 437 str = Span<const uint8_t>(data, strLen); 438 } 439 return str; 440 } 441 442 void WriteData(EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length); 443 444 static bool CanBeCompressed(const uint8_t *utf8Data, uint32_t utf8Len); 445 static bool CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len); 446 static bool CanBeCompressed(const EcmaString *string); 447 448 bool ToElementIndex(uint32_t *index); 449 450 bool ToTypedArrayIndex(uint32_t *index); 451 452 template<bool isLower> 453 static EcmaString *ConvertCase(const EcmaVM *vm, const JSHandle<EcmaString> &src); 454 455 template<bool isLower> 456 static EcmaString *LocaleConvertCase(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale); 457 458 template<typename T> 459 static EcmaString *TrimBody(const JSThread *thread, const JSHandle<EcmaString> &src, Span<T> &data, TrimMode mode); 460 461 static EcmaString *Trim(const JSThread *thread, const JSHandle<EcmaString> &src, TrimMode mode = TrimMode::TRIM); 462 463 // single char copy for loop 464 template<typename DstType, typename SrcType> CopyChars(DstType * dst,SrcType * src,uint32_t count)465 static void CopyChars(DstType *dst, SrcType *src, uint32_t count) 466 { 467 Span<SrcType> srcSp(src, count); 468 Span<DstType> dstSp(dst, count); 469 for (uint32_t i = 0; i < count; i++) { 470 dstSp[i] = srcSp[i]; 471 } 472 } 473 474 // memory block copy 475 template<typename T> 476 static bool MemCopyChars(Span<T> &dst, size_t dstMax, Span<const T> &src, size_t count); 477 478 template<typename T> ComputeHashForData(const T * data,size_t size,uint32_t hashSeed)479 static uint32_t ComputeHashForData(const T *data, size_t size, uint32_t hashSeed) 480 { 481 uint32_t hash = hashSeed; 482 Span<const T> sp(data, size); 483 for (auto c : sp) { 484 constexpr size_t SHIFT = 5; 485 hash = (hash << SHIFT) - hash + c; 486 } 487 return hash; 488 } 489 IsASCIICharacter(uint16_t data)490 static bool IsASCIICharacter(uint16_t data) 491 { 492 // \0 is not considered ASCII in Ecma-Modified-UTF8 [only modify '\u0000'] 493 return data - 1U < base::utf_helper::UTF8_1B_MAX; 494 } 495 496 template<typename T1, typename T2> 497 static int32_t IndexOf(Span<const T1> &lhsSp, Span<const T2> &rhsSp, int32_t pos, int32_t max); 498 499 template<typename T1, typename T2> 500 static int32_t LastIndexOf(Span<const T1> &lhsSp, Span<const T2> &rhsSp, int32_t pos); 501 502 bool IsFlat() const; 503 IsLineString()504 bool IsLineString() const 505 { 506 return GetClass()->IsLineString(); 507 } IsConstantString()508 bool IsConstantString() const 509 { 510 return GetClass()->IsConstantString(); 511 } IsTreeString()512 bool IsTreeString() const 513 { 514 return GetClass()->IsTreeString(); 515 } IsLineOrConstantString()516 bool IsLineOrConstantString() const 517 { 518 auto hclass = GetClass(); 519 return hclass->IsLineString() || hclass->IsConstantString(); 520 } 521 GetStringType()522 JSType GetStringType() const 523 { 524 JSType type = GetClass()->GetObjectType(); 525 ASSERT(type >= JSType::STRING_FIRST && type <= JSType::STRING_LAST); 526 return type; 527 } 528 529 template <typename Char> 530 static void WriteToFlat(EcmaString *src, Char *buf, uint32_t maxLength); 531 532 static const uint8_t *GetUtf8DataFlat(const EcmaString *src, CVector<uint8_t> &buf); 533 534 static const uint16_t *GetUtf16DataFlat(const EcmaString *src, CVector<uint16_t> &buf); 535 536 // string must be not flat 537 static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle<TreeEcmaString> &string, MemSpaceType type); 538 539 static EcmaString *Flatten(const EcmaVM *vm, const JSHandle<EcmaString> &string, 540 MemSpaceType type = MemSpaceType::SEMI_SPACE); 541 542 static EcmaString *FlattenNoGC(const EcmaVM *vm, EcmaString *string); 543 544 static EcmaString *ToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src); 545 546 static EcmaString *ToUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src); 547 548 static EcmaString *ToLocaleLower(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale); 549 550 static EcmaString *ToLocaleUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale); 551 552 static EcmaString *TryToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src); 553 554 static EcmaString *ConvertUtf8ToLowerOrUpper(const EcmaVM *vm, const JSHandle<EcmaString> &srcFlat, 555 bool toLower, uint32_t startIndex = 0); 556 }; 557 558 // The LineEcmaString abstract class captures sequential string values, only LineEcmaString can store chars data 559 class LineEcmaString : public EcmaString { 560 public: 561 // DATA_OFFSET: the string data stored after the string header. 562 // Data can be stored in utf8 or utf16 form according to compressed bit. 563 static constexpr size_t DATA_OFFSET = EcmaString::SIZE; // DATA_OFFSET equal to Empty String size 564 565 CAST_CHECK(LineEcmaString, IsLineString); 566 Cast(EcmaString * str)567 static LineEcmaString *Cast(EcmaString *str) 568 { 569 return static_cast<LineEcmaString *>(str); 570 } 571 Cast(const EcmaString * str)572 static LineEcmaString *Cast(const EcmaString *str) 573 { 574 return LineEcmaString::Cast(const_cast<EcmaString *>(str)); 575 } 576 ComputeSizeUtf8(uint32_t utf8Len)577 static size_t ComputeSizeUtf8(uint32_t utf8Len) 578 { 579 return DATA_OFFSET + utf8Len; 580 } 581 ComputeSizeUtf16(uint32_t utf16Len)582 static size_t ComputeSizeUtf16(uint32_t utf16Len) 583 { 584 return DATA_OFFSET + utf16Len * sizeof(uint16_t); 585 } 586 ObjectSize(EcmaString * str)587 static size_t ObjectSize(EcmaString *str) 588 { 589 uint32_t length = str->GetLength(); 590 return str->IsUtf16() ? ComputeSizeUtf16(length) : ComputeSizeUtf8(length); 591 } 592 GetData()593 uint16_t *GetData() const 594 { 595 return reinterpret_cast<uint16_t *>(ToUintPtr(this) + DATA_OFFSET); 596 } 597 598 template<bool verify = true> Get(int32_t index)599 uint16_t Get(int32_t index) const 600 { 601 int32_t length = static_cast<int32_t>(GetLength()); 602 if (verify) { 603 if ((index < 0) || (index >= length)) { 604 return 0; 605 } 606 } 607 if (!IsUtf16()) { 608 Span<const uint8_t> sp(GetDataUtf8(), length); 609 return sp[index]; 610 } 611 Span<const uint16_t> sp(GetDataUtf16(), length); 612 return sp[index]; 613 } 614 Set(uint32_t index,uint16_t src)615 void Set(uint32_t index, uint16_t src) 616 { 617 ASSERT(index < GetLength()); 618 if (IsUtf8()) { 619 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 620 *(reinterpret_cast<uint8_t *>(GetData()) + index) = static_cast<uint8_t>(src); 621 } else { 622 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 623 *(GetData() + index) = src; 624 } 625 } 626 }; 627 static_assert((LineEcmaString::DATA_OFFSET % static_cast<uint8_t>(MemAlignment::MEM_ALIGN_OBJECT)) == 0); 628 629 class ConstantString : public EcmaString { 630 public: 631 static constexpr size_t ENTITY_ID_OFFSET = EcmaString::SIZE; 632 // ConstantData is the pointer of const string in the pandafile. 633 // String in pandafile is encoded by the utf8 format. 634 ACCESSORS_PRIMITIVE_FIELD(EntityId, uint32_t, ENTITY_ID_OFFSET, CONSTANT_DATA_OFFSET); 635 ACCESSORS_NATIVE_FIELD(ConstantData, uint8_t, CONSTANT_DATA_OFFSET, SIZE); 636 637 CAST_CHECK(ConstantString, IsConstantString); 638 Cast(EcmaString * str)639 static ConstantString *Cast(EcmaString *str) 640 { 641 return static_cast<ConstantString *>(str); 642 } 643 Cast(const EcmaString * str)644 static ConstantString *Cast(const EcmaString *str) 645 { 646 return ConstantString::Cast(const_cast<EcmaString *>(str)); 647 } 648 ObjectSize()649 static size_t ObjectSize() 650 { 651 return ConstantString::SIZE; 652 } 653 654 template<bool verify = true> Get(int32_t index)655 uint16_t Get(int32_t index) const 656 { 657 int32_t length = static_cast<int32_t>(GetLength()); 658 if (verify) { 659 if ((index < 0) || (index >= length)) { 660 return 0; 661 } 662 } 663 ASSERT(IsUtf8()); 664 Span<const uint8_t> sp(GetConstantData(), length); 665 return sp[index]; 666 } 667 }; 668 669 class TreeEcmaString : public EcmaString { 670 public: 671 // Minimum length for a tree string 672 static constexpr uint32_t MIN_TREE_ECMASTRING_LENGTH = 13; 673 674 static constexpr size_t FIRST_OFFSET = EcmaString::SIZE; 675 ACCESSORS(First, FIRST_OFFSET, SECOND_OFFSET); 676 ACCESSORS(Second, SECOND_OFFSET, SIZE); 677 678 DECL_VISIT_OBJECT(FIRST_OFFSET, SIZE); 679 680 CAST_CHECK(TreeEcmaString, IsTreeString); 681 Cast(EcmaString * str)682 static TreeEcmaString *Cast(EcmaString *str) 683 { 684 return static_cast<TreeEcmaString *>(str); 685 } 686 Cast(const EcmaString * str)687 static TreeEcmaString *Cast(const EcmaString *str) 688 { 689 return TreeEcmaString::Cast(const_cast<EcmaString *>(str)); 690 } 691 IsFlat()692 bool IsFlat() const 693 { 694 auto strSecond = EcmaString::Cast(GetSecond()); 695 return strSecond->GetLength() == 0; 696 } 697 698 template<bool verify = true> Get(int32_t index)699 uint16_t Get(int32_t index) const 700 { 701 int32_t length = static_cast<int32_t>(GetLength()); 702 if (verify) { 703 if ((index < 0) || (index >= length)) { 704 return 0; 705 } 706 } 707 708 if (IsFlat()) { 709 EcmaString *first = EcmaString::Cast(GetFirst()); 710 return first->At<verify>(index); 711 } 712 EcmaString *string = const_cast<TreeEcmaString *>(this); 713 while (true) { 714 if (string->IsTreeString()) { 715 EcmaString *first = EcmaString::Cast(TreeEcmaString::Cast(string)->GetFirst()); 716 if (static_cast<int32_t>(first->GetLength()) > index) { 717 string = first; 718 } else { 719 index -= static_cast<int32_t>(first->GetLength()); 720 string = EcmaString::Cast(TreeEcmaString::Cast(string)->GetSecond()); 721 } 722 } else { 723 return string->At<verify>(index); 724 } 725 } 726 UNREACHABLE(); 727 } 728 }; 729 730 // if you want to use functions of EcmaString, please not use directly, 731 // and use functions of EcmaStringAccessor alternatively. 732 // eg: EcmaString *str = ***; str->GetLength() -----> EcmaStringAccessor(str).GetLength() 733 class PUBLIC_API EcmaStringAccessor { 734 public: 735 explicit EcmaStringAccessor(EcmaString *string); 736 737 explicit EcmaStringAccessor(TaggedObject *obj); 738 739 explicit EcmaStringAccessor(JSTaggedValue value); 740 741 explicit EcmaStringAccessor(const JSHandle<EcmaString> &strHandle); 742 CreateLineString(const EcmaVM * vm,size_t length,bool compressed)743 static EcmaString *CreateLineString(const EcmaVM *vm, size_t length, bool compressed) 744 { 745 return EcmaString::CreateLineString(vm, length, compressed); 746 } 747 CreateEmptyString(const EcmaVM * vm)748 static EcmaString *CreateEmptyString(const EcmaVM *vm) 749 { 750 return EcmaString::CreateEmptyString(vm); 751 } 752 753 static EcmaString *CreateFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, 754 MemSpaceType type = MemSpaceType::SEMI_SPACE, bool isConstantString = false, 755 uint32_t idOffset = 0) 756 { 757 return EcmaString::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type, isConstantString, idOffset); 758 } 759 760 static EcmaString *CreateConstantString(const EcmaVM *vm, const uint8_t *utf8Data, size_t length, 761 bool compressed, MemSpaceType type = MemSpaceType::SEMI_SPACE, uint32_t idOffset = 0) 762 { 763 return EcmaString::CreateConstantString(vm, utf8Data, length, compressed, type, idOffset); 764 } 765 766 static EcmaString *CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, 767 bool canBeCompress, MemSpaceType type = MemSpaceType::SEMI_SPACE) 768 { 769 return EcmaString::CreateFromUtf16(vm, utf16Data, utf16Len, canBeCompress, type); 770 } 771 772 static EcmaString *Concat(const EcmaVM *vm, const JSHandle<EcmaString> &str1Handle, 773 const JSHandle<EcmaString> &str2Handle, MemSpaceType type = MemSpaceType::SEMI_SPACE) 774 { 775 return EcmaString::Concat(vm, str1Handle, str2Handle, type); 776 } 777 CopyStringToOldSpace(const EcmaVM * vm,const JSHandle<EcmaString> & original,uint32_t length,bool compressed)778 static EcmaString *CopyStringToOldSpace(const EcmaVM *vm, const JSHandle<EcmaString> &original, 779 uint32_t length, bool compressed) 780 { 781 return EcmaString::CopyStringToOldSpace(vm, original, length, compressed); 782 } 783 784 // can change src data structure FastSubString(const EcmaVM * vm,const JSHandle<EcmaString> & src,uint32_t start,uint32_t length)785 static EcmaString *FastSubString(const EcmaVM *vm, 786 const JSHandle<EcmaString> &src, uint32_t start, uint32_t length) 787 { 788 return EcmaString::FastSubString(vm, src, start, length); 789 } 790 IsUtf8()791 bool IsUtf8() const 792 { 793 return string_->IsUtf8(); 794 } 795 IsUtf16()796 bool IsUtf16() const 797 { 798 return string_->IsUtf16(); 799 } 800 GetLength()801 uint32_t GetLength() const 802 { 803 return string_->GetLength(); 804 } 805 806 // require is LineString 807 inline size_t GetUtf8Length() const; 808 ObjectSize()809 size_t ObjectSize() const 810 { 811 if (string_->IsLineString()) { 812 return LineEcmaString::ObjectSize(string_); 813 } if (string_->IsConstantString()) { 814 return ConstantString::ObjectSize(); 815 } else { 816 return TreeEcmaString::SIZE; 817 } 818 } 819 820 // For TreeString, the calculation result is size of LineString correspondingly. GetFlatStringSize()821 size_t GetFlatStringSize() const 822 { 823 if (string_->IsConstantString()) { 824 return ConstantString::ObjectSize(); 825 } 826 return LineEcmaString::ObjectSize(string_); 827 } 828 IsInternString()829 bool IsInternString() const 830 { 831 return string_->IsInternString(); 832 } 833 SetInternString()834 void SetInternString() 835 { 836 string_->SetIsInternString(); 837 } 838 ClearInternString()839 void ClearInternString() 840 { 841 string_->ClearInternStringFlag(); 842 } 843 844 // require is LineString 845 // It's Utf8 format, but without 0 in the end. 846 inline const uint8_t *GetDataUtf8(); 847 848 // require is LineString 849 inline const uint16_t *GetDataUtf16(); 850 851 // not change string data structure. 852 // if string is not flat, this func has low efficiency. 853 std::u16string ToU16String(uint32_t len = 0) 854 { 855 return string_->ToU16String(len); 856 } 857 858 // not change string data structure. 859 // if string is not flat, this func has low efficiency. ToOneByteDataForced()860 std::unique_ptr<uint8_t[]> ToOneByteDataForced() 861 { 862 return string_->ToOneByteDataForced(); 863 } 864 865 // not change string data structure. 866 // if string is not flat, this func has low efficiency. ToUtf8Span(CVector<uint8_t> & buf)867 Span<const uint8_t> ToUtf8Span(CVector<uint8_t> &buf) 868 { 869 return string_->ToUtf8Span(buf); 870 } 871 872 // not change string data structure. 873 // if string is not flat, this func has low efficiency. 874 std::string ToStdString(StringConvertedUsage usage = StringConvertedUsage::PRINT); 875 876 // not change string data structure. 877 // if string is not flat, this func has low efficiency. 878 CString ToCString(StringConvertedUsage usage = StringConvertedUsage::LOGICOPERATION); 879 880 // not change string data structure. 881 // if string is not flat, this func has low efficiency. 882 uint32_t WriteToFlatUtf8(uint8_t *buf, uint32_t maxLength, bool isWriteBuffer = false) 883 { 884 return string_->WriteUtf8(buf, maxLength, isWriteBuffer); 885 } 886 WriteToUtf16(uint16_t * buf,uint32_t bufLength)887 uint32_t WriteToUtf16(uint16_t *buf, uint32_t bufLength) 888 { 889 return string_->WriteUtf16(buf, GetLength(), bufLength); 890 } 891 WriteToOneByte(uint8_t * buf,uint32_t maxLength)892 uint32_t WriteToOneByte(uint8_t *buf, uint32_t maxLength) 893 { 894 return string_->WriteOneByte(buf, maxLength); 895 } 896 897 // not change string data structure. 898 // if string is not flat, this func has low efficiency. WriteToFlatUtf16(uint16_t * buf,uint32_t maxLength)899 uint32_t WriteToFlatUtf16(uint16_t *buf, uint32_t maxLength) const 900 { 901 return string_->CopyDataUtf16(buf, maxLength); 902 } 903 904 // require dst is LineString 905 // not change src data structure. 906 // if src is not flat, this func has low efficiency. 907 inline static void ReadData(EcmaString * dst, EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length); 908 909 // not change src data structure. 910 // if src is not flat, this func has low efficiency. 911 template<bool verify = true> Get(uint32_t index)912 uint16_t Get(uint32_t index) const 913 { 914 return string_->At<verify>(index); 915 } 916 917 // require string is LineString. Set(uint32_t index,uint16_t src)918 void Set(uint32_t index, uint16_t src) 919 { 920 return string_->WriteData(index, src); 921 } 922 923 // not change src data structure. 924 // if src is not flat, this func has low efficiency. GetHashcode()925 uint32_t GetHashcode() 926 { 927 return string_->GetHashcode(); 928 } 929 930 // not change src data structure. 931 // if src is not flat, this func has low efficiency. ComputeHashcode(uint32_t hashSeed)932 uint32_t ComputeHashcode(uint32_t hashSeed) 933 { 934 return string_->ComputeHashcode(hashSeed); 935 } 936 ComputeHashcodeUtf8(const uint8_t * utf8Data,size_t utf8Len,bool canBeCompress)937 static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress) 938 { 939 return EcmaString::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress); 940 } 941 ComputeHashcodeUtf16(const uint16_t * utf16Data,uint32_t length)942 static uint32_t ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length) 943 { 944 return EcmaString::ComputeHashcodeUtf16(utf16Data, length); 945 } 946 947 // can change receiver and search data structure 948 static int32_t IndexOf(const EcmaVM *vm, 949 const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0) 950 { 951 return EcmaString::IndexOf(vm, receiver, search, pos); 952 } 953 954 // can change receiver and search data structure 955 static int32_t LastIndexOf(const EcmaVM *vm, 956 const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0) 957 { 958 return EcmaString::LastIndexOf(vm, receiver, search, pos); 959 } 960 961 // can change receiver and search data structure Compare(const EcmaVM * vm,const JSHandle<EcmaString> & left,const JSHandle<EcmaString> & right)962 static int32_t Compare(const EcmaVM *vm, const JSHandle<EcmaString>& left, const JSHandle<EcmaString>& right) 963 { 964 return EcmaString::Compare(vm, left, right); 965 } 966 967 // can change str1 and str2 data structure StringsAreEqual(const EcmaVM * vm,const JSHandle<EcmaString> & str1,const JSHandle<EcmaString> & str2)968 static bool StringsAreEqual(const EcmaVM *vm, const JSHandle<EcmaString> &str1, const JSHandle<EcmaString> &str2) 969 { 970 return EcmaString::StringsAreEqual(vm, str1, str2); 971 } 972 973 // not change str1 and str2 data structure. 974 // if str1 or str2 is not flat, this func has low efficiency. StringsAreEqual(EcmaString * str1,EcmaString * str2)975 static bool StringsAreEqual(EcmaString *str1, EcmaString *str2) 976 { 977 return EcmaString::StringsAreEqual(str1, str2); 978 } 979 980 // not change str1 and str2 data structure. 981 // if str1 or str2 is not flat, this func has low efficiency. StringsAreEqualSameUtfEncoding(EcmaString * str1,EcmaString * str2)982 static bool StringsAreEqualSameUtfEncoding(EcmaString *str1, EcmaString *str2) 983 { 984 return EcmaString::StringsAreEqualSameUtfEncoding(str1, str2); 985 } 986 987 // not change str1 data structure. 988 // if str1 is not flat, this func has low efficiency. StringsAreEqualUtf8(const EcmaString * str1,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress)989 static bool StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len, 990 bool canBeCompress) 991 { 992 return EcmaString::StringsAreEqualUtf8(str1, utf8Data, utf8Len, canBeCompress); 993 } 994 995 // not change str1 data structure. 996 // if str1 is not flat, this func has low efficiency. StringsAreEqualUtf16(const EcmaString * str1,const uint16_t * utf16Data,uint32_t utf16Len)997 static bool StringsAreEqualUtf16(const EcmaString *str1, const uint16_t *utf16Data, uint32_t utf16Len) 998 { 999 return EcmaString::StringsAreEqualUtf16(str1, utf16Data, utf16Len); 1000 } 1001 1002 // require str1 and str2 are LineString. 1003 // not change string data structure. 1004 // if string is not flat, this func has low efficiency. EqualToSplicedString(const EcmaString * str1,const EcmaString * str2)1005 bool EqualToSplicedString(const EcmaString *str1, const EcmaString *str2) 1006 { 1007 return string_->EqualToSplicedString(str1, str2); 1008 } 1009 CanBeCompressed(const uint8_t * utf8Data,uint32_t utf8Len)1010 static bool CanBeCompressed(const uint8_t *utf8Data, uint32_t utf8Len) 1011 { 1012 return EcmaString::CanBeCompressed(utf8Data, utf8Len); 1013 } 1014 CanBeCompressed(const uint16_t * utf16Data,uint32_t utf16Len)1015 static bool CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len) 1016 { 1017 return EcmaString::CanBeCompressed(utf16Data, utf16Len); 1018 } 1019 1020 // require string is LineString CanBeCompressed(const EcmaString * string)1021 static bool CanBeCompressed(const EcmaString *string) 1022 { 1023 return EcmaString::CanBeCompressed(string); 1024 } 1025 1026 // not change string data structure. 1027 // if string is not flat, this func has low efficiency. ToElementIndex(uint32_t * index)1028 bool ToElementIndex(uint32_t *index) 1029 { 1030 return string_->ToElementIndex(index); 1031 } 1032 1033 // not change string data structure. 1034 // if string is not flat, this func has low efficiency. ToTypedArrayIndex(uint32_t * index)1035 bool ToTypedArrayIndex(uint32_t *index) 1036 { 1037 return string_->ToTypedArrayIndex(index); 1038 } 1039 ToLower(const EcmaVM * vm,const JSHandle<EcmaString> & src)1040 static EcmaString *ToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src) 1041 { 1042 return EcmaString::ToLower(vm, src); 1043 } 1044 TryToLower(const EcmaVM * vm,const JSHandle<EcmaString> & src)1045 static EcmaString *TryToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src) 1046 { 1047 return EcmaString::TryToLower(vm, src); 1048 } 1049 ToUpper(const EcmaVM * vm,const JSHandle<EcmaString> & src)1050 static EcmaString *ToUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src) 1051 { 1052 return EcmaString::ToUpper(vm, src); 1053 } 1054 ToLocaleLower(const EcmaVM * vm,const JSHandle<EcmaString> & src,const icu::Locale & locale)1055 static EcmaString *ToLocaleLower(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale) 1056 { 1057 return EcmaString::ToLocaleLower(vm, src, locale); 1058 } 1059 ToLocaleUpper(const EcmaVM * vm,const JSHandle<EcmaString> & src,const icu::Locale & locale)1060 static EcmaString *ToLocaleUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale) 1061 { 1062 return EcmaString::ToLocaleUpper(vm, src, locale); 1063 } 1064 1065 static EcmaString *Trim(const JSThread *thread, 1066 const JSHandle<EcmaString> &src, EcmaString::TrimMode mode = EcmaString::TrimMode::TRIM) 1067 { 1068 return EcmaString::Trim(thread, src, mode); 1069 } 1070 IsFlat()1071 bool IsFlat() const 1072 { 1073 return string_->IsFlat(); 1074 } 1075 IsLineString()1076 bool IsLineString() const 1077 { 1078 return string_->IsLineString(); 1079 } 1080 IsConstantString()1081 bool IsConstantString() const 1082 { 1083 return string_->IsConstantString(); 1084 } 1085 IsLineOrConstantString()1086 bool IsLineOrConstantString() const 1087 { 1088 return string_->IsLineOrConstantString(); 1089 } 1090 IsTreeString()1091 bool IsTreeString() const 1092 { 1093 return string_->IsTreeString(); 1094 } 1095 1096 static EcmaString *Flatten(const EcmaVM *vm, const JSHandle<EcmaString> &string, 1097 MemSpaceType type = MemSpaceType::SEMI_SPACE) 1098 { 1099 return EcmaString::Flatten(vm, string, type); 1100 } 1101 1102 static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle<TreeEcmaString> &string, 1103 MemSpaceType type = MemSpaceType::SEMI_SPACE) 1104 { 1105 return EcmaString::SlowFlatten(vm, string, type); 1106 } 1107 FlattenNoGC(const EcmaVM * vm,EcmaString * string)1108 static EcmaString *FlattenNoGC(const EcmaVM *vm, EcmaString *string) 1109 { 1110 return EcmaString::FlattenNoGC(vm, string); 1111 } 1112 GetUtf8DataFlat(const EcmaString * src,CVector<uint8_t> & buf)1113 static const uint8_t *GetUtf8DataFlat(const EcmaString *src, CVector<uint8_t> &buf) 1114 { 1115 return EcmaString::GetUtf8DataFlat(src, buf); 1116 } 1117 GetUtf16DataFlat(const EcmaString * src,CVector<uint16_t> & buf)1118 static const uint16_t *GetUtf16DataFlat(const EcmaString *src, CVector<uint16_t> &buf) 1119 { 1120 return EcmaString::GetUtf16DataFlat(src, buf); 1121 } 1122 1123 private: 1124 EcmaString *string_ {nullptr}; 1125 }; 1126 } // namespace ecmascript 1127 } // namespace panda 1128 #endif // ECMASCRIPT_STRING_H