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