mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 23:07:35 +00:00
LibJS+Everywhere: Convert JS::Error to String
This includes an Error::create overload to create an Error from a UTF-8 StringView. If creating a String from that view fails, the factory will return an OOM InternalError instead. VM::throw_completion can also make use of this overload via its perfect forwarding.
This commit is contained in:
parent
153b793638
commit
88814acbd3
36 changed files with 198 additions and 151 deletions
|
@ -602,7 +602,7 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
|
|||
// b. If script is a List of errors, throw a SyntaxError exception.
|
||||
if (parser.has_errors()) {
|
||||
auto& error = parser.errors()[0];
|
||||
return vm.throw_completion<SyntaxError>(error.to_deprecated_string());
|
||||
return vm.throw_completion<SyntaxError>(TRY_OR_THROW_OOM(vm, error.to_string()));
|
||||
}
|
||||
|
||||
bool strict_eval = false;
|
||||
|
@ -704,7 +704,7 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
|
|||
if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) {
|
||||
auto executable_result = Bytecode::Generator::generate(program);
|
||||
if (executable_result.is_error())
|
||||
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, executable_result.error().to_deprecated_string());
|
||||
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
|
||||
|
||||
auto executable = executable_result.release_value();
|
||||
executable->name = "eval"sv;
|
||||
|
|
|
@ -137,7 +137,7 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::return_)
|
|||
|
||||
// 11. If Type(result) is not Object, then
|
||||
if (!result.is_object()) {
|
||||
auto error = TypeError::create(realm, DeprecatedString::formatted(ErrorType::NotAnObject.message(), "SyncIteratorReturnResult"));
|
||||
auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "SyncIteratorReturnResult")));
|
||||
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
|
||||
// b. Return promiseCapability.[[Promise]].
|
||||
|
@ -185,7 +185,8 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::throw_)
|
|||
|
||||
// 11. If Type(result) is not Object, then
|
||||
if (!result.is_object()) {
|
||||
auto error = TypeError::create(realm, DeprecatedString::formatted(ErrorType::NotAnObject.message(), "SyncIteratorThrowResult"));
|
||||
auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "SyncIteratorThrowResult")));
|
||||
|
||||
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
|
||||
|
||||
|
|
|
@ -824,7 +824,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
|
|||
auto compile = [&](auto& node, auto kind, auto name) -> ThrowCompletionOr<NonnullOwnPtr<Bytecode::Executable>> {
|
||||
auto executable_result = Bytecode::Generator::generate(node, kind);
|
||||
if (executable_result.is_error())
|
||||
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, executable_result.error().to_deprecated_string());
|
||||
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
|
||||
|
||||
auto bytecode_executable = executable_result.release_value();
|
||||
bytecode_executable->name = name;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <LibJS/Runtime/Error.h>
|
||||
#include <LibJS/Runtime/ExecutionContext.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/ThrowableStringBuilder.h>
|
||||
#include <LibJS/SourceRange.h>
|
||||
|
||||
namespace JS {
|
||||
|
@ -19,15 +20,20 @@ NonnullGCPtr<Error> Error::create(Realm& realm)
|
|||
return realm.heap().allocate<Error>(realm, *realm.intrinsics().error_prototype()).release_allocated_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
NonnullGCPtr<Error> Error::create(Realm& realm, DeprecatedString const& message)
|
||||
NonnullGCPtr<Error> Error::create(Realm& realm, String message)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
auto error = Error::create(realm);
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
error->define_direct_property(vm.names.message, PrimitiveString::create(vm, message), attr);
|
||||
error->define_direct_property(vm.names.message, PrimitiveString::create(vm, move(message)), attr);
|
||||
return error;
|
||||
}
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<Error>> Error::create(Realm& realm, StringView message)
|
||||
{
|
||||
return create(realm, TRY_OR_THROW_OOM(realm.vm(), String::from_utf8(message)));
|
||||
}
|
||||
|
||||
Error::Error(Object& prototype)
|
||||
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
||||
{
|
||||
|
@ -73,9 +79,10 @@ void Error::populate_stack()
|
|||
}
|
||||
}
|
||||
|
||||
DeprecatedString Error::stack_string() const
|
||||
ThrowCompletionOr<String> Error::stack_string(VM& vm) const
|
||||
{
|
||||
StringBuilder stack_string_builder;
|
||||
ThrowableStringBuilder stack_string_builder(vm);
|
||||
|
||||
// Note: We roughly follow V8's formatting
|
||||
// Note: The error's name and message get prepended by ErrorPrototype::stack
|
||||
// Note: We don't want to capture the global execution context, so we omit the last frame
|
||||
|
@ -87,15 +94,15 @@ DeprecatedString Error::stack_string() const
|
|||
if (!frame.source_range.filename().is_empty() || frame.source_range.start.offset != 0 || frame.source_range.end.offset != 0) {
|
||||
|
||||
if (function_name == "<unknown>"sv)
|
||||
stack_string_builder.appendff(" at {}:{}:{}\n", frame.source_range.filename(), frame.source_range.start.line, frame.source_range.start.column);
|
||||
MUST_OR_THROW_OOM(stack_string_builder.appendff(" at {}:{}:{}\n", frame.source_range.filename(), frame.source_range.start.line, frame.source_range.start.column));
|
||||
else
|
||||
stack_string_builder.appendff(" at {} ({}:{}:{})\n", function_name, frame.source_range.filename(), frame.source_range.start.line, frame.source_range.start.column);
|
||||
MUST_OR_THROW_OOM(stack_string_builder.appendff(" at {} ({}:{}:{})\n", function_name, frame.source_range.filename(), frame.source_range.start.line, frame.source_range.start.column));
|
||||
} else {
|
||||
stack_string_builder.appendff(" at {}\n", function_name.is_empty() ? "<unknown>"sv : function_name.view());
|
||||
MUST_OR_THROW_OOM(stack_string_builder.appendff(" at {}\n", function_name.is_empty() ? "<unknown>"sv : function_name.view()));
|
||||
}
|
||||
}
|
||||
|
||||
return stack_string_builder.to_deprecated_string();
|
||||
return stack_string_builder.to_string();
|
||||
}
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
|
@ -104,15 +111,20 @@ DeprecatedString Error::stack_string() const
|
|||
return realm.heap().allocate<ClassName>(realm, *realm.intrinsics().snake_name##_prototype()).release_allocated_value_but_fixme_should_propagate_errors(); \
|
||||
} \
|
||||
\
|
||||
NonnullGCPtr<ClassName> ClassName::create(Realm& realm, DeprecatedString const& message) \
|
||||
NonnullGCPtr<ClassName> ClassName::create(Realm& realm, String message) \
|
||||
{ \
|
||||
auto& vm = realm.vm(); \
|
||||
auto error = ClassName::create(realm); \
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable; \
|
||||
error->define_direct_property(vm.names.message, PrimitiveString::create(vm, message), attr); \
|
||||
error->define_direct_property(vm.names.message, PrimitiveString::create(vm, move(message)), attr); \
|
||||
return error; \
|
||||
} \
|
||||
\
|
||||
ThrowCompletionOr<NonnullGCPtr<ClassName>> ClassName::create(Realm& realm, StringView message) \
|
||||
{ \
|
||||
return create(realm, TRY_OR_THROW_OOM(realm.vm(), String::from_utf8(message))); \
|
||||
} \
|
||||
\
|
||||
ClassName::ClassName(Object& prototype) \
|
||||
: Error(prototype) \
|
||||
{ \
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/DeprecatedFlyString.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/SourceRange.h>
|
||||
|
@ -24,11 +25,12 @@ class Error : public Object {
|
|||
|
||||
public:
|
||||
static NonnullGCPtr<Error> create(Realm&);
|
||||
static NonnullGCPtr<Error> create(Realm&, DeprecatedString const& message);
|
||||
static NonnullGCPtr<Error> create(Realm&, String message);
|
||||
static ThrowCompletionOr<NonnullGCPtr<Error>> create(Realm&, StringView message);
|
||||
|
||||
virtual ~Error() override = default;
|
||||
|
||||
[[nodiscard]] DeprecatedString stack_string() const;
|
||||
[[nodiscard]] ThrowCompletionOr<String> stack_string(VM&) const;
|
||||
|
||||
ThrowCompletionOr<void> install_error_cause(Value options);
|
||||
|
||||
|
@ -45,16 +47,17 @@ private:
|
|||
// NOTE: Making these inherit from Error is not required by the spec but
|
||||
// our way of implementing the [[ErrorData]] internal slot, which is
|
||||
// used in Object.prototype.toString().
|
||||
#define DECLARE_NATIVE_ERROR(ClassName, snake_name, PrototypeName, ConstructorName) \
|
||||
class ClassName final : public Error { \
|
||||
JS_OBJECT(ClassName, Error); \
|
||||
\
|
||||
public: \
|
||||
static NonnullGCPtr<ClassName> create(Realm&); \
|
||||
static NonnullGCPtr<ClassName> create(Realm&, DeprecatedString const& message); \
|
||||
\
|
||||
explicit ClassName(Object& prototype); \
|
||||
virtual ~ClassName() override = default; \
|
||||
#define DECLARE_NATIVE_ERROR(ClassName, snake_name, PrototypeName, ConstructorName) \
|
||||
class ClassName final : public Error { \
|
||||
JS_OBJECT(ClassName, Error); \
|
||||
\
|
||||
public: \
|
||||
static NonnullGCPtr<ClassName> create(Realm&); \
|
||||
static NonnullGCPtr<ClassName> create(Realm&, String message); \
|
||||
static ThrowCompletionOr<NonnullGCPtr<ClassName>> create(Realm&, StringView message); \
|
||||
\
|
||||
explicit ClassName(Object& prototype); \
|
||||
virtual ~ClassName() override = default; \
|
||||
};
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
|
|
|
@ -100,7 +100,7 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::stack_getter)
|
|||
if (!message.is_empty())
|
||||
header = DeprecatedString::formatted("{}: {}", name, message);
|
||||
|
||||
return PrimitiveString::create(vm, DeprecatedString::formatted("{}\n{}", header, error.stack_string()));
|
||||
return PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, String::formatted("{}\n{}", header, MUST_OR_THROW_OOM(error.stack_string(vm)))));
|
||||
}
|
||||
|
||||
// B.1.2 set Error.prototype.stack ( value ), https://tc39.es/proposal-error-stacks/#sec-set-error.prototype-stack
|
||||
|
|
|
@ -178,7 +178,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
|
|||
// 17. If parameters is a List of errors, throw a SyntaxError exception.
|
||||
if (parameters_parser.has_errors()) {
|
||||
auto error = parameters_parser.errors()[0];
|
||||
return vm.throw_completion<SyntaxError>(error.to_deprecated_string());
|
||||
return vm.throw_completion<SyntaxError>(TRY_OR_THROW_OOM(vm, error.to_string()));
|
||||
}
|
||||
|
||||
// 18. Let body be ParseText(StringToCodePoints(bodyString), bodySym).
|
||||
|
@ -195,7 +195,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
|
|||
// 19. If body is a List of errors, throw a SyntaxError exception.
|
||||
if (body_parser.has_errors()) {
|
||||
auto error = body_parser.errors()[0];
|
||||
return vm.throw_completion<SyntaxError>(error.to_deprecated_string());
|
||||
return vm.throw_completion<SyntaxError>(TRY_OR_THROW_OOM(vm, error.to_string()));
|
||||
}
|
||||
|
||||
// 20. NOTE: The parameters and body are parsed separately to ensure that each is valid alone. For example, new Function("/*", "*/ ) {") is not legal.
|
||||
|
@ -209,7 +209,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
|
|||
// 23. If expr is a List of errors, throw a SyntaxError exception.
|
||||
if (source_parser.has_errors()) {
|
||||
auto error = source_parser.errors()[0];
|
||||
return vm.throw_completion<SyntaxError>(error.to_deprecated_string());
|
||||
return vm.throw_completion<SyntaxError>(TRY_OR_THROW_OOM(vm, error.to_string()));
|
||||
}
|
||||
|
||||
// 24. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
|
||||
|
|
|
@ -71,7 +71,7 @@ Promise::ResolvingFunctions Promise::create_resolving_functions()
|
|||
// 6. Set resolve.[[AlreadyResolved]] to alreadyResolved.
|
||||
|
||||
// 27.2.1.3.2 Promise Resolve Functions, https://tc39.es/ecma262/#sec-promise-resolve-functions
|
||||
auto resolve_function = PromiseResolvingFunction::create(realm, *this, *already_resolved, [](auto& vm, auto& promise, auto& already_resolved) {
|
||||
auto resolve_function = PromiseResolvingFunction::create(realm, *this, *already_resolved, [](auto& vm, auto& promise, auto& already_resolved) -> ThrowCompletionOr<Value> {
|
||||
dbgln_if(PROMISE_DEBUG, "[Promise @ {} / PromiseResolvingFunction]: Resolve function was called", &promise);
|
||||
|
||||
auto& realm = *vm.current_realm();
|
||||
|
@ -97,7 +97,7 @@ Promise::ResolvingFunctions Promise::create_resolving_functions()
|
|||
dbgln_if(PROMISE_DEBUG, "[Promise @ {} / PromiseResolvingFunction]: Promise can't be resolved with itself, rejecting with error", &promise);
|
||||
|
||||
// a. Let selfResolutionError be a newly created TypeError object.
|
||||
auto self_resolution_error = TypeError::create(realm, "Cannot resolve promise with itself");
|
||||
auto self_resolution_error = MUST_OR_THROW_OOM(TypeError::create(realm, "Cannot resolve promise with itself"sv));
|
||||
|
||||
// b. Perform RejectPromise(promise, selfResolutionError).
|
||||
promise.reject(self_resolution_error);
|
||||
|
|
|
@ -107,7 +107,7 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, StringView source_tex
|
|||
// b. If script is a List of errors, throw a SyntaxError exception.
|
||||
if (parser.has_errors()) {
|
||||
auto& error = parser.errors()[0];
|
||||
return vm.throw_completion<SyntaxError>(error.to_deprecated_string());
|
||||
return vm.throw_completion<SyntaxError>(TRY_OR_THROW_OOM(vm, error.to_string()));
|
||||
}
|
||||
|
||||
// c. If script Contains ScriptBody is false, return undefined.
|
||||
|
@ -272,7 +272,7 @@ ThrowCompletionOr<Value> shadow_realm_import_value(VM& vm, DeprecatedString spec
|
|||
// NOTE: Even though the spec tells us to use %ThrowTypeError%, it's not observable if we actually do.
|
||||
// Throw a nicer TypeError forwarding the import error message instead (we know the argument is an Error object).
|
||||
auto throw_type_error = NativeFunction::create(realm, {}, [](auto& vm) -> ThrowCompletionOr<Value> {
|
||||
return vm.template throw_completion<TypeError>(TRY(vm.argument(0).as_object().get_without_side_effects(vm.names.message).as_string().deprecated_string()));
|
||||
return vm.template throw_completion<TypeError>(TRY(vm.argument(0).as_object().get_without_side_effects(vm.names.message).as_string().utf8_string()));
|
||||
});
|
||||
|
||||
// 13. Return PerformPromiseThen(innerCapability.[[Promise]], onFulfilled, callerRealm.[[Intrinsics]].[[%ThrowTypeError%]], promiseCapability).
|
||||
|
|
|
@ -90,7 +90,7 @@ VM::VM(OwnPtr<CustomData> custom_data)
|
|||
// If you are here because you want to enable dynamic module importing make sure it won't be a security problem
|
||||
// by checking the default implementation of HostImportModuleDynamically and creating your own hook or calling
|
||||
// vm.enable_default_host_import_module_dynamically_hook().
|
||||
promise->reject(Error::create(realm, ErrorType::DynamicImportNotAllowed.message()));
|
||||
promise->reject(Error::create(realm, ErrorType::DynamicImportNotAllowed.message()).release_allocated_value_but_fixme_should_propagate_errors());
|
||||
|
||||
promise->perform_then(
|
||||
NativeFunction::create(realm, "", [](auto&) -> ThrowCompletionOr<Value> {
|
||||
|
@ -154,10 +154,10 @@ VM::VM(OwnPtr<CustomData> custom_data)
|
|||
JS_ENUMERATE_WELL_KNOWN_SYMBOLS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
m_error_messages[to_underlying(ErrorMessage::OutOfMemory)] = ErrorType::OutOfMemory.message();
|
||||
m_error_messages[to_underlying(ErrorMessage::OutOfMemory)] = String::from_utf8(ErrorType::OutOfMemory.message()).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
DeprecatedString const& VM::error_message(ErrorMessage type) const
|
||||
String const& VM::error_message(ErrorMessage type) const
|
||||
{
|
||||
VERIFY(type < ErrorMessage::__Count);
|
||||
|
||||
|
@ -1001,7 +1001,7 @@ void VM::import_module_dynamically(ScriptOrModule referencing_script_or_module,
|
|||
// If there is no ScriptOrModule in any of the execution contexts
|
||||
if (referencing_script_or_module.has<Empty>()) {
|
||||
// Throw an error for now
|
||||
promise->reject(InternalError::create(realm, DeprecatedString::formatted(ErrorType::ModuleNotFoundNoReferencingScript.message(), module_request.module_specifier)));
|
||||
promise->reject(InternalError::create(realm, String::formatted(ErrorType::ModuleNotFoundNoReferencingScript.message(), module_request.module_specifier).release_value_but_fixme_should_propagate_errors()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ public:
|
|||
// Keep this last:
|
||||
__Count,
|
||||
};
|
||||
DeprecatedString const& error_message(ErrorMessage) const;
|
||||
String const& error_message(ErrorMessage) const;
|
||||
|
||||
bool did_reach_stack_space_limit() const
|
||||
{
|
||||
|
@ -201,7 +201,12 @@ public:
|
|||
Completion throw_completion(Args&&... args)
|
||||
{
|
||||
auto& realm = *current_realm();
|
||||
return JS::throw_completion(T::create(realm, forward<Args>(args)...));
|
||||
auto completion = T::create(realm, forward<Args>(args)...);
|
||||
|
||||
if constexpr (IsSame<decltype(completion), ThrowCompletionOr<NonnullGCPtr<T>>>)
|
||||
return JS::throw_completion(MUST_OR_THROW_OOM(completion));
|
||||
else
|
||||
return JS::throw_completion(completion);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
|
@ -296,7 +301,7 @@ private:
|
|||
|
||||
PrimitiveString* m_empty_string { nullptr };
|
||||
PrimitiveString* m_single_ascii_character_strings[128] {};
|
||||
AK::Array<DeprecatedString, to_underlying(ErrorMessage::__Count)> m_error_messages;
|
||||
AK::Array<String, to_underlying(ErrorMessage::__Count)> m_error_messages;
|
||||
|
||||
struct StoredModule {
|
||||
ScriptOrModule referencing_script_or_module;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue