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); 538 539 static EcmaString *Flatten(const EcmaVM *vm, const JSHandle<EcmaString> &string); 540 541 static EcmaString *FlattenNoGC(const EcmaVM *vm, EcmaString *string); 542 543 static EcmaString *ToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src); 544 545 static EcmaString *ToUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src); 546 547 static EcmaString *ToLocaleLower(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale); 548 549 static EcmaString *ToLocaleUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale); 550 551 static EcmaString *TryToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src); 552 553 static EcmaString *ConvertUtf8ToLowerOrUpper(const EcmaVM *vm, const JSHandle<EcmaString> &srcFlat, 554 bool toLower, uint32_t startIndex = 0); 555 }; 556 557 // The LineEcmaString abstract class captures sequential string values, only LineEcmaString can store chars data 558 class LineEcmaString : public EcmaString { 559 public: 560 // DATA_OFFSET: the string data stored after the string header. 561 // Data can be stored in utf8 or utf16 form according to compressed bit. 562 static constexpr size_t DATA_OFFSET = EcmaString::SIZE; // DATA_OFFSET equal to Empty String size 563 564 CAST_CHECK(LineEcmaString, IsLineString); 565 Cast(EcmaString * str)566 static LineEcmaString *Cast(EcmaString *str) 567 { 568 return static_cast<LineEcmaString *>(str); 569 } 570 Cast(const EcmaString * str)571 static LineEcmaString *Cast(const EcmaString *str) 572 { 573 return LineEcmaString::Cast(const_cast<EcmaString *>(str)); 574 } 575 ComputeSizeUtf8(uint32_t utf8Len)576 static size_t ComputeSizeUtf8(uint32_t utf8Len) 577 { 578 return DATA_OFFSET + utf8Len; 579 } 580 ComputeSizeUtf16(uint32_t utf16Len)581 static size_t ComputeSizeUtf16(uint32_t utf16Len) 582 { 583 return DATA_OFFSET + utf16Len * sizeof(uint16_t); 584 } 585 ObjectSize(EcmaString * str)586 static size_t ObjectSize(EcmaString *str) 587 { 588 uint32_t length = str->GetLength(); 589 return str->IsUtf16() ? ComputeSizeUtf16(length) : ComputeSizeUtf8(length); 590 } 591 GetData()592 uint16_t *GetData() const 593 { 594 return reinterpret_cast<uint16_t *>(ToUintPtr(this) + DATA_OFFSET); 595 } 596 597 template<bool verify = true> Get(int32_t index)598 uint16_t Get(int32_t index) const 599 { 600 int32_t length = static_cast<int32_t>(GetLength()); 601 if (verify) { 602 if ((index < 0) || (index >= length)) { 603 return 0; 604 } 605 } 606 if (!IsUtf16()) { 607 Span<const uint8_t> sp(GetDataUtf8(), length); 608 return sp[index]; 609 } 610 Span<const uint16_t> sp(GetDataUtf16(), length); 611 return sp[index]; 612 } 613 Set(uint32_t index,uint16_t src)614 void Set(uint32_t index, uint16_t src) 615 { 616 ASSERT(index < GetLength()); 617 if (IsUtf8()) { 618 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 619 *(reinterpret_cast<uint8_t *>(GetData()) + index) = static_cast<uint8_t>(src); 620 } else { 621 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 622 *(GetData() + index) = src; 623 } 624 } 625 }; 626 static_assert((LineEcmaString::DATA_OFFSET % static_cast<uint8_t>(MemAlignment::MEM_ALIGN_OBJECT)) == 0); 627 628 class ConstantString : public EcmaString { 629 public: 630 static constexpr size_t ENTITY_ID_OFFSET = EcmaString::SIZE; 631 // ConstantData is the pointer of const string in the pandafile. 632 // String in pandafile is encoded by the utf8 format. 633 ACCESSORS_PRIMITIVE_FIELD(EntityId, uint32_t, ENTITY_ID_OFFSET, CONSTANT_DATA_OFFSET); 634 ACCESSORS_NATIVE_FIELD(ConstantData, uint8_t, CONSTANT_DATA_OFFSET, SIZE); 635 636 CAST_CHECK(ConstantString, IsConstantString); 637 Cast(EcmaString * str)638 static ConstantString *Cast(EcmaString *str) 639 { 640 return static_cast<ConstantString *>(str); 641 } 642 Cast(const EcmaString * str)643 static ConstantString *Cast(const EcmaString *str) 644 { 645 return ConstantString::Cast(const_cast<EcmaString *>(str)); 646 } 647 ObjectSize()648 static size_t ObjectSize() 649 { 650 return ConstantString::SIZE; 651 } 652 653 template<bool verify = true> Get(int32_t index)654 uint16_t Get(int32_t index) const 655 { 656 int32_t length = static_cast<int32_t>(GetLength()); 657 if (verify) { 658 if ((index < 0) || (index >= length)) { 659 return 0; 660 } 661 } 662 ASSERT(IsUtf8()); 663 Span<const uint8_t> sp(GetConstantData(), length); 664 return sp[index]; 665 } 666 }; 667 668 class TreeEcmaString : public EcmaString { 669 public: 670 // Minimum length for a tree string 671 static constexpr uint32_t MIN_TREE_ECMASTRING_LENGTH = 13; 672 673 static constexpr size_t FIRST_OFFSET = EcmaString::SIZE; 674 ACCESSORS(First, FIRST_OFFSET, SECOND_OFFSET); 675 ACCESSORS(Second, SECOND_OFFSET, SIZE); 676 677 DECL_VISIT_OBJECT(FIRST_OFFSET, SIZE); 678 679 CAST_CHECK(TreeEcmaString, IsTreeString); 680 Cast(EcmaString * str)681 static TreeEcmaString *Cast(EcmaString *str) 682 { 683 return static_cast<TreeEcmaString *>(str); 684 } 685 Cast(const EcmaString * str)686 static TreeEcmaString *Cast(const EcmaString *str) 687 { 688 return TreeEcmaString::Cast(const_cast<EcmaString *>(str)); 689 } 690 IsFlat()691 bool IsFlat() const 692 { 693 auto strSecond = EcmaString::Cast(GetSecond()); 694 return strSecond->GetLength() == 0; 695 } 696 697 template<bool verify = true> Get(int32_t index)698 uint16_t Get(int32_t index) const 699 { 700 int32_t length = static_cast<int32_t>(GetLength()); 701 if (verify) { 702 if ((index < 0) || (index >= length)) { 703 return 0; 704 } 705 } 706 707 if (IsFlat()) { 708 EcmaString *first = EcmaString::Cast(GetFirst()); 709 return first->At<verify>(index); 710 } 711 EcmaString *string = const_cast<TreeEcmaString *>(this); 712 while (true) { 713 if (string->IsTreeString()) { 714 EcmaString *first = EcmaString::Cast(TreeEcmaString::Cast(string)->GetFirst()); 715 if (static_cast<int32_t>(first->GetLength()) > index) { 716 string = first; 717 } else { 718 index -= static_cast<int32_t>(first->GetLength()); 719 string = EcmaString::Cast(TreeEcmaString::Cast(string)->GetSecond()); 720 } 721 } else { 722 return string->At<verify>(index); 723 } 724 } 725 UNREACHABLE(); 726 } 727 }; 728 729 // if you want to use functions of EcmaString, please not use directly, 730 // and use functions of EcmaStringAccessor alternatively. 731 // eg: EcmaString *str = ***; str->GetLength() -----> EcmaStringAccessor(str).GetLength() 732 class PUBLIC_API EcmaStringAccessor { 733 public: 734 explicit EcmaStringAccessor(EcmaString *string); 735 736 explicit EcmaStringAccessor(TaggedObject *obj); 737 738 explicit EcmaStringAccessor(JSTaggedValue value); 739 740 explicit EcmaStringAccessor(const JSHandle<EcmaString> &strHandle); 741 CreateLineString(const EcmaVM * vm,size_t length,bool compressed)742 static EcmaString *CreateLineString(const EcmaVM *vm, size_t length, bool compressed) 743 { 744 return EcmaString::CreateLineString(vm, length, compressed); 745 } 746 CreateEmptyString(const EcmaVM * vm)747 static EcmaString *CreateEmptyString(const EcmaVM *vm) 748 { 749 return EcmaString::CreateEmptyString(vm); 750 } 751 752 static EcmaString *CreateFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, 753 MemSpaceType type = MemSpaceType::SEMI_SPACE, bool isConstantString = false, 754 uint32_t idOffset = 0) 755 { 756 return EcmaString::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type, isConstantString, idOffset); 757 } 758 759 static EcmaString *CreateConstantString(const EcmaVM *vm, const uint8_t *utf8Data, size_t length, 760 bool compressed, MemSpaceType type = MemSpaceType::SEMI_SPACE, uint32_t idOffset = 0) 761 { 762 return EcmaString::CreateConstantString(vm, utf8Data, length, compressed, type, idOffset); 763 } 764 765 static EcmaString *CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, 766 bool canBeCompress, MemSpaceType type = MemSpaceType::SEMI_SPACE) 767 { 768 return EcmaString::CreateFromUtf16(vm, utf16Data, utf16Len, canBeCompress, type); 769 } 770 771 static EcmaString *Concat(const EcmaVM *vm, const JSHandle<EcmaString> &str1Handle, 772 const JSHandle<EcmaString> &str2Handle, MemSpaceType type = MemSpaceType::SEMI_SPACE) 773 { 774 return EcmaString::Concat(vm, str1Handle, str2Handle, type); 775 } 776 CopyStringToOldSpace(const EcmaVM * vm,const JSHandle<EcmaString> & original,uint32_t length,bool compressed)777 static EcmaString *CopyStringToOldSpace(const EcmaVM *vm, const JSHandle<EcmaString> &original, 778 uint32_t length, bool compressed) 779 { 780 return EcmaString::CopyStringToOldSpace(vm, original, length, compressed); 781 } 782 783 // can change src data structure FastSubString(const EcmaVM * vm,const JSHandle<EcmaString> & src,uint32_t start,uint32_t length)784 static EcmaString *FastSubString(const EcmaVM *vm, 785 const JSHandle<EcmaString> &src, uint32_t start, uint32_t length) 786 { 787 return EcmaString::FastSubString(vm, src, start, length); 788 } 789 IsUtf8()790 bool IsUtf8() const 791 { 792 return string_->IsUtf8(); 793 } 794 IsUtf16()795 bool IsUtf16() const 796 { 797 return string_->IsUtf16(); 798 } 799 GetLength()800 uint32_t GetLength() const 801 { 802 return string_->GetLength(); 803 } 804 805 // require is LineString 806 inline size_t GetUtf8Length() const; 807 ObjectSize()808 size_t ObjectSize() const 809 { 810 if (string_->IsLineString()) { 811 return LineEcmaString::ObjectSize(string_); 812 } if (string_->IsConstantString()) { 813 return ConstantString::ObjectSize(); 814 } else { 815 return TreeEcmaString::SIZE; 816 } 817 } 818 819 // For TreeString, the calculation result is size of LineString correspondingly. GetFlatStringSize()820 size_t GetFlatStringSize() const 821 { 822 if (string_->IsConstantString()) { 823 return ConstantString::ObjectSize(); 824 } 825 return LineEcmaString::ObjectSize(string_); 826 } 827 IsInternString()828 bool IsInternString() const 829 { 830 return string_->IsInternString(); 831 } 832 SetInternString()833 void SetInternString() 834 { 835 string_->SetIsInternString(); 836 } 837 ClearInternString()838 void ClearInternString() 839 { 840 string_->ClearInternStringFlag(); 841 } 842 843 // require is LineString 844 // It's Utf8 format, but without 0 in the end. 845 inline const uint8_t *GetDataUtf8(); 846 847 // require is LineString 848 inline const uint16_t *GetDataUtf16(); 849 850 // not change string data structure. 851 // if string is not flat, this func has low efficiency. 852 std::u16string ToU16String(uint32_t len = 0) 853 { 854 return string_->ToU16String(len); 855 } 856 857 // not change string data structure. 858 // if string is not flat, this func has low efficiency. ToOneByteDataForced()859 std::unique_ptr<uint8_t[]> ToOneByteDataForced() 860 { 861 return string_->ToOneByteDataForced(); 862 } 863 864 // not change string data structure. 865 // if string is not flat, this func has low efficiency. ToUtf8Span(CVector<uint8_t> & buf)866 Span<const uint8_t> ToUtf8Span(CVector<uint8_t> &buf) 867 { 868 return string_->ToUtf8Span(buf); 869 } 870 871 // not change string data structure. 872 // if string is not flat, this func has low efficiency. 873 std::string ToStdString(StringConvertedUsage usage = StringConvertedUsage::PRINT); 874 875 // not change string data structure. 876 // if string is not flat, this func has low efficiency. 877 CString ToCString(StringConvertedUsage usage = StringConvertedUsage::LOGICOPERATION); 878 879 // not change string data structure. 880 // if string is not flat, this func has low efficiency. 881 uint32_t WriteToFlatUtf8(uint8_t *buf, uint32_t maxLength, bool isWriteBuffer = false) 882 { 883 return string_->WriteUtf8(buf, maxLength, isWriteBuffer); 884 } 885 WriteToUtf16(uint16_t * buf,uint32_t bufLength)886 uint32_t WriteToUtf16(uint16_t *buf, uint32_t bufLength) 887 { 888 return string_->WriteUtf16(buf, GetLength(), bufLength); 889 } 890 WriteToOneByte(uint8_t * buf,uint32_t maxLength)891 uint32_t WriteToOneByte(uint8_t *buf, uint32_t maxLength) 892 { 893 return string_->WriteOneByte(buf, maxLength); 894 } 895 896 // not change string data structure. 897 // if string is not flat, this func has low efficiency. WriteToFlatUtf16(uint16_t * buf,uint32_t maxLength)898 uint32_t WriteToFlatUtf16(uint16_t *buf, uint32_t maxLength) const 899 { 900 return string_->CopyDataUtf16(buf, maxLength); 901 } 902 903 // require dst is LineString 904 // not change src data structure. 905 // if src is not flat, this func has low efficiency. 906 inline static void ReadData(EcmaString * dst, EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length); 907 908 // not change src data structure. 909 // if src is not flat, this func has low efficiency. 910 template<bool verify = true> Get(uint32_t index)911 uint16_t Get(uint32_t index) const 912 { 913 return string_->At<verify>(index); 914 } 915 916 // require string is LineString. Set(uint32_t index,uint16_t src)917 void Set(uint32_t index, uint16_t src) 918 { 919 return string_->WriteData(index, src); 920 } 921 922 // not change src data structure. 923 // if src is not flat, this func has low efficiency. GetHashcode()924 uint32_t GetHashcode() 925 { 926 return string_->GetHashcode(); 927 } 928 929 // not change src data structure. 930 // if src is not flat, this func has low efficiency. ComputeHashcode(uint32_t hashSeed)931 uint32_t ComputeHashcode(uint32_t hashSeed) 932 { 933 return string_->ComputeHashcode(hashSeed); 934 } 935 ComputeHashcodeUtf8(const uint8_t * utf8Data,size_t utf8Len,bool canBeCompress)936 static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress) 937 { 938 return EcmaString::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress); 939 } 940 ComputeHashcodeUtf16(const uint16_t * utf16Data,uint32_t length)941 static uint32_t ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length) 942 { 943 return EcmaString::ComputeHashcodeUtf16(utf16Data, length); 944 } 945 946 // can change receiver and search data structure 947 static int32_t IndexOf(const EcmaVM *vm, 948 const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0) 949 { 950 return EcmaString::IndexOf(vm, receiver, search, pos); 951 } 952 953 // can change receiver and search data structure 954 static int32_t LastIndexOf(const EcmaVM *vm, 955 const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0) 956 { 957 return EcmaString::LastIndexOf(vm, receiver, search, pos); 958 } 959 960 // can change receiver and search data structure Compare(const EcmaVM * vm,const JSHandle<EcmaString> & left,const JSHandle<EcmaString> & right)961 static int32_t Compare(const EcmaVM *vm, const JSHandle<EcmaString>& left, const JSHandle<EcmaString>& right) 962 { 963 return EcmaString::Compare(vm, left, right); 964 } 965 966 // can change str1 and str2 data structure StringsAreEqual(const EcmaVM * vm,const JSHandle<EcmaString> & str1,const JSHandle<EcmaString> & str2)967 static bool StringsAreEqual(const EcmaVM *vm, const JSHandle<EcmaString> &str1, const JSHandle<EcmaString> &str2) 968 { 969 return EcmaString::StringsAreEqual(vm, str1, str2); 970 } 971 972 // not change str1 and str2 data structure. 973 // if str1 or str2 is not flat, this func has low efficiency. StringsAreEqual(EcmaString * str1,EcmaString * str2)974 static bool StringsAreEqual(EcmaString *str1, EcmaString *str2) 975 { 976 return EcmaString::StringsAreEqual(str1, str2); 977 } 978 979 // not change str1 and str2 data structure. 980 // if str1 or str2 is not flat, this func has low efficiency. StringsAreEqualSameUtfEncoding(EcmaString * str1,EcmaString * str2)981 static bool StringsAreEqualSameUtfEncoding(EcmaString *str1, EcmaString *str2) 982 { 983 return EcmaString::StringsAreEqualSameUtfEncoding(str1, str2); 984 } 985 986 // not change str1 data structure. 987 // if str1 is not flat, this func has low efficiency. StringsAreEqualUtf8(const EcmaString * str1,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress)988 static bool StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len, 989 bool canBeCompress) 990 { 991 return EcmaString::StringsAreEqualUtf8(str1, utf8Data, utf8Len, canBeCompress); 992 } 993 994 // not change str1 data structure. 995 // if str1 is not flat, this func has low efficiency. StringsAreEqualUtf16(const EcmaString * str1,const uint16_t * utf16Data,uint32_t utf16Len)996 static bool StringsAreEqualUtf16(const EcmaString *str1, const uint16_t *utf16Data, uint32_t utf16Len) 997 { 998 return EcmaString::StringsAreEqualUtf16(str1, utf16Data, utf16Len); 999 } 1000 1001 // require str1 and str2 are LineString. 1002 // not change string data structure. 1003 // if string is not flat, this func has low efficiency. EqualToSplicedString(const EcmaString * str1,const EcmaString * str2)1004 bool EqualToSplicedString(const EcmaString *str1, const EcmaString *str2) 1005 { 1006 return string_->EqualToSplicedString(str1, str2); 1007 } 1008 CanBeCompressed(const uint8_t * utf8Data,uint32_t utf8Len)1009 static bool CanBeCompressed(const uint8_t *utf8Data, uint32_t utf8Len) 1010 { 1011 return EcmaString::CanBeCompressed(utf8Data, utf8Len); 1012 } 1013 CanBeCompressed(const uint16_t * utf16Data,uint32_t utf16Len)1014 static bool CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len) 1015 { 1016 return EcmaString::CanBeCompressed(utf16Data, utf16Len); 1017 } 1018 1019 // require string is LineString CanBeCompressed(const EcmaString * string)1020 static bool CanBeCompressed(const EcmaString *string) 1021 { 1022 return EcmaString::CanBeCompressed(string); 1023 } 1024 1025 // not change string data structure. 1026 // if string is not flat, this func has low efficiency. ToElementIndex(uint32_t * index)1027 bool ToElementIndex(uint32_t *index) 1028 { 1029 return string_->ToElementIndex(index); 1030 } 1031 1032 // not change string data structure. 1033 // if string is not flat, this func has low efficiency. ToTypedArrayIndex(uint32_t * index)1034 bool ToTypedArrayIndex(uint32_t *index) 1035 { 1036 return string_->ToTypedArrayIndex(index); 1037 } 1038 ToLower(const EcmaVM * vm,const JSHandle<EcmaString> & src)1039 static EcmaString *ToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src) 1040 { 1041 return EcmaString::ToLower(vm, src); 1042 } 1043 TryToLower(const EcmaVM * vm,const JSHandle<EcmaString> & src)1044 static EcmaString *TryToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src) 1045 { 1046 return EcmaString::TryToLower(vm, src); 1047 } 1048 ToUpper(const EcmaVM * vm,const JSHandle<EcmaString> & src)1049 static EcmaString *ToUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src) 1050 { 1051 return EcmaString::ToUpper(vm, src); 1052 } 1053 ToLocaleLower(const EcmaVM * vm,const JSHandle<EcmaString> & src,const icu::Locale & locale)1054 static EcmaString *ToLocaleLower(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale) 1055 { 1056 return EcmaString::ToLocaleLower(vm, src, locale); 1057 } 1058 ToLocaleUpper(const EcmaVM * vm,const JSHandle<EcmaString> & src,const icu::Locale & locale)1059 static EcmaString *ToLocaleUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale) 1060 { 1061 return EcmaString::ToLocaleUpper(vm, src, locale); 1062 } 1063 1064 static EcmaString *Trim(const JSThread *thread, 1065 const JSHandle<EcmaString> &src, EcmaString::TrimMode mode = EcmaString::TrimMode::TRIM) 1066 { 1067 return EcmaString::Trim(thread, src, mode); 1068 } 1069 IsFlat()1070 bool IsFlat() const 1071 { 1072 return string_->IsFlat(); 1073 } 1074 IsLineString()1075 bool IsLineString() const 1076 { 1077 return string_->IsLineString(); 1078 } 1079 IsConstantString()1080 bool IsConstantString() const 1081 { 1082 return string_->IsConstantString(); 1083 } 1084 IsLineOrConstantString()1085 bool IsLineOrConstantString() const 1086 { 1087 return string_->IsLineOrConstantString(); 1088 } 1089 IsTreeString()1090 bool IsTreeString() const 1091 { 1092 return string_->IsTreeString(); 1093 } 1094 Flatten(const EcmaVM * vm,const JSHandle<EcmaString> & string)1095 static EcmaString *Flatten(const EcmaVM *vm, const JSHandle<EcmaString> &string) 1096 { 1097 return EcmaString::Flatten(vm, string); 1098 } 1099 SlowFlatten(const EcmaVM * vm,const JSHandle<TreeEcmaString> & string)1100 static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle<TreeEcmaString> &string) 1101 { 1102 return EcmaString::SlowFlatten(vm, string); 1103 } 1104 FlattenNoGC(const EcmaVM * vm,EcmaString * string)1105 static EcmaString *FlattenNoGC(const EcmaVM *vm, EcmaString *string) 1106 { 1107 return EcmaString::FlattenNoGC(vm, string); 1108 } 1109 GetUtf8DataFlat(const EcmaString * src,CVector<uint8_t> & buf)1110 static const uint8_t *GetUtf8DataFlat(const EcmaString *src, CVector<uint8_t> &buf) 1111 { 1112 return EcmaString::GetUtf8DataFlat(src, buf); 1113 } 1114 GetUtf16DataFlat(const EcmaString * src,CVector<uint16_t> & buf)1115 static const uint16_t *GetUtf16DataFlat(const EcmaString *src, CVector<uint16_t> &buf) 1116 { 1117 return EcmaString::GetUtf16DataFlat(src, buf); 1118 } 1119 1120 private: 1121 EcmaString *string_ {nullptr}; 1122 }; 1123 } // namespace ecmascript 1124 } // namespace panda 1125 #endif // ECMASCRIPT_STRING_H