1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-19 09:27:34 +00:00

LibJS: Make PrimitiveString::resolve_rope_if_needed() infallible

Work towards #20449.
This commit is contained in:
Andreas Kling 2023-08-08 18:34:19 +02:00
parent 1a27c525d5
commit 9708b86d65
2 changed files with 25 additions and 26 deletions

View file

@ -77,7 +77,7 @@ bool PrimitiveString::is_empty() const
ThrowCompletionOr<String> PrimitiveString::utf8_string() const ThrowCompletionOr<String> PrimitiveString::utf8_string() const
{ {
auto& vm = this->vm(); auto& vm = this->vm();
TRY(resolve_rope_if_needed(EncodingPreference::UTF8)); resolve_rope_if_needed(EncodingPreference::UTF8);
if (!has_utf8_string()) { if (!has_utf8_string()) {
if (has_deprecated_string()) if (has_deprecated_string())
@ -99,7 +99,7 @@ ThrowCompletionOr<StringView> PrimitiveString::utf8_string_view() const
ThrowCompletionOr<DeprecatedString> PrimitiveString::deprecated_string() const ThrowCompletionOr<DeprecatedString> PrimitiveString::deprecated_string() const
{ {
TRY(resolve_rope_if_needed(EncodingPreference::UTF8)); resolve_rope_if_needed(EncodingPreference::UTF8);
if (!has_deprecated_string()) { if (!has_deprecated_string()) {
if (has_utf8_string()) if (has_utf8_string())
@ -115,7 +115,7 @@ ThrowCompletionOr<DeprecatedString> PrimitiveString::deprecated_string() const
ThrowCompletionOr<Utf16String> PrimitiveString::utf16_string() const ThrowCompletionOr<Utf16String> PrimitiveString::utf16_string() const
{ {
TRY(resolve_rope_if_needed(EncodingPreference::UTF16)); resolve_rope_if_needed(EncodingPreference::UTF16);
if (!has_utf16_string()) { if (!has_utf16_string()) {
if (has_utf8_string()) { if (has_utf8_string()) {
@ -245,10 +245,10 @@ NonnullGCPtr<PrimitiveString> PrimitiveString::create(VM& vm, PrimitiveString& l
return vm.heap().allocate_without_realm<PrimitiveString>(lhs, rhs); return vm.heap().allocate_without_realm<PrimitiveString>(lhs, rhs);
} }
ThrowCompletionOr<void> PrimitiveString::resolve_rope_if_needed(EncodingPreference preference) const void PrimitiveString::resolve_rope_if_needed(EncodingPreference preference) const
{ {
if (!m_is_rope) if (!m_is_rope)
return {}; return;
auto& vm = this->vm(); auto& vm = this->vm();
@ -259,16 +259,16 @@ ThrowCompletionOr<void> PrimitiveString::resolve_rope_if_needed(EncodingPreferen
// NOTE: We traverse the rope tree without using recursion, since we'd run out of // NOTE: We traverse the rope tree without using recursion, since we'd run out of
// stack space quickly when handling a long sequence of unresolved concatenations. // stack space quickly when handling a long sequence of unresolved concatenations.
Vector<PrimitiveString const*> stack; Vector<PrimitiveString const*> stack;
TRY_OR_THROW_OOM(vm, stack.try_append(m_rhs)); stack.append(m_rhs);
TRY_OR_THROW_OOM(vm, stack.try_append(m_lhs)); stack.append(m_lhs);
while (!stack.is_empty()) { while (!stack.is_empty()) {
auto const* current = stack.take_last(); auto const* current = stack.take_last();
if (current->m_is_rope) { if (current->m_is_rope) {
TRY_OR_THROW_OOM(vm, stack.try_append(current->m_rhs)); stack.append(current->m_rhs);
TRY_OR_THROW_OOM(vm, stack.try_append(current->m_lhs)); stack.append(current->m_lhs);
continue; continue;
} }
TRY_OR_THROW_OOM(vm, pieces.try_append(current)); pieces.append(current);
} }
if (preference == EncodingPreference::UTF16) { if (preference == EncodingPreference::UTF16) {
@ -277,38 +277,38 @@ ThrowCompletionOr<void> PrimitiveString::resolve_rope_if_needed(EncodingPreferen
Utf16Data code_units; Utf16Data code_units;
for (auto const* current : pieces) for (auto const* current : pieces)
code_units.extend(TRY(current->utf16_string()).string()); code_units.extend(MUST(current->utf16_string()).string());
m_utf16_string = TRY(Utf16String::create(vm, move(code_units))); m_utf16_string = MUST(Utf16String::create(vm, move(code_units)));
m_is_rope = false; m_is_rope = false;
m_lhs = nullptr; m_lhs = nullptr;
m_rhs = nullptr; m_rhs = nullptr;
return {}; return;
} }
// Now that we have all the pieces, we can concatenate them using a StringBuilder. // Now that we have all the pieces, we can concatenate them using a StringBuilder.
ThrowableStringBuilder builder(vm); StringBuilder builder;
// We keep track of the previous piece in order to handle surrogate pairs spread across two pieces. // We keep track of the previous piece in order to handle surrogate pairs spread across two pieces.
PrimitiveString const* previous = nullptr; PrimitiveString const* previous = nullptr;
for (auto const* current : pieces) { for (auto const* current : pieces) {
if (!previous) { if (!previous) {
// This is the very first piece, just append it and continue. // This is the very first piece, just append it and continue.
TRY(builder.append(TRY(current->utf8_string()))); builder.append(MUST(current->utf8_string()));
previous = current; previous = current;
continue; continue;
} }
// Get the UTF-8 representations for both strings. // Get the UTF-8 representations for both strings.
auto current_string_as_utf8 = TRY(current->utf8_string_view()); auto current_string_as_utf8 = MUST(current->utf8_string_view());
auto previous_string_as_utf8 = TRY(previous->utf8_string_view()); auto previous_string_as_utf8 = MUST(previous->utf8_string_view());
// NOTE: Now we need to look at the end of the previous string and the start // NOTE: Now we need to look at the end of the previous string and the start
// of the current string, to see if they should be combined into a surrogate. // of the current string, to see if they should be combined into a surrogate.
// Surrogates encoded as UTF-8 are 3 bytes. // Surrogates encoded as UTF-8 are 3 bytes.
if ((previous_string_as_utf8.length() < 3) || (current_string_as_utf8.length() < 3)) { if ((previous_string_as_utf8.length() < 3) || (current_string_as_utf8.length() < 3)) {
TRY(builder.append(current_string_as_utf8)); builder.append(current_string_as_utf8);
previous = current; previous = current;
continue; continue;
} }
@ -316,7 +316,7 @@ ThrowCompletionOr<void> PrimitiveString::resolve_rope_if_needed(EncodingPreferen
// Might the previous string end with a UTF-8 encoded surrogate? // Might the previous string end with a UTF-8 encoded surrogate?
if ((static_cast<u8>(previous_string_as_utf8[previous_string_as_utf8.length() - 3]) & 0xf0) != 0xe0) { if ((static_cast<u8>(previous_string_as_utf8[previous_string_as_utf8.length() - 3]) & 0xf0) != 0xe0) {
// If not, just append the current string and continue. // If not, just append the current string and continue.
TRY(builder.append(current_string_as_utf8)); builder.append(current_string_as_utf8);
previous = current; previous = current;
continue; continue;
} }
@ -324,7 +324,7 @@ ThrowCompletionOr<void> PrimitiveString::resolve_rope_if_needed(EncodingPreferen
// Might the current string begin with a UTF-8 encoded surrogate? // Might the current string begin with a UTF-8 encoded surrogate?
if ((static_cast<u8>(current_string_as_utf8[0]) & 0xf0) != 0xe0) { if ((static_cast<u8>(current_string_as_utf8[0]) & 0xf0) != 0xe0) {
// If not, just append the current string and continue. // If not, just append the current string and continue.
TRY(builder.append(current_string_as_utf8)); builder.append(current_string_as_utf8);
previous = current; previous = current;
continue; continue;
} }
@ -333,25 +333,24 @@ ThrowCompletionOr<void> PrimitiveString::resolve_rope_if_needed(EncodingPreferen
auto low_surrogate = *Utf8View(current_string_as_utf8).begin(); auto low_surrogate = *Utf8View(current_string_as_utf8).begin();
if (!Utf16View::is_high_surrogate(high_surrogate) || !Utf16View::is_low_surrogate(low_surrogate)) { if (!Utf16View::is_high_surrogate(high_surrogate) || !Utf16View::is_low_surrogate(low_surrogate)) {
TRY(builder.append(current_string_as_utf8)); builder.append(current_string_as_utf8);
previous = current; previous = current;
continue; continue;
} }
// Remove 3 bytes from the builder and replace them with the UTF-8 encoded code point. // Remove 3 bytes from the builder and replace them with the UTF-8 encoded code point.
builder.trim(3); builder.trim(3);
TRY(builder.append_code_point(Utf16View::decode_surrogate_pair(high_surrogate, low_surrogate))); builder.append_code_point(Utf16View::decode_surrogate_pair(high_surrogate, low_surrogate));
// Append the remaining part of the current string. // Append the remaining part of the current string.
TRY(builder.append(current_string_as_utf8.substring_view(3))); builder.append(current_string_as_utf8.substring_view(3));
previous = current; previous = current;
} }
m_utf8_string = TRY(builder.to_string()); m_utf8_string = MUST(builder.to_string());
m_is_rope = false; m_is_rope = false;
m_lhs = nullptr; m_lhs = nullptr;
m_rhs = nullptr; m_rhs = nullptr;
return {};
} }
} }

View file

@ -63,7 +63,7 @@ private:
UTF8, UTF8,
UTF16, UTF16,
}; };
ThrowCompletionOr<void> resolve_rope_if_needed(EncodingPreference) const; void resolve_rope_if_needed(EncodingPreference) const;
mutable bool m_is_rope { false }; mutable bool m_is_rope { false };