mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-25 05:55:13 +00:00
LibJS: Pre-allocate the out-of-memory error string on the VM
If we are out of memory, we can't try to allocate a string that could fail as well. When Error is converted to String, this would result in an endless OOM-throwing loop. Instead, pre-allocate the string on the VM, and use it to construct the Error. Note that as of this commit, the OOM string is still a DeprecatedString. This is just preporatory for Error's conversion to String.
This commit is contained in:
parent
93ad25fbe5
commit
4d10911f96
Notes:
sideshowbarker
2024-07-17 06:20:50 +09:00
Author: https://github.com/trflynn89 Commit: https://github.com/SerenityOS/serenity/commit/4d10911f96 Pull-request: https://github.com/SerenityOS/serenity/pull/17494 Reviewed-by: https://github.com/linusg ✅
5 changed files with 42 additions and 31 deletions
|
@ -17,18 +17,18 @@
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
#define TRY_OR_THROW_OOM(vm, expression) \
|
#define TRY_OR_THROW_OOM(vm, expression) \
|
||||||
({ \
|
({ \
|
||||||
/* Ignore -Wshadow to allow nesting the macro. */ \
|
/* Ignore -Wshadow to allow nesting the macro. */ \
|
||||||
AK_IGNORE_DIAGNOSTIC("-Wshadow", \
|
AK_IGNORE_DIAGNOSTIC("-Wshadow", \
|
||||||
auto&& _temporary_result = (expression)); \
|
auto&& _temporary_result = (expression)); \
|
||||||
if (_temporary_result.is_error()) { \
|
if (_temporary_result.is_error()) { \
|
||||||
VERIFY(_temporary_result.error().code() == ENOMEM); \
|
VERIFY(_temporary_result.error().code() == ENOMEM); \
|
||||||
return (vm).throw_completion<JS::InternalError>(JS::ErrorType::OutOfMemory); \
|
return (vm).throw_completion<JS::InternalError>((vm).error_message(::JS::VM::ErrorMessage::OutOfMemory)); \
|
||||||
} \
|
} \
|
||||||
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
|
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
|
||||||
"Do not return a reference from a fallible expression"); \
|
"Do not return a reference from a fallible expression"); \
|
||||||
_temporary_result.release_value(); \
|
_temporary_result.release_value(); \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define MUST_OR_THROW_OOM(expression) \
|
#define MUST_OR_THROW_OOM(expression) \
|
||||||
|
|
|
@ -16,39 +16,31 @@ ThrowableStringBuilder::ThrowableStringBuilder(VM& vm)
|
||||||
|
|
||||||
ThrowCompletionOr<void> ThrowableStringBuilder::append(char ch)
|
ThrowCompletionOr<void> ThrowableStringBuilder::append(char ch)
|
||||||
{
|
{
|
||||||
if (try_append(ch).is_error())
|
TRY_OR_THROW_OOM(m_vm, try_append(ch));
|
||||||
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + 1);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> ThrowableStringBuilder::append(StringView string)
|
ThrowCompletionOr<void> ThrowableStringBuilder::append(StringView string)
|
||||||
{
|
{
|
||||||
if (try_append(string).is_error())
|
TRY_OR_THROW_OOM(m_vm, try_append(string));
|
||||||
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + string.length());
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> ThrowableStringBuilder::append(Utf16View const& string)
|
ThrowCompletionOr<void> ThrowableStringBuilder::append(Utf16View const& string)
|
||||||
{
|
{
|
||||||
if (try_append(string).is_error())
|
TRY_OR_THROW_OOM(m_vm, try_append(string));
|
||||||
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + (string.length_in_code_units() * 2));
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> ThrowableStringBuilder::append_code_point(u32 value)
|
ThrowCompletionOr<void> ThrowableStringBuilder::append_code_point(u32 value)
|
||||||
{
|
{
|
||||||
if (auto result = try_append_code_point(value); result.is_error())
|
TRY_OR_THROW_OOM(m_vm, try_append_code_point(value));
|
||||||
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + sizeof(value));
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<String> ThrowableStringBuilder::to_string() const
|
ThrowCompletionOr<String> ThrowableStringBuilder::to_string() const
|
||||||
{
|
{
|
||||||
auto result = StringBuilder::to_string();
|
return TRY_OR_THROW_OOM(m_vm, StringBuilder::to_string());
|
||||||
if (result.is_error())
|
|
||||||
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length());
|
|
||||||
|
|
||||||
return result.release_value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,7 @@ public:
|
||||||
ThrowCompletionOr<void> appendff(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
ThrowCompletionOr<void> appendff(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
||||||
{
|
{
|
||||||
AK::VariadicFormatParams<AK::AllowDebugOnlyFormatters::No, Parameters...> variadic_format_params { parameters... };
|
AK::VariadicFormatParams<AK::AllowDebugOnlyFormatters::No, Parameters...> variadic_format_params { parameters... };
|
||||||
|
TRY_OR_THROW_OOM(m_vm, vformat(*this, fmtstr.view(), variadic_format_params));
|
||||||
if (vformat(*this, fmtstr.view(), variadic_format_params).is_error()) {
|
|
||||||
// The size returned here is a bit of an estimate, as we don't know what the final formatted string length would be.
|
|
||||||
return m_vm.throw_completion<InternalError>(ErrorType::NotEnoughMemoryToAllocate, length() + fmtstr.view().length());
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,18 @@ VM::VM(OwnPtr<CustomData> custom_data)
|
||||||
m_well_known_symbol_##snake_name = Symbol::create(*this, String::from_utf8("Symbol." #SymbolName##sv).release_value_but_fixme_should_propagate_errors(), false);
|
m_well_known_symbol_##snake_name = Symbol::create(*this, String::from_utf8("Symbol." #SymbolName##sv).release_value_but_fixme_should_propagate_errors(), false);
|
||||||
JS_ENUMERATE_WELL_KNOWN_SYMBOLS
|
JS_ENUMERATE_WELL_KNOWN_SYMBOLS
|
||||||
#undef __JS_ENUMERATE
|
#undef __JS_ENUMERATE
|
||||||
|
|
||||||
|
m_error_messages[to_underlying(ErrorMessage::OutOfMemory)] = ErrorType::OutOfMemory.message();
|
||||||
|
}
|
||||||
|
|
||||||
|
DeprecatedString const& VM::error_message(ErrorMessage type) const
|
||||||
|
{
|
||||||
|
VERIFY(type < ErrorMessage::__Count);
|
||||||
|
|
||||||
|
auto const& message = m_error_messages[to_underlying(type)];
|
||||||
|
VERIFY(!message.is_empty());
|
||||||
|
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::enable_default_host_import_module_dynamically_hook()
|
void VM::enable_default_host_import_module_dynamically_hook()
|
||||||
|
|
|
@ -91,6 +91,17 @@ public:
|
||||||
return *m_single_ascii_character_strings[character];
|
return *m_single_ascii_character_strings[character];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This represents the list of errors from ErrorTypes.h whose messages are used in contexts which
|
||||||
|
// must not fail to allocate when they are used. For example, we cannot allocate when we raise an
|
||||||
|
// out-of-memory error, thus we pre-allocate that error string at VM creation time.
|
||||||
|
enum class ErrorMessage {
|
||||||
|
OutOfMemory,
|
||||||
|
|
||||||
|
// Keep this last:
|
||||||
|
__Count,
|
||||||
|
};
|
||||||
|
DeprecatedString const& error_message(ErrorMessage) const;
|
||||||
|
|
||||||
bool did_reach_stack_space_limit() const
|
bool did_reach_stack_space_limit() const
|
||||||
{
|
{
|
||||||
// Address sanitizer (ASAN) used to check for more space but
|
// Address sanitizer (ASAN) used to check for more space but
|
||||||
|
@ -285,6 +296,7 @@ private:
|
||||||
|
|
||||||
PrimitiveString* m_empty_string { nullptr };
|
PrimitiveString* m_empty_string { nullptr };
|
||||||
PrimitiveString* m_single_ascii_character_strings[128] {};
|
PrimitiveString* m_single_ascii_character_strings[128] {};
|
||||||
|
AK::Array<DeprecatedString, to_underlying(ErrorMessage::__Count)> m_error_messages;
|
||||||
|
|
||||||
struct StoredModule {
|
struct StoredModule {
|
||||||
ScriptOrModule referencing_script_or_module;
|
ScriptOrModule referencing_script_or_module;
|
||||||
|
|
Loading…
Add table
Reference in a new issue