mirror of
https://github.com/RGBCube/serenity
synced 2025-07-04 21:37:36 +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:
parent
fab8ef3dfc
commit
76b9d06b19
4 changed files with 107 additions and 30 deletions
|
@ -231,6 +231,7 @@ set(SOURCES
|
|||
Runtime/Temporal/ZonedDateTime.cpp
|
||||
Runtime/Temporal/ZonedDateTimeConstructor.cpp
|
||||
Runtime/Temporal/ZonedDateTimePrototype.cpp
|
||||
Runtime/ThrowableStringBuilder.cpp
|
||||
Runtime/TypedArray.cpp
|
||||
Runtime/TypedArrayConstructor.cpp
|
||||
Runtime/TypedArrayPrototype.cpp
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Utf16View.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
|
@ -24,6 +23,7 @@
|
|||
#include <LibJS/Runtime/StringIterator.h>
|
||||
#include <LibJS/Runtime/StringObject.h>
|
||||
#include <LibJS/Runtime/StringPrototype.h>
|
||||
#include <LibJS/Runtime/ThrowableStringBuilder.h>
|
||||
#include <LibJS/Runtime/Utf16String.h>
|
||||
#include <LibJS/Runtime/Value.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_length = max_length - string_length;
|
||||
|
||||
StringBuilder filler_builder;
|
||||
ThrowableStringBuilder filler_builder(vm);
|
||||
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 formatted = placement == PadPlacement::Start
|
||||
|
@ -575,9 +575,9 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::repeat)
|
|||
if (string.is_empty())
|
||||
return PrimitiveString::create(vm, DeprecatedString::empty());
|
||||
|
||||
StringBuilder builder;
|
||||
ThrowableStringBuilder builder(vm);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
builder.append(string);
|
||||
TRY(builder.append(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));
|
||||
}
|
||||
|
||||
StringBuilder builder;
|
||||
builder.append(preserved);
|
||||
builder.append(replacement);
|
||||
builder.append(string.substring_view(*position + search_string.length_in_code_units()));
|
||||
ThrowableStringBuilder builder(vm);
|
||||
TRY(builder.append(preserved));
|
||||
TRY(builder.append(replacement));
|
||||
TRY(builder.append(string.substring_view(*position + search_string.length_in_code_units())));
|
||||
|
||||
return PrimitiveString::create(vm, builder.build());
|
||||
}
|
||||
|
@ -667,7 +667,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
|
|||
}
|
||||
|
||||
size_t end_of_last_match = 0;
|
||||
StringBuilder result;
|
||||
ThrowableStringBuilder result(vm);
|
||||
|
||||
for (auto position : match_positions) {
|
||||
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));
|
||||
}
|
||||
|
||||
result.append(preserved);
|
||||
result.append(replacement);
|
||||
TRY(result.append(preserved));
|
||||
TRY(result.append(replacement));
|
||||
|
||||
end_of_last_match = position + search_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());
|
||||
}
|
||||
|
@ -995,7 +995,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_well_formed)
|
|||
size_t k = 0;
|
||||
|
||||
// 5. Let result be the empty String.
|
||||
StringBuilder result;
|
||||
ThrowableStringBuilder result(vm);
|
||||
|
||||
// 6. Repeat, while k < strLen,
|
||||
while (k < length) {
|
||||
|
@ -1005,12 +1005,12 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_well_formed)
|
|||
// b. If cp.[[IsUnpairedSurrogate]] is true, then
|
||||
if (code_point.is_unpaired_surrogate) {
|
||||
// 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,
|
||||
else {
|
||||
// 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]].
|
||||
|
@ -1119,22 +1119,22 @@ static ThrowCompletionOr<Value> create_html(VM& vm, Value string, DeprecatedStri
|
|||
{
|
||||
TRY(require_object_coercible(vm, string));
|
||||
auto str = TRY(string.to_string(vm));
|
||||
StringBuilder builder;
|
||||
builder.append('<');
|
||||
builder.append(tag);
|
||||
ThrowableStringBuilder builder(vm);
|
||||
TRY(builder.append('<'));
|
||||
TRY(builder.append(tag));
|
||||
if (!attribute.is_empty()) {
|
||||
auto value_string = TRY(value.to_string(vm));
|
||||
builder.append(' ');
|
||||
builder.append(attribute);
|
||||
builder.append("=\""sv);
|
||||
builder.append(value_string.replace("\""sv, """sv, ReplaceMode::All));
|
||||
builder.append('"');
|
||||
TRY(builder.append(' '));
|
||||
TRY(builder.append(attribute));
|
||||
TRY(builder.append("=\""sv));
|
||||
TRY(builder.append(value_string.replace("\""sv, """sv, ReplaceMode::All)));
|
||||
TRY(builder.append('"'));
|
||||
}
|
||||
builder.append('>');
|
||||
builder.append(str);
|
||||
builder.append("</"sv);
|
||||
builder.append(tag);
|
||||
builder.append('>');
|
||||
TRY(builder.append('>'));
|
||||
TRY(builder.append(str));
|
||||
TRY(builder.append("</"sv));
|
||||
TRY(builder.append(tag));
|
||||
TRY(builder.append('>'));
|
||||
return PrimitiveString::create(vm, builder.build());
|
||||
}
|
||||
|
||||
|
|
45
Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp
Normal file
45
Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp
Normal 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 {};
|
||||
}
|
||||
|
||||
}
|
31
Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h
Normal file
31
Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue