2020-03-24 14:37:39 +01:00
/*
* Copyright ( c ) 2020 , Andreas Kling < kling @ serenityos . org >
2022-05-02 20:54:39 +02:00
* Copyright ( c ) 2021 - 2022 , 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>
2022-02-06 17:00:28 +01:00
# include <LibJS/SourceRange.h>
2020-03-24 14:37:39 +01:00
namespace JS {
2022-12-13 20:49:50 +00:00
NonnullGCPtr < Error > Error : : create ( Realm & realm )
2021-06-11 20:40:08 +01:00
{
2023-01-28 13:39:44 -05:00
return realm . heap ( ) . allocate < Error > ( realm , * realm . intrinsics ( ) . error_prototype ( ) ) . release_allocated_value_but_fixme_should_propagate_errors ( ) ;
2021-06-11 20:40:08 +01:00
}
2022-12-13 20:49:50 +00:00
NonnullGCPtr < Error > Error : : create ( Realm & realm , DeprecatedString const & 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 ;
2022-12-06 22:17:27 +00:00
error - > define_direct_property ( vm . names . message , PrimitiveString : : create ( vm , message ) , attr ) ;
2021-04-12 00:08:28 +02:00
return error ;
2020-04-17 19:31:48 +02:00
}
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 ( )
{
2023-01-26 08:33:18 -05:00
static auto dummy_source_range = SourceRange { . code = SourceCode : : create ( String { } , String { } ) , . start = { } , . end = { } } ;
LibJS: Reduce AST memory usage by shrink-wrapping source range info
Before this change, each AST node had a 64-byte SourceRange member.
This SourceRange had the following layout:
filename: StringView (16 bytes)
start: Position (24 bytes)
end: Position (24 bytes)
The Position structs have { line, column, offset }, all members size_t.
To reduce memory consumption, AST nodes now only store the following:
source_code: NonnullRefPtr<SourceCode> (8 bytes)
start_offset: u32 (4 bytes)
end_offset: u32 (4 bytes)
SourceCode is a new ref-counted data structure that keeps the filename
and original parsed source code in a single location, and all AST nodes
have a pointer to it.
The start_offset and end_offset can be turned into (line, column) when
necessary by calling SourceCode::range_from_offsets(). This will walk
the source code string and compute line/column numbers on the fly, so
it's not necessarily fast, but it should be rare since this information
is primarily used for diagnostics and exception stack traces.
With this, ASTNode shrinks from 80 bytes to 32 bytes. This gives us a
~23% reduction in memory usage when loading twitter.com/awesomekling
(330 MiB before, 253 MiB after!) :^)
2022-11-21 17:37:38 +01:00
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 - - ) {
auto * context = vm . execution_context_stack ( ) [ i ] ;
auto function_name = context - > function_name ;
if ( function_name . is_empty ( ) )
function_name = " <unknown> " sv ;
m_traceback . empend (
move ( function_name ) ,
// 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.
LibJS: Reduce AST memory usage by shrink-wrapping source range info
Before this change, each AST node had a 64-byte SourceRange member.
This SourceRange had the following layout:
filename: StringView (16 bytes)
start: Position (24 bytes)
end: Position (24 bytes)
The Position structs have { line, column, offset }, all members size_t.
To reduce memory consumption, AST nodes now only store the following:
source_code: NonnullRefPtr<SourceCode> (8 bytes)
start_offset: u32 (4 bytes)
end_offset: u32 (4 bytes)
SourceCode is a new ref-counted data structure that keeps the filename
and original parsed source code in a single location, and all AST nodes
have a pointer to it.
The start_offset and end_offset can be turned into (line, column) when
necessary by calling SourceCode::range_from_offsets(). This will walk
the source code string and compute line/column numbers on the fly, so
it's not necessarily fast, but it should be rare since this information
is primarily used for diagnostics and exception stack traces.
With this, ASTNode shrinks from 80 bytes to 32 bytes. This gives us a
~23% reduction in memory usage when loading twitter.com/awesomekling
(330 MiB before, 253 MiB after!) :^)
2022-11-21 17:37:38 +01:00
context - > current_node ? context - > current_node - > source_range ( ) : dummy_source_range ) ;
2022-02-07 14:48:09 +01:00
}
}
2022-02-06 17:00:28 +01:00
2022-12-04 18:02:33 +00:00
DeprecatedString Error : : stack_string ( ) const
2022-02-07 14:48:09 +01:00
{
StringBuilder stack_string_builder ;
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 ;
// Note: Since we don't know whether we have a valid SourceRange here we just check for some default values.
2023-01-26 08:33:18 -05:00
if ( ! frame . source_range . filename ( ) . is_empty ( ) | | frame . source_range . start . offset ! = 0 | | frame . 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 )
LibJS: Reduce AST memory usage by shrink-wrapping source range info
Before this change, each AST node had a 64-byte SourceRange member.
This SourceRange had the following layout:
filename: StringView (16 bytes)
start: Position (24 bytes)
end: Position (24 bytes)
The Position structs have { line, column, offset }, all members size_t.
To reduce memory consumption, AST nodes now only store the following:
source_code: NonnullRefPtr<SourceCode> (8 bytes)
start_offset: u32 (4 bytes)
end_offset: u32 (4 bytes)
SourceCode is a new ref-counted data structure that keeps the filename
and original parsed source code in a single location, and all AST nodes
have a pointer to it.
The start_offset and end_offset can be turned into (line, column) when
necessary by calling SourceCode::range_from_offsets(). This will walk
the source code string and compute line/column numbers on the fly, so
it's not necessarily fast, but it should be rare since this information
is primarily used for diagnostics and exception stack traces.
With this, ASTNode shrinks from 80 bytes to 32 bytes. This gives us a
~23% reduction in memory usage when loading twitter.com/awesomekling
(330 MiB before, 253 MiB after!) :^)
2022-11-21 17:37:38 +01:00
stack_string_builder . appendff ( " at {}:{}:{} \n " , frame . source_range . filename ( ) , frame . source_range . start . line , frame . source_range . start . column ) ;
2022-02-06 17:00:28 +01:00
else
LibJS: Reduce AST memory usage by shrink-wrapping source range info
Before this change, each AST node had a 64-byte SourceRange member.
This SourceRange had the following layout:
filename: StringView (16 bytes)
start: Position (24 bytes)
end: Position (24 bytes)
The Position structs have { line, column, offset }, all members size_t.
To reduce memory consumption, AST nodes now only store the following:
source_code: NonnullRefPtr<SourceCode> (8 bytes)
start_offset: u32 (4 bytes)
end_offset: u32 (4 bytes)
SourceCode is a new ref-counted data structure that keeps the filename
and original parsed source code in a single location, and all AST nodes
have a pointer to it.
The start_offset and end_offset can be turned into (line, column) when
necessary by calling SourceCode::range_from_offsets(). This will walk
the source code string and compute line/column numbers on the fly, so
it's not necessarily fast, but it should be rare since this information
is primarily used for diagnostics and exception stack traces.
With this, ASTNode shrinks from 80 bytes to 32 bytes. This gives us a
~23% reduction in memory usage when loading twitter.com/awesomekling
(330 MiB before, 253 MiB after!) :^)
2022-11-21 17:37:38 +01:00
stack_string_builder . appendff ( " at {} ({}:{}:{}) \n " , function_name , frame . source_range . filename ( ) , frame . source_range . start . line , frame . source_range . start . column ) ;
2022-02-06 17:00:28 +01:00
} else {
stack_string_builder . appendff ( " at {} \n " , function_name . is_empty ( ) ? " <unknown> " sv : function_name . view ( ) ) ;
}
}
2023-01-26 18:58:09 +00:00
return stack_string_builder . to_deprecated_string ( ) ;
2022-02-06 17:00:28 +01:00
}
2023-01-28 13:39:44 -05: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 , DeprecatedString const & 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 ) ; \
return error ; \
} \
\
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
}