2020-03-24 10:37:39 -03:00
/*
* Copyright ( c ) 2020 , Andreas Kling < kling @ serenityos . org >
2023-04-12 19:47:15 -03:00
* Copyright ( c ) 2021 - 2023 , Linus Groh < linusg @ serenityos . org >
2020-03-24 10:37:39 -03:00
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-03-24 10:37:39 -03:00
*/
2022-02-06 13:00:28 -03:00
# include <LibJS/AST.h>
2021-10-02 12:03:45 -03:00
# include <LibJS/Runtime/Completion.h>
2020-03-24 10:37:39 -03:00
# include <LibJS/Runtime/Error.h>
2022-02-06 13:00:28 -03:00
# include <LibJS/Runtime/ExecutionContext.h>
2020-04-17 14:31:48 -03:00
# include <LibJS/Runtime/GlobalObject.h>
2023-02-16 16:09:11 -03:00
# include <LibJS/Runtime/ThrowableStringBuilder.h>
2022-02-06 13:00:28 -03:00
# include <LibJS/SourceRange.h>
2020-03-24 10:37:39 -03:00
namespace JS {
2023-05-28 03:28:43 -03:00
SourceRange const & TracebackFrame : : source_range ( ) const
{
2023-05-28 09:14:14 -03:00
if ( auto * unrealized = source_range_storage . get_pointer < UnrealizedSourceRange > ( ) ) {
auto source_range = [ & ] {
if ( ! unrealized - > source_code ) {
static auto dummy_source_code = SourceCode : : create ( String { } , String { } ) ;
return SourceRange { dummy_source_code , { } , { } } ;
}
return unrealized - > source_code - > range_from_offsets ( unrealized - > start_offset , unrealized - > end_offset ) ;
} ( ) ;
2023-05-28 03:28:43 -03:00
source_range_storage = move ( source_range ) ;
}
return source_range_storage . get < SourceRange > ( ) ;
}
2022-12-13 17:49:50 -03:00
NonnullGCPtr < Error > Error : : create ( Realm & realm )
2021-06-11 16:40:08 -03:00
{
2023-04-12 19:47:15 -03:00
return realm . heap ( ) . allocate < Error > ( realm , realm . intrinsics ( ) . error_prototype ( ) ) . release_allocated_value_but_fixme_should_propagate_errors ( ) ;
2021-06-11 16:40:08 -03:00
}
2023-02-16 16:09:11 -03:00
NonnullGCPtr < Error > Error : : create ( Realm & realm , String message )
2020-04-17 14:31:48 -03:00
{
2022-08-15 20:20:49 -03:00
auto & vm = realm . vm ( ) ;
2022-12-13 17:49:50 -03:00
auto error = Error : : create ( realm ) ;
2021-06-11 16:40:08 -03:00
u8 attr = Attribute : : Writable | Attribute : : Configurable ;
2023-02-16 16:09:11 -03:00
error - > define_direct_property ( vm . names . message , PrimitiveString : : create ( vm , move ( message ) ) , attr ) ;
2021-04-11 19:08:28 -03:00
return error ;
2020-04-17 14:31:48 -03:00
}
2023-02-16 16:09:11 -03:00
ThrowCompletionOr < NonnullGCPtr < Error > > Error : : create ( Realm & realm , StringView message )
{
return create ( realm , TRY_OR_THROW_OOM ( realm . vm ( ) , String : : from_utf8 ( message ) ) ) ;
}
2021-04-11 19:08:28 -03:00
Error : : Error ( Object & prototype )
2022-12-14 08:17:58 -03:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2020-03-24 10:37:39 -03:00
{
2022-02-06 13:00:28 -03:00
populate_stack ( ) ;
2020-03-24 10:37:39 -03:00
}
2021-12-21 10:34:06 -03:00
// 20.5.8.1 InstallErrorCause ( O, options ), https://tc39.es/ecma262/#sec-installerrorcause
2021-10-02 12:03:45 -03:00
ThrowCompletionOr < void > Error : : install_error_cause ( Value options )
2021-06-26 15:06:55 -03:00
{
auto & vm = this - > vm ( ) ;
2021-10-02 12:03:45 -03:00
// 1. If Type(options) is Object and ? HasProperty(options, "cause") is true, then
2021-10-02 22:00:39 -03:00
if ( options . is_object ( ) & & TRY ( options . as_object ( ) . has_property ( vm . names . cause ) ) ) {
2021-10-02 12:03:45 -03:00
// a. Let cause be ? Get(options, "cause").
2021-10-02 19:52:27 -03:00
auto cause = TRY ( options . as_object ( ) . get ( vm . names . cause ) ) ;
2021-10-02 12:03:45 -03:00
2022-05-02 15:54:39 -03:00
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause).
create_non_enumerable_data_property_or_throw ( vm . names . cause , cause ) ;
2021-10-02 12:03:45 -03:00
}
2022-05-02 15:54:39 -03:00
// 2. Return unused.
2021-10-02 12:03:45 -03:00
return { } ;
2021-06-26 15:06:55 -03:00
}
2022-02-06 13:00:28 -03:00
void Error : : populate_stack ( )
{
2022-02-07 10:48:09 -03:00
auto & vm = this - > vm ( ) ;
m_traceback . ensure_capacity ( vm . execution_context_stack ( ) . size ( ) ) ;
for ( ssize_t i = vm . execution_context_stack ( ) . size ( ) - 1 ; i > = 0 ; i - - ) {
2023-02-26 20:09:02 -03:00
auto context = vm . execution_context_stack ( ) [ i ] ;
2022-02-07 10:48:09 -03:00
auto function_name = context - > function_name ;
if ( function_name . is_empty ( ) )
function_name = " <unknown> " sv ;
2023-05-28 03:28:43 -03:00
TracebackFrame frame {
. function_name = move ( function_name ) ,
. source_range_storage = TracebackFrame : : UnrealizedSourceRange { } ,
} ;
// We might not have an AST node associated with the execution context, e.g. in promise
// reaction jobs (which aren't called anywhere from the source code).
// They're not going to generate any _unhandled_ exceptions though, so a meaningless
// source range is fine.
if ( context - > current_node ) {
auto * unrealized = frame . source_range_storage . get_pointer < TracebackFrame : : UnrealizedSourceRange > ( ) ;
unrealized - > source_code = context - > current_node - > source_code ( ) ;
unrealized - > start_offset = context - > current_node - > start_offset ( ) ;
unrealized - > end_offset = context - > current_node - > end_offset ( ) ;
}
m_traceback . append ( move ( frame ) ) ;
2022-02-07 10:48:09 -03:00
}
}
2022-02-06 13:00:28 -03:00
2023-02-16 16:09:11 -03:00
ThrowCompletionOr < String > Error : : stack_string ( VM & vm ) const
2022-02-07 10:48:09 -03:00
{
2023-02-16 16:09:11 -03:00
ThrowableStringBuilder stack_string_builder ( vm ) ;
2022-02-06 13:00:28 -03:00
// Note: We roughly follow V8's formatting
// Note: The error's name and message get prepended by ErrorPrototype::stack
2022-02-08 15:17:12 -03:00
// Note: We don't want to capture the global execution context, so we omit the last frame
2022-02-06 13:00:28 -03:00
// FIXME: We generate a stack-frame for the Errors constructor, other engines do not
2022-02-07 10:48:09 -03:00
for ( size_t i = 0 ; i < m_traceback . size ( ) - 1 ; + + i ) {
auto const & frame = m_traceback [ i ] ;
auto function_name = frame . function_name ;
2023-05-28 03:28:43 -03:00
auto source_range = frame . source_range ( ) ;
2022-02-07 10:48:09 -03:00
// Note: Since we don't know whether we have a valid SourceRange here we just check for some default values.
2023-05-28 03:28:43 -03:00
if ( ! source_range . filename ( ) . is_empty ( ) | | source_range . start . offset ! = 0 | | source_range . end . offset ! = 0 ) {
2022-02-06 13:00:28 -03:00
2022-02-07 10:48:09 -03:00
if ( function_name = = " <unknown> " sv )
2023-05-28 03:28:43 -03:00
MUST_OR_THROW_OOM ( stack_string_builder . appendff ( " at {}:{}:{} \n " , source_range . filename ( ) , source_range . start . line , source_range . start . column ) ) ;
2022-02-06 13:00:28 -03:00
else
2023-05-28 03:28:43 -03:00
MUST_OR_THROW_OOM ( stack_string_builder . appendff ( " at {} ({}:{}:{}) \n " , function_name , source_range . filename ( ) , source_range . start . line , source_range . start . column ) ) ;
2022-02-06 13:00:28 -03:00
} else {
2023-02-16 16:09:11 -03:00
MUST_OR_THROW_OOM ( stack_string_builder . appendff ( " at {} \n " , function_name . is_empty ( ) ? " <unknown> " sv : function_name . view ( ) ) ) ;
2022-02-06 13:00:28 -03:00
}
}
2023-02-16 16:09:11 -03:00
return stack_string_builder . to_string ( ) ;
2022-02-06 13:00:28 -03:00
}
2023-04-12 19:47:15 -03:00
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
NonnullGCPtr < ClassName > ClassName : : create ( Realm & realm ) \
{ \
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 , 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 , 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 ) \
{ \
2021-04-11 19:08:28 -03:00
}
2020-04-10 07:42:33 -03:00
2021-06-11 13:54:42 -03:00
JS_ENUMERATE_NATIVE_ERRORS
2020-04-10 09:06:52 -03:00
# undef __JS_ENUMERATE
2020-04-10 07:42:33 -03:00
2020-03-24 10:37:39 -03:00
}