LibJS: Remove C++ AST

Delete AST.cpp, AST.h, ASTDump.cpp, ScopeRecord.h, and the dead
get_builtin(MemberExpression const&) from Builtins.cpp.

Extract ImportEntry and ExportEntry into a new ModuleEntry.h,
since they are data types used by the module system, not AST
node types.

Inline ModuleRequest's sorting constructor and
SourceRange::filename().

Remove the dead annex_b_function_declarations field from
EvalDeclarationData, which was only populated by the C++ parser.
This commit is contained in:
Andreas Kling 2026-03-19 13:37:46 -05:00 committed by Andreas Kling
parent 169452f41b
commit 8ec7e7c07c
15 changed files with 112 additions and 3917 deletions

View file

@ -1,382 +0,0 @@
/*
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021-2022, David Tuin <davidot@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Demangle.h>
#include <AK/HashTable.h>
#include <AK/QuickSort.h>
#include <AK/TemporaryChange.h>
#include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/AST.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalEnvironment.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/SharedFunctionInstanceData.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <typeinfo>
namespace JS {
ASTNode::ASTNode(SourceRange source_range)
: m_source_range(move(source_range))
{
}
ByteString ASTNode::class_name() const
{
// NOTE: We strip the "JS::" prefix.
auto const* typename_ptr = typeid(*this).name();
return demangle({ typename_ptr, strlen(typename_ptr) }).substring(4);
}
Optional<Utf16String> CallExpression::expression_string() const
{
if (is<Identifier>(*m_callee))
return static_cast<Identifier const&>(*m_callee).string().to_utf16_string();
if (is<MemberExpression>(*m_callee))
return static_cast<MemberExpression const&>(*m_callee).to_string_approximation();
return {};
}
static Optional<Utf16FlyString> nullopt_or_private_identifier_description(Expression const& expression)
{
if (is<PrivateIdentifier>(expression))
return static_cast<PrivateIdentifier const&>(expression).string();
return {};
}
Optional<Utf16FlyString> ClassField::private_bound_identifier() const
{
return nullopt_or_private_identifier_description(*m_key);
}
Optional<Utf16FlyString> ClassMethod::private_bound_identifier() const
{
return nullopt_or_private_identifier_description(*m_key);
}
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);
}
bool BindingPattern::contains_expression() const
{
for (auto& entry : entries) {
if (entry.name.has<NonnullRefPtr<Expression const>>())
return true;
if (entry.initializer)
return true;
if (auto binding_ptr = entry.alias.get_pointer<NonnullRefPtr<BindingPattern const>>(); binding_ptr && (*binding_ptr)->contains_expression())
return true;
}
return false;
}
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 {};
}
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)
: 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;
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 {};
}
static Utf16String expression_to_string_approximation(Expression const& expression)
{
if (is<Identifier>(expression))
return as<Identifier>(expression).string().to_utf16_string();
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);
}
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;
}
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;
}
ThrowCompletionOr<void> ScopeNode::for_each_lexically_scoped_declaration(ThrowCompletionOrVoidCallback<Declaration const&>&& callback) const
{
for (auto& declaration : m_lexical_declarations)
TRY(callback(declaration));
return {};
}
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 {};
}
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 {};
}
ThrowCompletionOr<void> ScopeNode::for_each_var_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback<FunctionDeclaration const&>&& callback) const
{
for (ssize_t i = m_var_declarations.size() - 1; i >= 0; i--) {
auto& declaration = m_var_declarations[i];
if (is<FunctionDeclaration>(declaration))
TRY(callback(static_cast<FunctionDeclaration const&>(*declaration)));
}
return {};
}
ThrowCompletionOr<void> ScopeNode::for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback<VariableDeclaration const&>&& callback) const
{
for (auto& declaration : m_var_declarations) {
if (!is<FunctionDeclaration>(declaration)) {
VERIFY(is<VariableDeclaration>(declaration));
TRY(callback(static_cast<VariableDeclaration const&>(*declaration)));
}
}
return {};
}
ThrowCompletionOr<void> ScopeNode::for_each_function_hoistable_with_annexB_extension(ThrowCompletionOrVoidCallback<FunctionDeclaration&>&& callback) const
{
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.
TRY(callback(const_cast<FunctionDeclaration&>(*function)));
}
return {};
}
void ScopeNode::add_lexical_declaration(NonnullRefPtr<Declaration const> declaration)
{
m_lexical_declarations.append(move(declaration));
}
void ScopeNode::add_var_scoped_declaration(NonnullRefPtr<Declaration const> declaration)
{
m_var_declarations.append(move(declaration));
}
void ScopeNode::add_hoisted_function(NonnullRefPtr<FunctionDeclaration const> declaration)
{
m_functions_hoistable_with_annexB_extension.append(move(declaration));
}
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);
}
Utf16FlyString ExportStatement::local_name_for_default = "*default*"_utf16_fly_string;
bool ExportStatement::has_export(Utf16FlyString const& export_name) const
{
return m_entries.contains([&](auto& entry) {
// Make sure that empty exported names does not overlap with anything
if (entry.kind != ExportEntry::Kind::NamedExport)
return false;
return entry.export_name == export_name;
});
}
bool ImportStatement::has_bound_name(Utf16FlyString const& name) const
{
return m_entries.contains([&](auto& entry) { return entry.local_name == name; });
}
ModuleRequest::ModuleRequest(Utf16FlyString module_specifier_, Vector<ImportAttribute> attributes)
: module_specifier(move(module_specifier_))
, attributes(move(attributes))
{
// 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.
quick_sort(this->attributes, [](ImportAttribute const& lhs, ImportAttribute const& rhs) {
return lhs.key < rhs.key;
});
}
ByteString SourceRange::filename() const
{
return code->filename().to_byte_string();
}
NonnullRefPtr<CallExpression> CallExpression::create(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens)
{
return ASTNodeWithTailArray::create<CallExpression>(arguments.size(), move(source_range), move(callee), arguments, invocation_style, inside_parens);
}
NonnullRefPtr<NewExpression> NewExpression::create(SourceRange source_range, NonnullRefPtr<Expression const> callee, ReadonlySpan<Argument> arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens)
{
return ASTNodeWithTailArray::create<NewExpression>(arguments.size(), move(source_range), move(callee), arguments, invocation_style, inside_parens);
}
NonnullRefPtr<FunctionParameters> FunctionParameters::empty()
{
static auto empty = adopt_ref(*new FunctionParameters({}));
return empty;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,26 +0,0 @@
/*
* Copyright (c) 2023, Simon Wanner <simon@skyrising.xyz>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/AST.h>
#include <LibJS/Bytecode/Builtins.h>
namespace JS::Bytecode {
Optional<Builtin> get_builtin(MemberExpression const& expression)
{
if (expression.is_computed() || !expression.object().is_identifier() || !expression.property().is_identifier())
return {};
auto base_name = static_cast<Identifier const&>(expression.object()).string();
auto property_name = static_cast<Identifier const&>(expression.property()).string();
#define CHECK_MEMBER_BUILTIN(name, snake_case_name, base, property, ...) \
if (base_name == #base##sv && property_name == #property##sv) \
return Builtin::name;
JS_ENUMERATE_BUILTINS(CHECK_MEMBER_BUILTIN)
#undef CHECK_MEMBER_BUILTIN
return {};
}
}

View file

@ -9,7 +9,6 @@
#include <AK/HashTable.h>
#include <AK/TemporaryChange.h>
#include <LibGC/RootHashMap.h>
#include <LibJS/AST.h>
#include <LibJS/Bytecode/AsmInterpreter/AsmInterpreter.h>
#include <LibJS/Bytecode/BasicBlock.h>
#include <LibJS/Bytecode/FormatOperand.h>

View file

@ -1,11 +1,8 @@
include(libjs_generators)
set(SOURCES
AST.cpp
ASTDump.cpp
Bytecode/AsmInterpreter/AsmInterpreter.cpp
Bytecode/BasicBlock.cpp
Bytecode/Builtins.cpp
Bytecode/Executable.cpp
Bytecode/IdentifierTable.cpp
Bytecode/Instruction.cpp

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <AK/Utf16FlyString.h>
#include <LibJS/Runtime/ModuleRequest.h>
namespace JS {
struct ImportEntry {
Optional<Utf16FlyString> import_name; // [[ImportName]]: stored string if Optional is not empty, NAMESPACE-OBJECT otherwise
Utf16FlyString local_name; // [[LocalName]]
Optional<ModuleRequest> m_module_request; // [[ModuleRequest]]
ImportEntry(Optional<Utf16FlyString> import_name_, Utf16FlyString local_name_)
: import_name(move(import_name_))
, local_name(move(local_name_))
{
}
bool is_namespace() const { return !import_name.has_value(); }
ModuleRequest const& module_request() const
{
return m_module_request.value();
}
};
// ExportEntry Record, https://tc39.es/ecma262/#table-exportentry-records
struct ExportEntry {
enum class Kind {
NamedExport,
ModuleRequestAll,
ModuleRequestAllButDefault,
// EmptyNamedExport is a special type for export {} from "module",
// which should import the module without getting any of the exports
// however we don't want give it a fake export name which may get
// duplicates
EmptyNamedExport,
} kind;
Optional<Utf16FlyString> export_name; // [[ExportName]]
Optional<Utf16FlyString> local_or_import_name; // Either [[ImportName]] or [[LocalName]]
ExportEntry(Kind export_kind, Optional<Utf16FlyString> export_name_, Optional<Utf16FlyString> local_or_import_name_)
: kind(export_kind)
, export_name(move(export_name_))
, local_or_import_name(move(local_or_import_name_))
{
}
Optional<ModuleRequest> m_module_request; // [[ModuleRequest]]
bool is_module_request() const
{
return m_module_request.has_value();
}
static ExportEntry indirect_export_entry(ModuleRequest module_request, Optional<Utf16FlyString> export_name, Optional<Utf16FlyString> import_name)
{
ExportEntry entry { Kind::NamedExport, move(export_name), move(import_name) };
entry.m_module_request = move(module_request);
return entry;
}
ModuleRequest const& module_request() const
{
return m_module_request.value();
}
static ExportEntry named_export(Utf16FlyString export_name, Utf16FlyString local_name)
{
return ExportEntry { Kind::NamedExport, move(export_name), move(local_name) };
}
static ExportEntry all_but_default_entry()
{
return ExportEntry { Kind::ModuleRequestAllButDefault, {}, {} };
}
static ExportEntry all_module_request(Utf16FlyString export_name)
{
return ExportEntry { Kind::ModuleRequestAll, move(export_name), {} };
}
static ExportEntry empty_named_export()
{
return ExportEntry { Kind::EmptyNamedExport, {}, {} };
}
};
}

View file

@ -952,8 +952,6 @@ ThrowCompletionOr<void> eval_declaration_instantiation(VM& vm, EvalDeclarationDa
// iii. Let fobj be ! benv.GetBindingValue(F, false).
// iv. Perform ? genv.SetMutableBinding(F, fobj, false).
// v. Return unused.
if (i < data.annex_b_function_declarations.size())
data.annex_b_function_declarations[i]->set_should_do_additional_annexB_steps();
}
}

View file

@ -28,8 +28,6 @@
namespace JS {
class FunctionDeclaration;
GC::Ref<DeclarativeEnvironment> new_declarative_environment(Environment&);
JS_API GC::Ref<ObjectEnvironment> new_object_environment(Object&, bool is_with_environment, Environment*);
GC::Ref<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject&, Object* new_target);
@ -106,7 +104,6 @@ struct EvalDeclarationData {
Vector<Utf16FlyString> var_scoped_names;
Vector<Utf16FlyString> annex_b_candidate_names;
Vector<NonnullRefPtr<FunctionDeclaration>> annex_b_function_declarations;
struct LexicalBinding {
Utf16FlyString name;

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/QuickSort.h>
#include <AK/StdLibExtras.h>
#include <AK/Utf16FlyString.h>
#include <AK/Vector.h>
@ -38,7 +39,17 @@ struct ModuleRequest {
{
}
ModuleRequest(Utf16FlyString specifier, Vector<ImportAttribute> attributes);
ModuleRequest(Utf16FlyString specifier, Vector<ImportAttribute> attrs)
: module_specifier(move(specifier))
, attributes(move(attrs))
{
// 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.
quick_sort(this->attributes, [](ImportAttribute const& lhs, ImportAttribute const& rhs) {
return lhs.key < rhs.key;
});
}
void add_attribute(Utf16String key, Utf16String value)
{

View file

@ -13,7 +13,7 @@
#include <AK/Utf16FlyString.h>
#include <LibGC/Ptr.h>
#include <LibGC/Root.h>
#include <LibJS/AST.h>
#include <LibJS/ModuleEntry.h>
#include <LibJS/ParserError.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/FunctionKind.h>

View file

@ -1,116 +0,0 @@
/*
* Copyright (c) 2026, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashMap.h>
#include <AK/HashTable.h>
#include <AK/NonnullRefPtr.h>
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <AK/Utf16FlyString.h>
#include <AK/Vector.h>
#include <LibJS/AST.h>
namespace JS {
struct ScopeVariable {
enum Flag : u16 {
None = 0,
IsVar = 1 << 0,
IsLexical = 1 << 1,
IsFunction = 1 << 2,
IsCatchParameter = 1 << 3,
IsForbiddenLexical = 1 << 4,
IsForbiddenVar = 1 << 5,
IsBound = 1 << 6,
IsParameterCandidate = 1 << 7,
IsReferencedInFormalParameters = 1 << 8,
};
u16 flags { 0 };
Identifier const* var_identifier { nullptr };
RefPtr<FunctionDeclaration const> function_declaration;
bool has_flag(u16 flag) const { return flags & flag; }
};
struct IdentifierGroup {
bool captured_by_nested_function { false };
bool used_inside_with_statement { false };
Vector<NonnullRefPtr<Identifier>> identifiers;
Optional<DeclarationKind> declaration_kind;
};
struct ScopeRecord {
enum class ScopeType {
Function,
Program,
Block,
ForLoop,
With,
Catch,
ClassStaticInit,
ClassField,
ClassDeclaration,
};
// NOTE: We really only need ModuleTopLevel and NotModuleTopLevel as the only
// difference seems to be in https://tc39.es/ecma262/#sec-static-semantics-varscopeddeclarations
// where ModuleItemList only does the VarScopedDeclaration and not the
// TopLevelVarScopedDeclarations.
enum class ScopeLevel {
NotTopLevel,
ScriptTopLevel,
ModuleTopLevel,
FunctionTopLevel,
StaticInitTopLevel,
};
ScopeType type;
ScopeLevel level;
RefPtr<ScopeNode> ast_node;
HashMap<Utf16FlyString, ScopeVariable> variables;
HashMap<Utf16FlyString, IdentifierGroup> identifier_groups;
Vector<NonnullRefPtr<FunctionDeclaration const>> functions_to_hoist;
RefPtr<FunctionParameters const> function_parameters;
bool contains_access_to_arguments_object_in_non_strict_mode { false };
bool contains_direct_call_to_eval { false };
bool contains_await_expression { false };
bool screwed_by_eval_in_scope_chain { false };
bool eval_in_current_function { false };
bool uses_this_from_environment { false };
bool uses_this { false };
bool is_arrow_function { false };
bool is_function_declaration { false };
bool has_parameter_expressions { false };
ScopeRecord* parent { nullptr };
ScopeRecord* top_level { nullptr };
Vector<NonnullOwnPtr<ScopeRecord>> children;
bool is_top_level() const { return level != ScopeLevel::NotTopLevel; }
bool has_variable_with_flags(Utf16FlyString const& name, u16 flags) const
{
auto it = variables.find(name);
return it != variables.end() && (it->value.flags & flags);
}
ScopeRecord const* last_function_scope() const
{
for (auto const* scope = this; scope; scope = scope->parent) {
if (scope->type == ScopeType::Function || scope->type == ScopeType::ClassStaticInit)
return scope;
}
return nullptr;
}
};
}

View file

@ -24,7 +24,7 @@ struct JS_API SourceRange {
Position start;
Position end;
ByteString filename() const;
ByteString filename() const { return code->filename().to_byte_string(); }
};
struct UnrealizedSourceRange {

View file

@ -10,6 +10,7 @@
#include <LibJS/CyclicModule.h>
#include <LibJS/Export.h>
#include <LibJS/Forward.h>
#include <LibJS/ModuleEntry.h>
#include <LibJS/Runtime/ExecutionContext.h>
namespace JS {

View file

@ -9,7 +9,6 @@
*/
#include <LibGC/DeferGC.h>
#include <LibJS/AST.h>
#include <LibJS/Module.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Environment.h>