summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimothy Flynn <trflynn89@pm.me>2023-01-05 12:56:08 -0500
committerLinus Groh <mail@linusgroh.de>2023-01-05 22:07:44 +0100
commit76b9d06b199dbcd0e39aa3ea535bbc615e777cec (patch)
tree4ba08e59e6e6981dcf46fd944bf47c9a04775c59
parentfab8ef3dfcd56c8e4d870f870a82765e2b36e510 (diff)
downloadserenity-76b9d06b199dbcd0e39aa3ea535bbc615e777cec.zip
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.
-rw-r--r--Userland/Libraries/LibJS/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringPrototype.cpp60
-rw-r--r--Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp45
-rw-r--r--Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h31
4 files changed, 107 insertions, 30 deletions
diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt
index 22c64abebe..a9f5381144 100644
--- a/Userland/Libraries/LibJS/CMakeLists.txt
+++ b/Userland/Libraries/LibJS/CMakeLists.txt
@@ -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
diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp
index e1a117d4cf..9f2ccd6a2c 100644
--- a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.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, "&quot;"sv, ReplaceMode::All));
- builder.append('"');
+ TRY(builder.append(' '));
+ TRY(builder.append(attribute));
+ TRY(builder.append("=\""sv));
+ TRY(builder.append(value_string.replace("\""sv, "&quot;"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());
}
diff --git a/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp b/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp
new file mode 100644
index 0000000000..91bee7aadf
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp
@@ -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 {};
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h b/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h
new file mode 100644
index 0000000000..08ca7eb2fa
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h
@@ -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;
+};
+
+}