1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-06-18 18:52:10 +00:00

LibJS: Add and begin using a completion-compatible string builder

ThrowableStringBuilder is a thin wrapper around StringBuilder to map
results from the try_* methods to a throw completion. This will let us
try to throw on OOM conditions rather than just blowing up.
This commit is contained in:
Timothy Flynn 2023-01-05 12:56:08 -05:00 committed by Linus Groh
parent fab8ef3dfc
commit 76b9d06b19
4 changed files with 107 additions and 30 deletions

View file

@ -231,6 +231,7 @@ set(SOURCES
Runtime/Temporal/ZonedDateTime.cpp Runtime/Temporal/ZonedDateTime.cpp
Runtime/Temporal/ZonedDateTimeConstructor.cpp Runtime/Temporal/ZonedDateTimeConstructor.cpp
Runtime/Temporal/ZonedDateTimePrototype.cpp Runtime/Temporal/ZonedDateTimePrototype.cpp
Runtime/ThrowableStringBuilder.cpp
Runtime/TypedArray.cpp Runtime/TypedArray.cpp
Runtime/TypedArrayConstructor.cpp Runtime/TypedArrayConstructor.cpp
Runtime/TypedArrayPrototype.cpp Runtime/TypedArrayPrototype.cpp

View file

@ -7,7 +7,6 @@
#include <AK/Checked.h> #include <AK/Checked.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/StringBuilder.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <LibJS/Heap/Heap.h> #include <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
@ -24,6 +23,7 @@
#include <LibJS/Runtime/StringIterator.h> #include <LibJS/Runtime/StringIterator.h>
#include <LibJS/Runtime/StringObject.h> #include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/StringPrototype.h> #include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/ThrowableStringBuilder.h>
#include <LibJS/Runtime/Utf16String.h> #include <LibJS/Runtime/Utf16String.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibLocale/Locale.h> #include <LibLocale/Locale.h>
@ -528,11 +528,11 @@ static ThrowCompletionOr<Value> pad_string(VM& vm, Utf16String string, PadPlacem
auto fill_code_units = fill_string.length_in_code_units(); auto fill_code_units = fill_string.length_in_code_units();
auto fill_length = max_length - string_length; auto fill_length = max_length - string_length;
StringBuilder filler_builder; ThrowableStringBuilder filler_builder(vm);
for (size_t i = 0; i < fill_length / fill_code_units; ++i) for (size_t i = 0; i < fill_length / fill_code_units; ++i)
filler_builder.append(fill_string.view()); TRY(filler_builder.append(fill_string.view()));
filler_builder.append(fill_string.substring_view(0, fill_length % fill_code_units)); TRY(filler_builder.append(fill_string.substring_view(0, fill_length % fill_code_units)));
auto filler = filler_builder.build(); auto filler = filler_builder.build();
auto formatted = placement == PadPlacement::Start auto formatted = placement == PadPlacement::Start
@ -575,9 +575,9 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::repeat)
if (string.is_empty()) if (string.is_empty())
return PrimitiveString::create(vm, DeprecatedString::empty()); return PrimitiveString::create(vm, DeprecatedString::empty());
StringBuilder builder; ThrowableStringBuilder builder(vm);
for (size_t i = 0; i < n; ++i) for (size_t i = 0; i < n; ++i)
builder.append(string); TRY(builder.append(string));
return PrimitiveString::create(vm, builder.to_deprecated_string()); return PrimitiveString::create(vm, builder.to_deprecated_string());
} }
@ -615,10 +615,10 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace)
replacement = TRY(get_substitution(vm, search_string.view(), string.view(), *position, {}, js_undefined(), replace_value)); replacement = TRY(get_substitution(vm, search_string.view(), string.view(), *position, {}, js_undefined(), replace_value));
} }
StringBuilder builder; ThrowableStringBuilder builder(vm);
builder.append(preserved); TRY(builder.append(preserved));
builder.append(replacement); TRY(builder.append(replacement));
builder.append(string.substring_view(*position + search_string.length_in_code_units())); TRY(builder.append(string.substring_view(*position + search_string.length_in_code_units())));
return PrimitiveString::create(vm, builder.build()); return PrimitiveString::create(vm, builder.build());
} }
@ -667,7 +667,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
} }
size_t end_of_last_match = 0; size_t end_of_last_match = 0;
StringBuilder result; ThrowableStringBuilder result(vm);
for (auto position : match_positions) { for (auto position : match_positions) {
auto preserved = string.substring_view(end_of_last_match, position - end_of_last_match); auto preserved = string.substring_view(end_of_last_match, position - end_of_last_match);
@ -680,14 +680,14 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
replacement = TRY(get_substitution(vm, search_string.view(), string.view(), position, {}, js_undefined(), replace_value)); replacement = TRY(get_substitution(vm, search_string.view(), string.view(), position, {}, js_undefined(), replace_value));
} }
result.append(preserved); TRY(result.append(preserved));
result.append(replacement); TRY(result.append(replacement));
end_of_last_match = position + search_length; end_of_last_match = position + search_length;
} }
if (end_of_last_match < string_length) if (end_of_last_match < string_length)
result.append(string.substring_view(end_of_last_match)); TRY(result.append(string.substring_view(end_of_last_match)));
return PrimitiveString::create(vm, result.build()); return PrimitiveString::create(vm, result.build());
} }
@ -995,7 +995,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_well_formed)
size_t k = 0; size_t k = 0;
// 5. Let result be the empty String. // 5. Let result be the empty String.
StringBuilder result; ThrowableStringBuilder result(vm);
// 6. Repeat, while k < strLen, // 6. Repeat, while k < strLen,
while (k < length) { while (k < length) {
@ -1005,12 +1005,12 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_well_formed)
// b. If cp.[[IsUnpairedSurrogate]] is true, then // b. If cp.[[IsUnpairedSurrogate]] is true, then
if (code_point.is_unpaired_surrogate) { if (code_point.is_unpaired_surrogate) {
// i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER). // i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).
result.append_code_point(0xfffd); TRY(result.append_code_point(0xfffd));
} }
// c. Else, // c. Else,
else { else {
// i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]). // i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).
result.append_code_point(code_point.code_point); TRY(result.append_code_point(code_point.code_point));
} }
// d. Set k to k + cp.[[CodeUnitCount]]. // d. Set k to k + cp.[[CodeUnitCount]].
@ -1119,22 +1119,22 @@ static ThrowCompletionOr<Value> create_html(VM& vm, Value string, DeprecatedStri
{ {
TRY(require_object_coercible(vm, string)); TRY(require_object_coercible(vm, string));
auto str = TRY(string.to_string(vm)); auto str = TRY(string.to_string(vm));
StringBuilder builder; ThrowableStringBuilder builder(vm);
builder.append('<'); TRY(builder.append('<'));
builder.append(tag); TRY(builder.append(tag));
if (!attribute.is_empty()) { if (!attribute.is_empty()) {
auto value_string = TRY(value.to_string(vm)); auto value_string = TRY(value.to_string(vm));
builder.append(' '); TRY(builder.append(' '));
builder.append(attribute); TRY(builder.append(attribute));
builder.append("=\""sv); TRY(builder.append("=\""sv));
builder.append(value_string.replace("\""sv, "&quot;"sv, ReplaceMode::All)); TRY(builder.append(value_string.replace("\""sv, "&quot;"sv, ReplaceMode::All)));
builder.append('"'); TRY(builder.append('"'));
} }
builder.append('>'); TRY(builder.append('>'));
builder.append(str); TRY(builder.append(str));
builder.append("</"sv); TRY(builder.append("</"sv));
builder.append(tag); TRY(builder.append(tag));
builder.append('>'); TRY(builder.append('>'));
return PrimitiveString::create(vm, builder.build()); return PrimitiveString::create(vm, builder.build());
} }

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Utf16View.h>
#include <LibJS/Runtime/ThrowableStringBuilder.h>
namespace JS {
ThrowableStringBuilder::ThrowableStringBuilder(VM& vm)
: m_vm(vm)
{
}
ThrowCompletionOr<void> ThrowableStringBuilder::append(char ch)
{
if (try_append(ch).is_error())
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + 1);
return {};
}
ThrowCompletionOr<void> ThrowableStringBuilder::append(StringView string)
{
if (try_append(string).is_error())
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + string.length());
return {};
}
ThrowCompletionOr<void> ThrowableStringBuilder::append(Utf16View const& string)
{
if (try_append(string).is_error())
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + (string.length_in_code_units() * 2));
return {};
}
ThrowCompletionOr<void> ThrowableStringBuilder::append_code_point(u32 value)
{
if (auto result = try_append_code_point(value); result.is_error())
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + sizeof(value));
return {};
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/ErrorTypes.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
class ThrowableStringBuilder : public AK::StringBuilder {
public:
explicit ThrowableStringBuilder(VM&);
ThrowCompletionOr<void> append(char);
ThrowCompletionOr<void> append(StringView);
ThrowCompletionOr<void> append(Utf16View const&);
ThrowCompletionOr<void> append_code_point(u32 value);
private:
VM& m_vm;
};
}