diff --git a/Userland/Libraries/LibJS/Runtime/Completion.h b/Userland/Libraries/LibJS/Runtime/Completion.h index b5d089ab84..78e7bc8b87 100644 --- a/Userland/Libraries/LibJS/Runtime/Completion.h +++ b/Userland/Libraries/LibJS/Runtime/Completion.h @@ -17,18 +17,18 @@ namespace JS { -#define TRY_OR_THROW_OOM(vm, expression) \ - ({ \ - /* Ignore -Wshadow to allow nesting the macro. */ \ - AK_IGNORE_DIAGNOSTIC("-Wshadow", \ - auto&& _temporary_result = (expression)); \ - if (_temporary_result.is_error()) { \ - VERIFY(_temporary_result.error().code() == ENOMEM); \ - return (vm).throw_completion(JS::ErrorType::OutOfMemory); \ - } \ - static_assert(!::AK::Detail::IsLvalueReference, \ - "Do not return a reference from a fallible expression"); \ - _temporary_result.release_value(); \ +#define TRY_OR_THROW_OOM(vm, expression) \ + ({ \ + /* Ignore -Wshadow to allow nesting the macro. */ \ + AK_IGNORE_DIAGNOSTIC("-Wshadow", \ + auto&& _temporary_result = (expression)); \ + if (_temporary_result.is_error()) { \ + VERIFY(_temporary_result.error().code() == ENOMEM); \ + return (vm).throw_completion((vm).error_message(::JS::VM::ErrorMessage::OutOfMemory)); \ + } \ + static_assert(!::AK::Detail::IsLvalueReference, \ + "Do not return a reference from a fallible expression"); \ + _temporary_result.release_value(); \ }) #define MUST_OR_THROW_OOM(expression) \ diff --git a/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp b/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp index 763a0513db..256fdb3311 100644 --- a/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp +++ b/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.cpp @@ -16,39 +16,31 @@ ThrowableStringBuilder::ThrowableStringBuilder(VM& vm) ThrowCompletionOr ThrowableStringBuilder::append(char ch) { - if (try_append(ch).is_error()) - return m_vm.throw_completion(ErrorType::NotEnoughMemoryToAllocate, length() + 1); + TRY_OR_THROW_OOM(m_vm, try_append(ch)); return {}; } ThrowCompletionOr ThrowableStringBuilder::append(StringView string) { - if (try_append(string).is_error()) - return m_vm.throw_completion(ErrorType::NotEnoughMemoryToAllocate, length() + string.length()); + TRY_OR_THROW_OOM(m_vm, try_append(string)); return {}; } ThrowCompletionOr ThrowableStringBuilder::append(Utf16View const& string) { - if (try_append(string).is_error()) - return m_vm.throw_completion(ErrorType::NotEnoughMemoryToAllocate, length() + (string.length_in_code_units() * 2)); + TRY_OR_THROW_OOM(m_vm, try_append(string)); return {}; } ThrowCompletionOr ThrowableStringBuilder::append_code_point(u32 value) { - if (auto result = try_append_code_point(value); result.is_error()) - return m_vm.throw_completion(ErrorType::NotEnoughMemoryToAllocate, length() + sizeof(value)); + TRY_OR_THROW_OOM(m_vm, try_append_code_point(value)); return {}; } ThrowCompletionOr ThrowableStringBuilder::to_string() const { - auto result = StringBuilder::to_string(); - if (result.is_error()) - return m_vm.throw_completion(ErrorType::NotEnoughMemoryToAllocate, length()); - - return result.release_value(); + return TRY_OR_THROW_OOM(m_vm, StringBuilder::to_string()); } } diff --git a/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h b/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h index 463b590f35..bbee3e3cbe 100644 --- a/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h +++ b/Userland/Libraries/LibJS/Runtime/ThrowableStringBuilder.h @@ -30,12 +30,7 @@ public: ThrowCompletionOr appendff(CheckedFormatString&& fmtstr, Parameters const&... parameters) { AK::VariadicFormatParams variadic_format_params { parameters... }; - - 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(ErrorType::NotEnoughMemoryToAllocate, length() + fmtstr.view().length()); - } - + TRY_OR_THROW_OOM(m_vm, vformat(*this, fmtstr.view(), variadic_format_params)); return {}; } diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index f4b7e62458..548cb86756 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -153,6 +153,18 @@ VM::VM(OwnPtr 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); JS_ENUMERATE_WELL_KNOWN_SYMBOLS #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() diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index 2009e377f2..b163921b3e 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -91,6 +91,17 @@ public: 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 { // Address sanitizer (ASAN) used to check for more space but @@ -285,6 +296,7 @@ private: PrimitiveString* m_empty_string { nullptr }; PrimitiveString* m_single_ascii_character_strings[128] {}; + AK::Array m_error_messages; struct StoredModule { ScriptOrModule referencing_script_or_module;