2020-03-07 19:42:11 +01:00
|
|
|
/*
|
2024-10-04 13:19:50 +02:00
|
|
|
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
2023-02-11 16:02:14 +00:00
|
|
|
* Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
|
2022-01-18 19:07:13 +01:00
|
|
|
* Copyright (c) 2021-2022, David Tuin <davidot@serenityos.org>
|
2020-03-07 19:42:11 +01:00
|
|
|
*
|
2021-04-22 01:24:48 -07:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-03-07 19:42:11 +01:00
|
|
|
*/
|
|
|
|
|
|
2021-01-17 09:21:15 +01:00
|
|
|
#include <AK/Demangle.h>
|
2020-09-18 18:00:57 +04:30
|
|
|
#include <AK/HashTable.h>
|
2022-01-27 02:44:03 +01:00
|
|
|
#include <AK/QuickSort.h>
|
2020-11-28 16:14:26 +01:00
|
|
|
#include <AK/TemporaryChange.h>
|
2020-06-06 01:14:10 +01:00
|
|
|
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
2020-03-07 19:42:11 +01:00
|
|
|
#include <LibJS/AST.h>
|
2021-09-24 22:40:38 +02:00
|
|
|
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
2020-03-24 14:37:39 +01:00
|
|
|
#include <LibJS/Runtime/Error.h>
|
2023-08-07 19:59:00 +02:00
|
|
|
#include <LibJS/Runtime/GlobalEnvironment.h>
|
2020-04-08 11:05:38 +02:00
|
|
|
#include <LibJS/Runtime/GlobalObject.h>
|
2026-02-11 00:43:51 +01:00
|
|
|
#include <LibJS/Runtime/SharedFunctionInstanceData.h>
|
2023-10-06 17:54:21 +02:00
|
|
|
#include <LibJS/Runtime/ValueInlines.h>
|
2021-02-10 12:21:14 +01:00
|
|
|
#include <typeinfo>
|
2020-03-07 19:42:11 +01:00
|
|
|
|
|
|
|
|
namespace JS {
|
|
|
|
|
|
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
|
|
|
ASTNode::ASTNode(SourceRange source_range)
|
2025-12-27 20:58:36 +01:00
|
|
|
: m_source_range(move(source_range))
|
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
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-16 17:49:34 +03:30
|
|
|
ByteString ASTNode::class_name() const
|
2021-01-17 09:21:15 +01:00
|
|
|
{
|
|
|
|
|
// NOTE: We strip the "JS::" prefix.
|
2022-07-11 19:53:29 +00:00
|
|
|
auto const* typename_ptr = typeid(*this).name();
|
|
|
|
|
return demangle({ typename_ptr, strlen(typename_ptr) }).substring(4);
|
2021-01-17 09:21:15 +01:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 11:12:58 -04:00
|
|
|
Optional<Utf16String> CallExpression::expression_string() const
|
2020-06-08 13:31:21 -05:00
|
|
|
{
|
2023-08-07 19:59:00 +02:00
|
|
|
if (is<Identifier>(*m_callee))
|
2025-08-06 11:12:58 -04:00
|
|
|
return static_cast<Identifier const&>(*m_callee).string().to_utf16_string();
|
2023-08-07 19:59:00 +02:00
|
|
|
|
|
|
|
|
if (is<MemberExpression>(*m_callee))
|
|
|
|
|
return static_cast<MemberExpression const&>(*m_callee).to_string_approximation();
|
|
|
|
|
|
|
|
|
|
return {};
|
2020-06-08 13:31:21 -05:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 11:12:58 -04:00
|
|
|
static Optional<Utf16FlyString> nullopt_or_private_identifier_description(Expression const& expression)
|
2021-10-12 22:45:52 +02:00
|
|
|
{
|
|
|
|
|
if (is<PrivateIdentifier>(expression))
|
|
|
|
|
return static_cast<PrivateIdentifier const&>(expression).string();
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 11:12:58 -04:00
|
|
|
Optional<Utf16FlyString> ClassField::private_bound_identifier() const
|
2021-10-12 22:45:52 +02:00
|
|
|
{
|
|
|
|
|
return nullopt_or_private_identifier_description(*m_key);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 11:12:58 -04:00
|
|
|
Optional<Utf16FlyString> ClassMethod::private_bound_identifier() const
|
2021-10-12 22:45:52 +02:00
|
|
|
{
|
|
|
|
|
return nullopt_or_private_identifier_description(*m_key);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-02 01:04:07 +03:00
|
|
|
ThrowCompletionOr<void> ClassDeclaration::for_each_bound_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_class_expression->m_name)
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
return callback(*m_class_expression->m_name);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-22 12:44:56 +02:00
|
|
|
bool BindingPattern::contains_expression() const
|
|
|
|
|
{
|
|
|
|
|
for (auto& entry : entries) {
|
2023-09-20 01:19:09 +02:00
|
|
|
if (entry.name.has<NonnullRefPtr<Expression const>>())
|
|
|
|
|
return true;
|
2021-09-22 12:44:56 +02:00
|
|
|
if (entry.initializer)
|
|
|
|
|
return true;
|
2023-02-19 22:07:52 +01:00
|
|
|
if (auto binding_ptr = entry.alias.get_pointer<NonnullRefPtr<BindingPattern const>>(); binding_ptr && (*binding_ptr)->contains_expression())
|
2021-09-22 12:44:56 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-02 01:04:07 +03:00
|
|
|
ThrowCompletionOr<void> BindingPattern::for_each_bound_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const
|
|
|
|
|
{
|
|
|
|
|
for (auto const& entry : entries) {
|
|
|
|
|
auto const& alias = entry.alias;
|
|
|
|
|
if (alias.has<NonnullRefPtr<Identifier const>>()) {
|
|
|
|
|
TRY(callback(alias.get<NonnullRefPtr<Identifier const>>()));
|
|
|
|
|
} else if (alias.has<NonnullRefPtr<BindingPattern const>>()) {
|
|
|
|
|
TRY(alias.get<NonnullRefPtr<BindingPattern const>>()->for_each_bound_identifier(forward<decltype(callback)>(callback)));
|
|
|
|
|
} else {
|
|
|
|
|
auto const& name = entry.name;
|
|
|
|
|
if (name.has<NonnullRefPtr<Identifier const>>())
|
|
|
|
|
TRY(callback(name.get<NonnullRefPtr<Identifier const>>()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 11:27:47 +01:00
|
|
|
FunctionNode::FunctionNode(RefPtr<Identifier const> name, Utf16View source_text, NonnullRefPtr<Statement const> body, NonnullRefPtr<FunctionParameters const> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights parsing_insights, bool is_arrow_function)
|
2025-04-08 00:45:27 +02:00
|
|
|
: m_name(move(name))
|
|
|
|
|
, m_source_text(move(source_text))
|
|
|
|
|
, m_body(move(body))
|
|
|
|
|
, m_parameters(move(parameters))
|
|
|
|
|
, m_function_length(function_length)
|
|
|
|
|
, m_kind(kind)
|
|
|
|
|
, m_is_strict_mode(is_strict_mode)
|
|
|
|
|
, m_is_arrow_function(is_arrow_function)
|
|
|
|
|
, m_parsing_insights(parsing_insights)
|
|
|
|
|
{
|
|
|
|
|
if (m_is_arrow_function)
|
|
|
|
|
VERIFY(!parsing_insights.might_need_arguments_object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FunctionNode::~FunctionNode() = default;
|
|
|
|
|
|
2023-07-02 01:04:07 +03:00
|
|
|
ThrowCompletionOr<void> FunctionDeclaration::for_each_bound_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_name)
|
|
|
|
|
return {};
|
|
|
|
|
return callback(*m_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ThrowCompletionOr<void> VariableDeclaration::for_each_bound_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const
|
|
|
|
|
{
|
|
|
|
|
for (auto const& entry : declarations()) {
|
|
|
|
|
TRY(entry->target().visit(
|
|
|
|
|
[&](NonnullRefPtr<Identifier const> const& id) {
|
|
|
|
|
return callback(id);
|
|
|
|
|
},
|
|
|
|
|
[&](NonnullRefPtr<BindingPattern const> const& binding) {
|
|
|
|
|
return binding->for_each_bound_identifier([&](auto const& id) {
|
|
|
|
|
return callback(id);
|
|
|
|
|
});
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ThrowCompletionOr<void> UsingDeclaration::for_each_bound_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const
|
|
|
|
|
{
|
|
|
|
|
for (auto const& entry : m_declarations) {
|
|
|
|
|
VERIFY(entry->target().has<NonnullRefPtr<Identifier const>>());
|
|
|
|
|
TRY(callback(entry->target().get<NonnullRefPtr<Identifier const>>()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:55:44 +01:00
|
|
|
static Utf16String expression_to_string_approximation(Expression const& expression)
|
2020-04-19 01:12:51 +01:00
|
|
|
{
|
2026-02-15 12:55:44 +01:00
|
|
|
if (is<Identifier>(expression))
|
|
|
|
|
return as<Identifier>(expression).string().to_utf16_string();
|
2025-08-06 11:12:58 -04:00
|
|
|
|
2026-02-15 12:55:44 +01:00
|
|
|
if (is<MemberExpression>(expression)) {
|
|
|
|
|
auto const& member = as<MemberExpression>(expression);
|
|
|
|
|
auto object_string = expression_to_string_approximation(member.object());
|
|
|
|
|
if (member.is_computed()) {
|
|
|
|
|
auto property_string = expression_to_string_approximation(member.property());
|
|
|
|
|
return Utf16String::formatted("{}[{}]", object_string, property_string);
|
|
|
|
|
}
|
|
|
|
|
if (is<PrivateIdentifier>(member.property()))
|
|
|
|
|
return Utf16String::formatted("{}.{}", object_string, as<PrivateIdentifier>(member.property()).string());
|
|
|
|
|
return Utf16String::formatted("{}.{}", object_string, as<Identifier>(member.property()).string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is<StringLiteral>(expression))
|
|
|
|
|
return Utf16String::formatted("'{}'", as<StringLiteral>(expression).value());
|
|
|
|
|
|
|
|
|
|
if (is<NumericLiteral>(expression))
|
|
|
|
|
return Utf16String::formatted("{}", as<NumericLiteral>(expression).value().as_double());
|
|
|
|
|
|
|
|
|
|
if (is<ThisExpression>(expression))
|
|
|
|
|
return "this"_utf16;
|
|
|
|
|
|
|
|
|
|
return "<object>"_utf16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Utf16String MemberExpression::to_string_approximation() const
|
|
|
|
|
{
|
|
|
|
|
return expression_to_string_approximation(*this);
|
2020-04-19 01:12:51 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-12 22:45:52 +02:00
|
|
|
bool MemberExpression::ends_in_private_name() const
|
|
|
|
|
{
|
|
|
|
|
if (is_computed())
|
|
|
|
|
return false;
|
|
|
|
|
if (is<PrivateIdentifier>(*m_property))
|
|
|
|
|
return true;
|
|
|
|
|
if (is<MemberExpression>(*m_property))
|
|
|
|
|
return static_cast<MemberExpression const&>(*m_property).ends_in_private_name();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-04 22:36:56 +02:00
|
|
|
bool ScopeNode::has_non_local_lexical_declarations() const
|
|
|
|
|
{
|
|
|
|
|
bool result = false;
|
|
|
|
|
MUST(for_each_lexically_declared_identifier([&](Identifier const& identifier) {
|
|
|
|
|
if (!identifier.is_local())
|
|
|
|
|
result = true;
|
|
|
|
|
}));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-07 13:31:01 +01:00
|
|
|
ThrowCompletionOr<void> ScopeNode::for_each_lexically_scoped_declaration(ThrowCompletionOrVoidCallback<Declaration const&>&& callback) const
|
2021-09-22 12:44:56 +02:00
|
|
|
{
|
2022-02-07 13:31:01 +01:00
|
|
|
for (auto& declaration : m_lexical_declarations)
|
|
|
|
|
TRY(callback(declaration));
|
|
|
|
|
|
|
|
|
|
return {};
|
2021-09-22 12:44:56 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-06 17:49:38 +02:00
|
|
|
ThrowCompletionOr<void> ScopeNode::for_each_lexically_declared_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const
|
|
|
|
|
{
|
|
|
|
|
for (auto const& declaration : m_lexical_declarations) {
|
|
|
|
|
TRY(declaration->for_each_bound_identifier([&](auto const& identifier) {
|
|
|
|
|
return callback(identifier);
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-05 02:10:40 +02:00
|
|
|
ThrowCompletionOr<void> ScopeNode::for_each_var_declared_identifier(ThrowCompletionOrVoidCallback<Identifier const&>&& callback) const
|
|
|
|
|
{
|
|
|
|
|
for (auto& declaration : m_var_declarations) {
|
|
|
|
|
TRY(declaration->for_each_bound_identifier([&](auto const& id) {
|
|
|
|
|
return callback(id);
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-07 13:31:01 +01:00
|
|
|
ThrowCompletionOr<void> ScopeNode::for_each_var_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback<FunctionDeclaration const&>&& callback) const
|
2021-09-22 12:44:56 +02:00
|
|
|
{
|
|
|
|
|
for (ssize_t i = m_var_declarations.size() - 1; i >= 0; i--) {
|
|
|
|
|
auto& declaration = m_var_declarations[i];
|
2022-02-07 13:31:01 +01:00
|
|
|
if (is<FunctionDeclaration>(declaration))
|
2023-03-06 14:17:01 +01:00
|
|
|
TRY(callback(static_cast<FunctionDeclaration const&>(*declaration)));
|
2021-09-22 12:44:56 +02:00
|
|
|
}
|
2022-02-07 13:31:01 +01:00
|
|
|
return {};
|
2021-09-22 12:44:56 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-07 13:31:01 +01:00
|
|
|
ThrowCompletionOr<void> ScopeNode::for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback<VariableDeclaration const&>&& callback) const
|
2021-09-22 12:44:56 +02:00
|
|
|
{
|
|
|
|
|
for (auto& declaration : m_var_declarations) {
|
|
|
|
|
if (!is<FunctionDeclaration>(declaration)) {
|
|
|
|
|
VERIFY(is<VariableDeclaration>(declaration));
|
2023-03-06 14:17:01 +01:00
|
|
|
TRY(callback(static_cast<VariableDeclaration const&>(*declaration)));
|
2021-09-22 12:44:56 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-02-07 13:31:01 +01:00
|
|
|
return {};
|
2021-09-22 12:44:56 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-07 13:31:01 +01:00
|
|
|
ThrowCompletionOr<void> ScopeNode::for_each_function_hoistable_with_annexB_extension(ThrowCompletionOrVoidCallback<FunctionDeclaration&>&& callback) const
|
2021-09-22 12:44:56 +02:00
|
|
|
{
|
|
|
|
|
for (auto& function : m_functions_hoistable_with_annexB_extension) {
|
|
|
|
|
// We need const_cast here since it might have to set a property on function declaration.
|
2023-03-06 14:17:01 +01:00
|
|
|
TRY(callback(const_cast<FunctionDeclaration&>(*function)));
|
2021-09-22 12:44:56 +02:00
|
|
|
}
|
2022-02-07 13:31:01 +01:00
|
|
|
return {};
|
2021-09-22 12:44:56 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-19 22:07:52 +01:00
|
|
|
void ScopeNode::add_lexical_declaration(NonnullRefPtr<Declaration const> declaration)
|
2020-04-13 16:42:54 +02:00
|
|
|
{
|
2021-09-22 12:44:56 +02:00
|
|
|
m_lexical_declarations.append(move(declaration));
|
2020-04-13 16:42:54 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-19 22:07:52 +01:00
|
|
|
void ScopeNode::add_var_scoped_declaration(NonnullRefPtr<Declaration const> declaration)
|
2020-06-04 14:48:36 +02:00
|
|
|
{
|
2021-09-22 12:44:56 +02:00
|
|
|
m_var_declarations.append(move(declaration));
|
2020-06-04 14:48:36 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-19 22:07:52 +01:00
|
|
|
void ScopeNode::add_hoisted_function(NonnullRefPtr<FunctionDeclaration const> declaration)
|
2021-07-04 03:15:52 +02:00
|
|
|
{
|
2021-09-22 12:44:56 +02:00
|
|
|
m_functions_hoistable_with_annexB_extension.append(move(declaration));
|
2021-07-04 03:15:52 +02:00
|
|
|
}
|
2021-08-14 17:42:30 +02:00
|
|
|
|
2026-01-25 20:18:38 +01:00
|
|
|
void ScopeNode::ensure_function_scope_data() const
|
|
|
|
|
{
|
|
|
|
|
if (m_function_scope_data)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto data = make<FunctionScopeData>();
|
|
|
|
|
|
|
|
|
|
// Extract functions_to_initialize from var-scoped function declarations (in reverse order, deduplicated).
|
|
|
|
|
HashTable<Utf16FlyString> seen_function_names;
|
|
|
|
|
for (ssize_t i = m_var_declarations.size() - 1; i >= 0; i--) {
|
|
|
|
|
auto const& declaration = m_var_declarations[i];
|
|
|
|
|
if (is<FunctionDeclaration>(declaration)) {
|
|
|
|
|
auto& function_decl = static_cast<FunctionDeclaration const&>(*declaration);
|
|
|
|
|
if (seen_function_names.set(function_decl.name()) == AK::HashSetResult::InsertedNewEntry)
|
|
|
|
|
data->functions_to_initialize.append(static_ptr_cast<FunctionDeclaration const>(declaration));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data->has_function_named_arguments = seen_function_names.contains("arguments"_utf16_fly_string);
|
|
|
|
|
|
|
|
|
|
// Check if "arguments" is lexically declared.
|
|
|
|
|
MUST(for_each_lexically_declared_identifier([&](auto const& identifier) {
|
|
|
|
|
if (identifier.string() == "arguments"_utf16_fly_string)
|
|
|
|
|
data->has_lexically_declared_arguments = true;
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Extract vars_to_initialize from var declarations.
|
|
|
|
|
HashTable<Utf16FlyString> seen_var_names;
|
|
|
|
|
MUST(for_each_var_declared_identifier([&](Identifier const& identifier) {
|
|
|
|
|
auto const& name = identifier.string();
|
|
|
|
|
if (seen_var_names.set(name) == AK::HashSetResult::InsertedNewEntry) {
|
|
|
|
|
data->vars_to_initialize.append({
|
|
|
|
|
.identifier = identifier,
|
|
|
|
|
.is_parameter = false,
|
|
|
|
|
.is_function_name = seen_function_names.contains(name),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
data->var_names.set(name);
|
|
|
|
|
|
|
|
|
|
if (!identifier.is_local()) {
|
|
|
|
|
data->non_local_var_count++;
|
|
|
|
|
data->non_local_var_count_for_parameter_expressions++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
m_function_scope_data = move(data);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 11:12:58 -04:00
|
|
|
Utf16FlyString ExportStatement::local_name_for_default = "*default*"_utf16_fly_string;
|
2022-01-16 23:51:28 +01:00
|
|
|
|
2025-08-06 11:12:58 -04:00
|
|
|
bool ExportStatement::has_export(Utf16FlyString const& export_name) const
|
2021-08-14 17:42:30 +02:00
|
|
|
{
|
2026-01-06 14:43:55 +01:00
|
|
|
return m_entries.contains([&](auto& entry) {
|
2022-08-29 22:12:25 +02:00
|
|
|
// Make sure that empty exported names does not overlap with anything
|
2022-09-01 22:55:02 +02:00
|
|
|
if (entry.kind != ExportEntry::Kind::NamedExport)
|
2022-08-29 22:12:25 +02:00
|
|
|
return false;
|
2021-08-14 17:42:30 +02:00
|
|
|
return entry.export_name == export_name;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 11:12:58 -04:00
|
|
|
bool ImportStatement::has_bound_name(Utf16FlyString const& name) const
|
2021-08-14 17:42:30 +02:00
|
|
|
{
|
2026-01-06 14:43:55 +01:00
|
|
|
return m_entries.contains([&](auto& entry) { return entry.local_name == name; });
|
2021-08-14 17:42:30 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 11:12:58 -04:00
|
|
|
ModuleRequest::ModuleRequest(Utf16FlyString module_specifier_, Vector<ImportAttribute> attributes)
|
2022-01-27 02:44:03 +01:00
|
|
|
: module_specifier(move(module_specifier_))
|
2023-12-02 16:20:01 +01:00
|
|
|
, attributes(move(attributes))
|
2022-01-27 02:44:03 +01:00
|
|
|
{
|
2025-04-28 15:03:56 -04:00
|
|
|
// 13.3.10.2 EvaluateImportCall ( specifierExpression [ , optionsExpression ] ), https://tc39.es/ecma262/#sec-evaluate-import-call
|
|
|
|
|
// 16.2.2.4 Static Semantics: WithClauseToAttributes, https://tc39.es/ecma262/#sec-withclausetoattributes
|
|
|
|
|
// 2. Sort attributes according to the lexicographic order of their [[Key]] field, treating the value of each such
|
|
|
|
|
// field as a sequence of UTF-16 code unit values.
|
2023-12-02 16:20:01 +01:00
|
|
|
quick_sort(this->attributes, [](ImportAttribute const& lhs, ImportAttribute const& rhs) {
|
2022-01-27 02:44:03 +01:00
|
|
|
return lhs.key < rhs.key;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-16 17:49:34 +03:30
|
|
|
ByteString SourceRange::filename() const
|
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
|
|
|
{
|
2023-12-16 17:49:34 +03:30
|
|
|
return code->filename().to_byte_string();
|
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
|
|
|
}
|
|
|
|
|
|
2023-06-15 15:13:48 +05:30
|
|
|
NonnullRefPtr<CallExpression> CallExpression::create(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens)
|
2022-11-26 19:51:50 +01:00
|
|
|
{
|
2023-06-15 15:13:48 +05:30
|
|
|
return ASTNodeWithTailArray::create<CallExpression>(arguments.size(), move(source_range), move(callee), arguments, invocation_style, inside_parens);
|
2022-11-26 19:51:50 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-15 15:13:48 +05:30
|
|
|
NonnullRefPtr<NewExpression> NewExpression::create(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens)
|
2022-11-26 19:51:50 +01:00
|
|
|
{
|
2023-06-15 15:13:48 +05:30
|
|
|
return ASTNodeWithTailArray::create<NewExpression>(arguments.size(), move(source_range), move(callee), arguments, invocation_style, inside_parens);
|
2022-11-26 19:51:50 +01:00
|
|
|
}
|
|
|
|
|
|
2025-03-27 12:59:50 +00:00
|
|
|
NonnullRefPtr<FunctionParameters> FunctionParameters::empty()
|
|
|
|
|
{
|
|
|
|
|
static auto empty = adopt_ref(*new FunctionParameters({}));
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-07 19:42:11 +01:00
|
|
|
}
|