Everywhere: Hoist the Libraries folder to the top-level

This commit is contained in:
Timothy Flynn 2024-11-09 12:25:08 -05:00 committed by Andreas Kling
commit 93712b24bf
Notes: github-actions[bot] 2024-11-10 11:51:52 +00:00
4547 changed files with 104 additions and 113 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/String.h>
#include <LibJS/Bytecode/BasicBlock.h>
#include <LibJS/Bytecode/Op.h>
namespace JS::Bytecode {
NonnullOwnPtr<BasicBlock> BasicBlock::create(u32 index, String name)
{
return adopt_own(*new BasicBlock(index, move(name)));
}
BasicBlock::BasicBlock(u32 index, String name)
: m_index(index)
, m_name(move(name))
{
}
BasicBlock::~BasicBlock()
{
Bytecode::InstructionStreamIterator it(instruction_stream());
while (!it.at_end()) {
auto& to_destroy = (*it);
++it;
Instruction::destroy(const_cast<Instruction&>(to_destroy));
}
}
void BasicBlock::grow(size_t additional_size)
{
m_buffer.grow_capacity(m_buffer.size() + additional_size);
m_buffer.resize(m_buffer.size() + additional_size);
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Badge.h>
#include <AK/String.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/ScopedOperand.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Handle.h>
namespace JS::Bytecode {
struct UnwindInfo {
JS::GCPtr<Executable const> executable;
JS::GCPtr<Environment> lexical_environment;
bool handler_called { false };
};
class BasicBlock {
AK_MAKE_NONCOPYABLE(BasicBlock);
public:
static NonnullOwnPtr<BasicBlock> create(u32 index, String name);
~BasicBlock();
u32 index() const { return m_index; }
ReadonlyBytes instruction_stream() const { return m_buffer.span(); }
u8* data() { return m_buffer.data(); }
u8 const* data() const { return m_buffer.data(); }
size_t size() const { return m_buffer.size(); }
void rewind()
{
m_buffer.resize_and_keep_capacity(m_last_instruction_start_offset);
m_terminated = false;
}
void grow(size_t additional_size);
void terminate(Badge<Generator>) { m_terminated = true; }
bool is_terminated() const { return m_terminated; }
String const& name() const { return m_name; }
void set_handler(BasicBlock const& handler) { m_handler = &handler; }
void set_finalizer(BasicBlock const& finalizer) { m_finalizer = &finalizer; }
BasicBlock const* handler() const { return m_handler; }
BasicBlock const* finalizer() const { return m_finalizer; }
auto const& source_map() const { return m_source_map; }
void add_source_map_entry(size_t bytecode_offset, SourceRecord const& source_record) { m_source_map.set(bytecode_offset, source_record); }
[[nodiscard]] bool has_resolved_this() const { return m_has_resolved_this; }
void set_has_resolved_this() { m_has_resolved_this = true; }
[[nodiscard]] size_t last_instruction_start_offset() const { return m_last_instruction_start_offset; }
void set_last_instruction_start_offset(size_t offset) { m_last_instruction_start_offset = offset; }
private:
explicit BasicBlock(u32 index, String name);
u32 m_index { 0 };
Vector<u8> m_buffer;
BasicBlock const* m_handler { nullptr };
BasicBlock const* m_finalizer { nullptr };
String m_name;
bool m_terminated { false };
bool m_has_resolved_this { false };
HashMap<size_t, SourceRecord> m_source_map;
size_t m_last_instruction_start_offset { 0 };
};
}

View file

@ -0,0 +1,26 @@
/*
* 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

@ -0,0 +1,74 @@
/*
* Copyright (c) 2023, Simon Wanner <simon@skyrising.xyz>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Format.h>
#include <LibJS/Forward.h>
namespace JS::Bytecode {
// TitleCaseName, snake_case_name, base, property, argument_count
#define JS_ENUMERATE_BUILTINS(O) \
O(MathAbs, math_abs, Math, abs, 1) \
O(MathLog, math_log, Math, log, 1) \
O(MathPow, math_pow, Math, pow, 2) \
O(MathExp, math_exp, Math, exp, 1) \
O(MathCeil, math_ceil, Math, ceil, 1) \
O(MathFloor, math_floor, Math, floor, 1) \
O(MathRound, math_round, Math, round, 1) \
O(MathSqrt, math_sqrt, Math, sqrt, 1)
enum class Builtin : u8 {
#define DEFINE_BUILTIN_ENUM(name, ...) name,
JS_ENUMERATE_BUILTINS(DEFINE_BUILTIN_ENUM)
#undef DEFINE_BUILTIN_ENUM
__Count,
};
static StringView builtin_name(Builtin value)
{
switch (value) {
#define DEFINE_BUILTIN_CASE(name, snake_case_name, base, property, ...) \
case Builtin::name: \
return #base "." #property##sv;
JS_ENUMERATE_BUILTINS(DEFINE_BUILTIN_CASE)
#undef DEFINE_BUILTIN_CASE
case Builtin::__Count:
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();
}
inline size_t builtin_argument_count(Builtin value)
{
switch (value) {
#define DEFINE_BUILTIN_CASE(name, snake_case_name, base, property, arg_count, ...) \
case Builtin::name: \
return arg_count;
JS_ENUMERATE_BUILTINS(DEFINE_BUILTIN_CASE)
#undef DEFINE_BUILTIN_CASE
case Builtin::__Count:
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();
}
Optional<Builtin> get_builtin(MemberExpression const& expression);
}
namespace AK {
template<>
struct Formatter<JS::Bytecode::Builtin> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, JS::Bytecode::Builtin value)
{
return Formatter<StringView>::format(builder, builtin_name(value));
}
};
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/AST.h>
#include <LibJS/Bytecode/CodeGenerationError.h>
namespace JS::Bytecode {
ErrorOr<String> CodeGenerationError::to_string() const
{
return String::formatted("CodeGenerationError in {}: {}", failing_node ? failing_node->class_name() : "<unknown node>", reason_literal);
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Error.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibJS/Forward.h>
namespace JS::Bytecode {
struct CodeGenerationError {
ASTNode const* failing_node { nullptr };
StringView reason_literal;
ErrorOr<String> to_string() const;
};
template<typename T>
using CodeGenerationErrorOr = ErrorOr<T, CodeGenerationError>;
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Bytecode/BasicBlock.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/Instruction.h>
#include <LibJS/Bytecode/RegexTable.h>
#include <LibJS/SourceCode.h>
namespace JS::Bytecode {
JS_DEFINE_ALLOCATOR(Executable);
Executable::Executable(
Vector<u8> bytecode,
NonnullOwnPtr<IdentifierTable> identifier_table,
NonnullOwnPtr<StringTable> string_table,
NonnullOwnPtr<RegexTable> regex_table,
Vector<Value> constants,
NonnullRefPtr<SourceCode const> source_code,
size_t number_of_property_lookup_caches,
size_t number_of_global_variable_caches,
size_t number_of_registers,
bool is_strict_mode)
: bytecode(move(bytecode))
, string_table(move(string_table))
, identifier_table(move(identifier_table))
, regex_table(move(regex_table))
, constants(move(constants))
, source_code(move(source_code))
, number_of_registers(number_of_registers)
, is_strict_mode(is_strict_mode)
{
property_lookup_caches.resize(number_of_property_lookup_caches);
global_variable_caches.resize(number_of_global_variable_caches);
}
Executable::~Executable() = default;
void Executable::dump() const
{
warnln("\033[37;1mJS bytecode executable\033[0m \"{}\"", name);
InstructionStreamIterator it(bytecode, this);
size_t basic_block_offset_index = 0;
while (!it.at_end()) {
bool print_basic_block_marker = false;
if (basic_block_offset_index < basic_block_start_offsets.size()
&& it.offset() == basic_block_start_offsets[basic_block_offset_index]) {
++basic_block_offset_index;
print_basic_block_marker = true;
}
StringBuilder builder;
builder.appendff("[{:4x}] ", it.offset());
if (print_basic_block_marker)
builder.appendff("{:4}: ", basic_block_offset_index - 1);
else
builder.append(" "sv);
builder.append((*it).to_byte_string(*this));
warnln("{}", builder.string_view());
++it;
}
if (!exception_handlers.is_empty()) {
warnln("");
warnln("Exception handlers:");
for (auto& handlers : exception_handlers) {
warnln(" from {:4x} to {:4x} handler {:4x} finalizer {:4x}",
handlers.start_offset,
handlers.end_offset,
handlers.handler_offset.value_or(0),
handlers.finalizer_offset.value_or(0));
}
}
warnln("");
}
void Executable::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(constants);
}
Optional<Executable::ExceptionHandlers const&> Executable::exception_handlers_for_offset(size_t offset) const
{
for (auto& handlers : exception_handlers) {
if (handlers.start_offset <= offset && offset < handlers.end_offset)
return handlers;
}
return {};
}
UnrealizedSourceRange Executable::source_range_at(size_t offset) const
{
if (offset >= bytecode.size())
return {};
auto it = InstructionStreamIterator(bytecode.span().slice(offset), this);
VERIFY(!it.at_end());
auto mapping = source_map.get(offset);
if (!mapping.has_value())
return {};
return UnrealizedSourceRange {
.source_code = source_code,
.start_offset = mapping->source_start_offset,
.end_offset = mapping->source_end_offset,
};
}
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DeprecatedFlyString.h>
#include <AK/HashMap.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <AK/WeakPtr.h>
#include <LibJS/Bytecode/IdentifierTable.h>
#include <LibJS/Bytecode/Label.h>
#include <LibJS/Bytecode/StringTable.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/Heap/CellAllocator.h>
#include <LibJS/Runtime/EnvironmentCoordinate.h>
#include <LibJS/SourceRange.h>
namespace JS::Bytecode {
struct PropertyLookupCache {
WeakPtr<Shape> shape;
Optional<u32> property_offset;
WeakPtr<Object> prototype;
WeakPtr<PrototypeChainValidity> prototype_chain_validity;
};
struct GlobalVariableCache : public PropertyLookupCache {
u64 environment_serial_number { 0 };
Optional<u32> environment_binding_index;
};
struct SourceRecord {
u32 source_start_offset {};
u32 source_end_offset {};
};
class Executable final : public Cell {
JS_CELL(Executable, Cell);
JS_DECLARE_ALLOCATOR(Executable);
public:
Executable(
Vector<u8> bytecode,
NonnullOwnPtr<IdentifierTable>,
NonnullOwnPtr<StringTable>,
NonnullOwnPtr<RegexTable>,
Vector<Value> constants,
NonnullRefPtr<SourceCode const>,
size_t number_of_property_lookup_caches,
size_t number_of_global_variable_caches,
size_t number_of_registers,
bool is_strict_mode);
virtual ~Executable() override;
DeprecatedFlyString name;
Vector<u8> bytecode;
Vector<PropertyLookupCache> property_lookup_caches;
Vector<GlobalVariableCache> global_variable_caches;
NonnullOwnPtr<StringTable> string_table;
NonnullOwnPtr<IdentifierTable> identifier_table;
NonnullOwnPtr<RegexTable> regex_table;
Vector<Value> constants;
NonnullRefPtr<SourceCode const> source_code;
size_t number_of_registers { 0 };
bool is_strict_mode { false };
struct ExceptionHandlers {
size_t start_offset;
size_t end_offset;
Optional<size_t> handler_offset;
Optional<size_t> finalizer_offset;
};
Vector<ExceptionHandlers> exception_handlers;
Vector<size_t> basic_block_start_offsets;
HashMap<size_t, SourceRecord> source_map;
Vector<DeprecatedFlyString> local_variable_names;
size_t local_index_base { 0 };
Optional<IdentifierTableIndex> length_identifier;
ByteString const& get_string(StringTableIndex index) const { return string_table->get(index); }
DeprecatedFlyString const& get_identifier(IdentifierTableIndex index) const { return identifier_table->get(index); }
Optional<DeprecatedFlyString const&> get_identifier(Optional<IdentifierTableIndex> const& index) const
{
if (!index.has_value())
return {};
return get_identifier(*index);
}
[[nodiscard]] Optional<ExceptionHandlers const&> exception_handlers_for_offset(size_t offset) const;
[[nodiscard]] UnrealizedSourceRange source_range_at(size_t offset) const;
void dump() const;
private:
virtual void visit_edges(Visitor&) override;
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,409 @@
/*
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/SinglyLinkedList.h>
#include <LibJS/AST.h>
#include <LibJS/Bytecode/BasicBlock.h>
#include <LibJS/Bytecode/CodeGenerationError.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/IdentifierTable.h>
#include <LibJS/Bytecode/Label.h>
#include <LibJS/Bytecode/Op.h>
#include <LibJS/Bytecode/Register.h>
#include <LibJS/Bytecode/StringTable.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/FunctionKind.h>
#include <LibRegex/Regex.h>
namespace JS::Bytecode {
class Generator {
public:
VM& vm() { return m_vm; }
enum class SurroundingScopeKind {
Global,
Function,
Block,
};
enum class MustPropagateCompletion {
No,
Yes,
};
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> generate_from_ast_node(VM&, ASTNode const&, FunctionKind = FunctionKind::Normal);
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> generate_from_function(VM&, ECMAScriptFunctionObject const& function);
CodeGenerationErrorOr<void> emit_function_declaration_instantiation(ECMAScriptFunctionObject const& function);
[[nodiscard]] ScopedOperand allocate_register();
[[nodiscard]] ScopedOperand local(u32 local_index);
[[nodiscard]] ScopedOperand accumulator();
[[nodiscard]] ScopedOperand this_value();
void free_register(Register);
void set_local_initialized(u32 local_index);
[[nodiscard]] bool is_local_initialized(u32 local_index) const;
class SourceLocationScope {
public:
SourceLocationScope(Generator&, ASTNode const& node);
~SourceLocationScope();
private:
Generator& m_generator;
ASTNode const* m_previous_node { nullptr };
};
class UnwindContext {
public:
UnwindContext(Generator&, Optional<Label> finalizer);
UnwindContext const* previous() const { return m_previous_context; }
void set_handler(Label handler) { m_handler = handler; }
Optional<Label> handler() const { return m_handler; }
Optional<Label> finalizer() const { return m_finalizer; }
~UnwindContext();
private:
Generator& m_generator;
Optional<Label> m_finalizer;
Optional<Label> m_handler {};
UnwindContext const* m_previous_context { nullptr };
};
template<typename OpType, typename... Args>
requires(requires { OpType(declval<Args>()...); })
void emit(Args&&... args)
{
VERIFY(!is_current_block_terminated());
size_t slot_offset = m_current_basic_block->size();
m_current_basic_block->set_last_instruction_start_offset(slot_offset);
grow(sizeof(OpType));
void* slot = m_current_basic_block->data() + slot_offset;
new (slot) OpType(forward<Args>(args)...);
if constexpr (OpType::IsTerminator)
m_current_basic_block->terminate({});
m_current_basic_block->add_source_map_entry(slot_offset, { m_current_ast_node->start_offset(), m_current_ast_node->end_offset() });
}
template<typename OpType, typename ExtraSlotType, typename... Args>
requires(requires { OpType(declval<Args>()...); })
void emit_with_extra_slots(size_t extra_slot_count, Args&&... args)
{
VERIFY(!is_current_block_terminated());
size_t size_to_allocate = round_up_to_power_of_two(sizeof(OpType) + extra_slot_count * sizeof(ExtraSlotType), alignof(void*));
size_t slot_offset = m_current_basic_block->size();
m_current_basic_block->set_last_instruction_start_offset(slot_offset);
grow(size_to_allocate);
void* slot = m_current_basic_block->data() + slot_offset;
new (slot) OpType(forward<Args>(args)...);
if constexpr (OpType::IsTerminator)
m_current_basic_block->terminate({});
m_current_basic_block->add_source_map_entry(slot_offset, { m_current_ast_node->start_offset(), m_current_ast_node->end_offset() });
}
template<typename OpType, typename... Args>
requires(requires { OpType(declval<Args>()...); })
void emit_with_extra_operand_slots(size_t extra_operand_slots, Args&&... args)
{
emit_with_extra_slots<OpType, Operand>(extra_operand_slots, forward<Args>(args)...);
}
template<typename OpType, typename... Args>
requires(requires { OpType(declval<Args>()...); })
void emit_with_extra_value_slots(size_t extra_operand_slots, Args&&... args)
{
emit_with_extra_slots<OpType, Value>(extra_operand_slots, forward<Args>(args)...);
}
void emit_jump_if(ScopedOperand const& condition, Label true_target, Label false_target);
struct ReferenceOperands {
Optional<ScopedOperand> base {}; // [[Base]]
Optional<ScopedOperand> referenced_name {}; // [[ReferencedName]] as an operand
Optional<IdentifierTableIndex> referenced_identifier {}; // [[ReferencedName]] as an identifier
Optional<IdentifierTableIndex> referenced_private_identifier {}; // [[ReferencedName]] as a private identifier
Optional<ScopedOperand> this_value {}; // [[ThisValue]]
Optional<ScopedOperand> loaded_value {}; // Loaded value, if we've performed a load.
};
CodeGenerationErrorOr<ReferenceOperands> emit_load_from_reference(JS::ASTNode const&, Optional<ScopedOperand> preferred_dst = {});
CodeGenerationErrorOr<void> emit_store_to_reference(JS::ASTNode const&, ScopedOperand value);
CodeGenerationErrorOr<void> emit_store_to_reference(ReferenceOperands const&, ScopedOperand value);
CodeGenerationErrorOr<Optional<ScopedOperand>> emit_delete_reference(JS::ASTNode const&);
CodeGenerationErrorOr<ReferenceOperands> emit_super_reference(MemberExpression const&);
void emit_set_variable(JS::Identifier const& identifier, ScopedOperand value, Bytecode::Op::BindingInitializationMode initialization_mode = Bytecode::Op::BindingInitializationMode::Set, Bytecode::Op::EnvironmentMode mode = Bytecode::Op::EnvironmentMode::Lexical);
void push_home_object(ScopedOperand);
void pop_home_object();
void emit_new_function(ScopedOperand dst, JS::FunctionExpression const&, Optional<IdentifierTableIndex> lhs_name);
CodeGenerationErrorOr<Optional<ScopedOperand>> emit_named_evaluation_if_anonymous_function(Expression const&, Optional<IdentifierTableIndex> lhs_name, Optional<ScopedOperand> preferred_dst = {});
void begin_continuable_scope(Label continue_target, Vector<DeprecatedFlyString> const& language_label_set);
void end_continuable_scope();
void begin_breakable_scope(Label breakable_target, Vector<DeprecatedFlyString> const& language_label_set);
void end_breakable_scope();
[[nodiscard]] Label nearest_continuable_scope() const;
[[nodiscard]] Label nearest_breakable_scope() const;
void switch_to_basic_block(BasicBlock& block)
{
m_current_basic_block = &block;
}
[[nodiscard]] BasicBlock& current_block() { return *m_current_basic_block; }
BasicBlock& make_block(String name = {})
{
if (name.is_empty())
name = String::number(m_next_block++);
auto block = BasicBlock::create(m_root_basic_blocks.size(), name);
if (auto const* context = m_current_unwind_context) {
if (context->handler().has_value())
block->set_handler(*m_root_basic_blocks[context->handler().value().basic_block_index()]);
if (m_current_unwind_context->finalizer().has_value())
block->set_finalizer(*m_root_basic_blocks[context->finalizer().value().basic_block_index()]);
}
m_root_basic_blocks.append(move(block));
return *m_root_basic_blocks.last();
}
bool is_current_block_terminated() const
{
return m_current_basic_block->is_terminated();
}
StringTableIndex intern_string(ByteString string)
{
return m_string_table->insert(move(string));
}
RegexTableIndex intern_regex(ParsedRegex regex)
{
return m_regex_table->insert(move(regex));
}
IdentifierTableIndex intern_identifier(DeprecatedFlyString string)
{
return m_identifier_table->insert(move(string));
}
Optional<IdentifierTableIndex> intern_identifier_for_expression(Expression const& expression);
bool is_in_generator_or_async_function() const { return m_enclosing_function_kind == FunctionKind::Async || m_enclosing_function_kind == FunctionKind::Generator || m_enclosing_function_kind == FunctionKind::AsyncGenerator; }
bool is_in_generator_function() const { return m_enclosing_function_kind == FunctionKind::Generator || m_enclosing_function_kind == FunctionKind::AsyncGenerator; }
bool is_in_async_function() const { return m_enclosing_function_kind == FunctionKind::Async || m_enclosing_function_kind == FunctionKind::AsyncGenerator; }
bool is_in_async_generator_function() const { return m_enclosing_function_kind == FunctionKind::AsyncGenerator; }
enum class BindingMode {
Lexical,
Var,
Global,
};
struct LexicalScope {
SurroundingScopeKind kind;
};
// Returns true if a lexical environment was created.
bool emit_block_declaration_instantiation(ScopeNode const&);
void begin_variable_scope();
void end_variable_scope();
enum class BlockBoundaryType {
Break,
Continue,
Unwind,
ReturnToFinally,
LeaveFinally,
LeaveLexicalEnvironment,
};
template<typename OpType>
void perform_needed_unwinds()
requires(OpType::IsTerminator && !IsSame<OpType, Op::Jump>)
{
for (size_t i = m_boundaries.size(); i > 0; --i) {
auto boundary = m_boundaries[i - 1];
using enum BlockBoundaryType;
switch (boundary) {
case Unwind:
if constexpr (IsSame<OpType, Bytecode::Op::Throw>)
return;
emit<Bytecode::Op::LeaveUnwindContext>();
break;
case LeaveLexicalEnvironment:
emit<Bytecode::Op::LeaveLexicalEnvironment>();
break;
case Break:
case Continue:
break;
case ReturnToFinally:
return;
case LeaveFinally:
emit<Bytecode::Op::LeaveFinally>();
break;
};
}
}
bool is_in_finalizer() const { return m_boundaries.contains_slow(BlockBoundaryType::LeaveFinally); }
bool must_enter_finalizer() const { return m_boundaries.contains_slow(BlockBoundaryType::ReturnToFinally); }
void generate_break();
void generate_break(DeprecatedFlyString const& break_label);
void generate_continue();
void generate_continue(DeprecatedFlyString const& continue_label);
template<typename OpType>
void emit_return(ScopedOperand value)
requires(IsOneOf<OpType, Op::Return, Op::Yield>)
{
// FIXME: Tell the call sites about the `saved_return_value` destination
// And take that into account in the movs below.
perform_needed_unwinds<OpType>();
if (must_enter_finalizer()) {
VERIFY(m_current_basic_block->finalizer() != nullptr);
// Compare to:
// * Interpreter::do_return
// * Interpreter::run_bytecode::handle_ContinuePendingUnwind
// * Return::execute_impl
// * Yield::execute_impl
if constexpr (IsSame<OpType, Op::Yield>)
emit<Bytecode::Op::PrepareYield>(Operand(Register::saved_return_value()), value);
else
emit<Bytecode::Op::Mov>(Operand(Register::saved_return_value()), value);
emit<Bytecode::Op::Mov>(Operand(Register::exception()), add_constant(Value {}));
// FIXME: Do we really need to clear the return value register here?
emit<Bytecode::Op::Mov>(Operand(Register::return_value()), add_constant(Value {}));
emit<Bytecode::Op::Jump>(Label { *m_current_basic_block->finalizer() });
return;
}
if constexpr (IsSame<OpType, Op::Return>)
emit<Op::Return>(value);
else
emit<Op::Yield>(nullptr, value);
}
void start_boundary(BlockBoundaryType type) { m_boundaries.append(type); }
void end_boundary(BlockBoundaryType type)
{
VERIFY(m_boundaries.last() == type);
m_boundaries.take_last();
}
[[nodiscard]] ScopedOperand copy_if_needed_to_preserve_evaluation_order(ScopedOperand const&);
[[nodiscard]] ScopedOperand get_this(Optional<ScopedOperand> preferred_dst = {});
void emit_get_by_id(ScopedOperand dst, ScopedOperand base, IdentifierTableIndex property_identifier, Optional<IdentifierTableIndex> base_identifier = {});
void emit_get_by_id_with_this(ScopedOperand dst, ScopedOperand base, IdentifierTableIndex, ScopedOperand this_value);
void emit_iterator_value(ScopedOperand dst, ScopedOperand result);
void emit_iterator_complete(ScopedOperand dst, ScopedOperand result);
[[nodiscard]] size_t next_global_variable_cache() { return m_next_global_variable_cache++; }
[[nodiscard]] size_t next_property_lookup_cache() { return m_next_property_lookup_cache++; }
enum class DeduplicateConstant {
Yes,
No,
};
[[nodiscard]] ScopedOperand add_constant(Value);
[[nodiscard]] Value get_constant(ScopedOperand const& operand) const
{
VERIFY(operand.operand().is_constant());
return m_constants[operand.operand().index()];
}
UnwindContext const* current_unwind_context() const { return m_current_unwind_context; }
[[nodiscard]] bool is_finished() const { return m_finished; }
[[nodiscard]] bool must_propagate_completion() const { return m_must_propagate_completion; }
private:
VM& m_vm;
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> compile(VM&, ASTNode const&, FunctionKind, GCPtr<ECMAScriptFunctionObject const>, MustPropagateCompletion, Vector<DeprecatedFlyString> local_variable_names);
enum class JumpType {
Continue,
Break,
};
void generate_scoped_jump(JumpType);
void generate_labelled_jump(JumpType, DeprecatedFlyString const& label);
Generator(VM&, GCPtr<ECMAScriptFunctionObject const>, MustPropagateCompletion);
~Generator() = default;
void grow(size_t);
// Returns true if a fused instruction was emitted.
[[nodiscard]] bool fuse_compare_and_jump(ScopedOperand const& condition, Label true_target, Label false_target);
struct LabelableScope {
Label bytecode_target;
Vector<DeprecatedFlyString> language_label_set;
};
BasicBlock* m_current_basic_block { nullptr };
ASTNode const* m_current_ast_node { nullptr };
UnwindContext const* m_current_unwind_context { nullptr };
Vector<NonnullOwnPtr<BasicBlock>> m_root_basic_blocks;
NonnullOwnPtr<StringTable> m_string_table;
NonnullOwnPtr<IdentifierTable> m_identifier_table;
NonnullOwnPtr<RegexTable> m_regex_table;
MarkedVector<Value> m_constants;
mutable Optional<ScopedOperand> m_true_constant;
mutable Optional<ScopedOperand> m_false_constant;
mutable Optional<ScopedOperand> m_null_constant;
mutable Optional<ScopedOperand> m_undefined_constant;
mutable Optional<ScopedOperand> m_empty_constant;
mutable HashMap<i32, ScopedOperand> m_int32_constants;
ScopedOperand m_accumulator;
ScopedOperand m_this_value;
Vector<Register> m_free_registers;
u32 m_next_register { Register::reserved_register_count };
u32 m_next_block { 1 };
u32 m_next_property_lookup_cache { 0 };
u32 m_next_global_variable_cache { 0 };
FunctionKind m_enclosing_function_kind { FunctionKind::Normal };
Vector<LabelableScope> m_continuable_scopes;
Vector<LabelableScope> m_breakable_scopes;
Vector<BlockBoundaryType> m_boundaries;
Vector<ScopedOperand> m_home_objects;
HashTable<u32> m_initialized_locals;
bool m_finished { false };
bool m_must_propagate_completion { true };
GCPtr<ECMAScriptFunctionObject const> m_function;
Optional<IdentifierTableIndex> m_length_identifier;
};
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Bytecode/IdentifierTable.h>
namespace JS::Bytecode {
IdentifierTableIndex IdentifierTable::insert(DeprecatedFlyString string)
{
m_identifiers.append(move(string));
VERIFY(m_identifiers.size() <= NumericLimits<u32>::max());
return { static_cast<u32>(m_identifiers.size() - 1) };
}
DeprecatedFlyString const& IdentifierTable::get(IdentifierTableIndex index) const
{
return m_identifiers[index.value];
}
void IdentifierTable::dump() const
{
outln("Identifier Table:");
for (size_t i = 0; i < m_identifiers.size(); i++)
outln("{}: {}", i, m_identifiers[i]);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DeprecatedFlyString.h>
#include <AK/DistinctNumeric.h>
#include <AK/Vector.h>
namespace JS::Bytecode {
struct IdentifierTableIndex {
bool is_valid() const { return value != NumericLimits<u32>::max(); }
u32 value { 0 };
};
class IdentifierTable {
AK_MAKE_NONMOVABLE(IdentifierTable);
AK_MAKE_NONCOPYABLE(IdentifierTable);
public:
IdentifierTable() = default;
IdentifierTableIndex insert(DeprecatedFlyString);
DeprecatedFlyString const& get(IdentifierTableIndex) const;
void dump() const;
bool is_empty() const { return m_identifiers.is_empty(); }
private:
Vector<DeprecatedFlyString> m_identifiers;
};
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/Instruction.h>
#include <LibJS/Bytecode/Op.h>
namespace JS::Bytecode {
void Instruction::destroy(Instruction& instruction)
{
#define __BYTECODE_OP(op) \
case Type::op: \
static_cast<Op::op&>(instruction).~op(); \
return;
switch (instruction.type()) {
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
default:
VERIFY_NOT_REACHED();
}
#undef __BYTECODE_OP
}
void Instruction::visit_labels(Function<void(JS::Bytecode::Label&)> visitor)
{
#define __BYTECODE_OP(op) \
case Type::op: \
static_cast<Op::op&>(*this).visit_labels_impl(move(visitor)); \
return;
switch (type()) {
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
default:
VERIFY_NOT_REACHED();
}
#undef __BYTECODE_OP
}
void Instruction::visit_operands(Function<void(JS::Bytecode::Operand&)> visitor)
{
#define __BYTECODE_OP(op) \
case Type::op: \
static_cast<Op::op&>(*this).visit_operands_impl(move(visitor)); \
return;
switch (type()) {
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
default:
VERIFY_NOT_REACHED();
}
#undef __BYTECODE_OP
}
template<typename Op>
concept HasVariableLength = Op::IsVariableLength;
template<typename Op>
concept HasFixedLength = !Op::IsVariableLength;
template<HasVariableLength Op>
size_t get_length_impl(Op const& op)
{
return op.length_impl();
}
// Function template for types without a length_impl method
template<HasFixedLength Op>
size_t get_length_impl(Op const&)
{
return sizeof(Op);
}
size_t Instruction::length() const
{
#define __BYTECODE_OP(op) \
case Type::op: { \
auto& typed_op = static_cast<Op::op const&>(*this); \
return get_length_impl(typed_op); \
}
switch (type()) {
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
default:
VERIFY_NOT_REACHED();
}
#undef __BYTECODE_OP
}
UnrealizedSourceRange InstructionStreamIterator::source_range() const
{
VERIFY(m_executable);
auto record = m_executable->source_map.get(offset()).value();
return {
.source_code = m_executable->source_code,
.start_offset = record.source_start_offset,
.end_offset = record.source_end_offset,
};
}
RefPtr<SourceCode> InstructionStreamIterator::source_code() const
{
return m_executable ? m_executable->source_code.ptr() : nullptr;
}
Operand::Operand(Register reg)
: m_type(Type::Register)
, m_index(reg.index())
{
}
}

View file

@ -0,0 +1,219 @@
/*
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Forward.h>
#include <AK/Function.h>
#include <AK/Span.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Forward.h>
#include <LibJS/SourceRange.h>
#define ENUMERATE_BYTECODE_OPS(O) \
O(Add) \
O(AddPrivateName) \
O(ArrayAppend) \
O(AsyncIteratorClose) \
O(Await) \
O(BitwiseAnd) \
O(BitwiseNot) \
O(BitwiseOr) \
O(BitwiseXor) \
O(BlockDeclarationInstantiation) \
O(Call) \
O(CallBuiltin) \
O(CallConstruct) \
O(CallDirectEval) \
O(CallWithArgumentArray) \
O(Catch) \
O(ConcatString) \
O(ContinuePendingUnwind) \
O(CopyObjectExcludingProperties) \
O(CreateArguments) \
O(CreateLexicalEnvironment) \
O(CreatePrivateEnvironment) \
O(CreateRestParams) \
O(CreateVariable) \
O(CreateVariableEnvironment) \
O(Decrement) \
O(DeleteById) \
O(DeleteByIdWithThis) \
O(DeleteByValue) \
O(DeleteByValueWithThis) \
O(DeleteVariable) \
O(Div) \
O(Dump) \
O(End) \
O(EnterObjectEnvironment) \
O(EnterUnwindContext) \
O(Exp) \
O(GetArgument) \
O(GetById) \
O(GetByIdWithThis) \
O(GetByValue) \
O(GetByValueWithThis) \
O(GetCalleeAndThisFromEnvironment) \
O(GetGlobal) \
O(GetImportMeta) \
O(GetIterator) \
O(GetLength) \
O(GetLengthWithThis) \
O(GetMethod) \
O(GetNewTarget) \
O(GetNextMethodFromIteratorRecord) \
O(GetObjectFromIteratorRecord) \
O(GetObjectPropertyIterator) \
O(GetPrivateById) \
O(GetBinding) \
O(GreaterThan) \
O(GreaterThanEquals) \
O(HasPrivateId) \
O(ImportCall) \
O(In) \
O(Increment) \
O(InitializeLexicalBinding) \
O(InitializeVariableBinding) \
O(InstanceOf) \
O(IteratorClose) \
O(IteratorNext) \
O(IteratorToArray) \
O(Jump) \
O(JumpFalse) \
O(JumpGreaterThan) \
O(JumpGreaterThanEquals) \
O(JumpIf) \
O(JumpLessThan) \
O(JumpLessThanEquals) \
O(JumpLooselyEquals) \
O(JumpLooselyInequals) \
O(JumpNullish) \
O(JumpStrictlyEquals) \
O(JumpStrictlyInequals) \
O(JumpTrue) \
O(JumpUndefined) \
O(LeaveFinally) \
O(LeaveLexicalEnvironment) \
O(LeavePrivateEnvironment) \
O(LeaveUnwindContext) \
O(LeftShift) \
O(LessThan) \
O(LessThanEquals) \
O(LooselyEquals) \
O(LooselyInequals) \
O(Mod) \
O(Mov) \
O(Mul) \
O(NewArray) \
O(NewClass) \
O(NewFunction) \
O(NewObject) \
O(NewPrimitiveArray) \
O(NewRegExp) \
O(NewTypeError) \
O(Not) \
O(PrepareYield) \
O(PostfixDecrement) \
O(PostfixIncrement) \
O(PutById) \
O(PutByIdWithThis) \
O(PutBySpread) \
O(PutByValue) \
O(PutByValueWithThis) \
O(PutPrivateById) \
O(ResolveSuperBase) \
O(ResolveThisBinding) \
O(RestoreScheduledJump) \
O(Return) \
O(RightShift) \
O(ScheduleJump) \
O(SetArgument) \
O(SetLexicalBinding) \
O(SetVariableBinding) \
O(StrictlyEquals) \
O(StrictlyInequals) \
O(Sub) \
O(SuperCallWithArgumentArray) \
O(Throw) \
O(ThrowIfNotObject) \
O(ThrowIfNullish) \
O(ThrowIfTDZ) \
O(Typeof) \
O(TypeofBinding) \
O(UnaryMinus) \
O(UnaryPlus) \
O(UnsignedRightShift) \
O(Yield)
namespace JS::Bytecode {
class alignas(void*) Instruction {
public:
constexpr static bool IsTerminator = false;
static constexpr bool IsVariableLength = false;
enum class Type {
#define __BYTECODE_OP(op) \
op,
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
#undef __BYTECODE_OP
};
Type type() const { return m_type; }
size_t length() const;
ByteString to_byte_string(Bytecode::Executable const&) const;
void visit_labels(Function<void(Label&)> visitor);
void visit_operands(Function<void(Operand&)> visitor);
static void destroy(Instruction&);
protected:
explicit Instruction(Type type)
: m_type(type)
{
}
void visit_labels_impl(Function<void(Label&)>) { }
void visit_operands_impl(Function<void(Operand&)>) { }
private:
Type m_type {};
};
class InstructionStreamIterator {
public:
InstructionStreamIterator(ReadonlyBytes bytes, Executable const* executable = nullptr, size_t offset = 0)
: m_begin(bytes.data())
, m_end(bytes.data() + bytes.size())
, m_ptr(bytes.data() + offset)
, m_executable(executable)
{
}
size_t offset() const { return m_ptr - m_begin; }
bool at_end() const { return m_ptr >= m_end; }
Instruction const& operator*() const { return dereference(); }
ALWAYS_INLINE void operator++()
{
m_ptr += dereference().length();
}
UnrealizedSourceRange source_range() const;
RefPtr<SourceCode> source_code() const;
Executable const* executable() const { return m_executable; }
private:
Instruction const& dereference() const { return *reinterpret_cast<Instruction const*>(m_ptr); }
u8 const* m_begin { nullptr };
u8 const* m_end { nullptr };
u8 const* m_ptr { nullptr };
GCPtr<Executable const> m_executable;
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/Label.h>
#include <LibJS/Bytecode/Register.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/Runtime/FunctionKind.h>
#include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Value.h>
namespace JS::Bytecode {
class InstructionStreamIterator;
class Interpreter {
public:
explicit Interpreter(VM&);
~Interpreter();
[[nodiscard]] Realm& realm() { return *m_realm; }
[[nodiscard]] Object& global_object() { return *m_global_object; }
[[nodiscard]] DeclarativeEnvironment& global_declarative_environment() { return *m_global_declarative_environment; }
VM& vm() { return m_vm; }
VM const& vm() const { return m_vm; }
ThrowCompletionOr<Value> run(Script&, JS::GCPtr<Environment> lexical_environment_override = nullptr);
ThrowCompletionOr<Value> run(SourceTextModule&);
ThrowCompletionOr<Value> run(Bytecode::Executable& executable, Optional<size_t> entry_point = {}, Value initial_accumulator_value = {})
{
auto result_and_return_register = run_executable(executable, entry_point, initial_accumulator_value);
return move(result_and_return_register.value);
}
struct ResultAndReturnRegister {
ThrowCompletionOr<Value> value;
Value return_register_value;
};
ResultAndReturnRegister run_executable(Bytecode::Executable&, Optional<size_t> entry_point, Value initial_accumulator_value = {});
ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); }
ALWAYS_INLINE Value& saved_return_value() { return reg(Register::saved_return_value()); }
Value& reg(Register const& r)
{
return m_registers_and_constants_and_locals.data()[r.index()];
}
Value reg(Register const& r) const
{
return m_registers_and_constants_and_locals.data()[r.index()];
}
[[nodiscard]] Value get(Operand) const;
void set(Operand, Value);
Value do_yield(Value value, Optional<Label> continuation);
void do_return(Value value)
{
reg(Register::return_value()) = value;
reg(Register::exception()) = {};
}
void enter_unwind_context();
void leave_unwind_context();
void catch_exception(Operand dst);
void restore_scheduled_jump();
void leave_finally();
void enter_object_environment(Object&);
Executable& current_executable() { return *m_current_executable; }
Executable const& current_executable() const { return *m_current_executable; }
Optional<size_t> program_counter() const { return m_program_counter; }
Span<Value> allocate_argument_values(size_t argument_count)
{
m_argument_values_buffer.resize_and_keep_capacity(argument_count);
return m_argument_values_buffer.span();
}
ExecutionContext& running_execution_context() { return *m_running_execution_context; }
private:
void run_bytecode(size_t entry_point);
enum class HandleExceptionResponse {
ExitFromExecutable,
ContinueInThisExecutable,
};
[[nodiscard]] HandleExceptionResponse handle_exception(size_t& program_counter, Value exception);
VM& m_vm;
Optional<size_t> m_scheduled_jump;
GCPtr<Executable> m_current_executable { nullptr };
GCPtr<Realm> m_realm { nullptr };
GCPtr<Object> m_global_object { nullptr };
GCPtr<DeclarativeEnvironment> m_global_declarative_environment { nullptr };
Optional<size_t&> m_program_counter;
Span<Value> m_arguments;
Span<Value> m_registers_and_constants_and_locals;
Vector<Value> m_argument_values_buffer;
ExecutionContext* m_running_execution_context { nullptr };
};
extern bool g_dump_bytecode;
ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM&, ASTNode const&, JS::FunctionKind kind, DeprecatedFlyString const& name);
ThrowCompletionOr<NonnullGCPtr<Bytecode::Executable>> compile(VM&, ECMAScriptFunctionObject const&);
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Bytecode/BasicBlock.h>
#include <LibJS/Bytecode/Label.h>
namespace JS::Bytecode {
Label::Label(Bytecode::BasicBlock const& basic_block)
: m_address_or_basic_block_index(basic_block.index())
{
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Format.h>
namespace JS::Bytecode {
class BasicBlock;
class Label {
public:
explicit Label(BasicBlock const&);
explicit Label(u32 basic_block_index)
: m_address_or_basic_block_index(basic_block_index)
{
}
// Used while compiling.
size_t basic_block_index() const { return m_address_or_basic_block_index; }
// Used after compiling.
size_t address() const { return m_address_or_basic_block_index; }
void set_address(size_t address) { m_address_or_basic_block_index = address; }
private:
u32 m_address_or_basic_block_index { 0 };
};
}
template<>
struct AK::Formatter<JS::Bytecode::Label> : AK::Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, JS::Bytecode::Label const& label)
{
return AK::Formatter<FormatString>::format(builder, "@{:x}"sv, label.address());
}
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <LibJS/Forward.h>
namespace JS::Bytecode {
class Operand {
public:
enum class Type {
Register,
Local,
Constant,
};
[[nodiscard]] bool operator==(Operand const&) const = default;
explicit Operand(Type type, u32 index)
: m_type(type)
, m_index(index)
{
}
explicit Operand(Register);
[[nodiscard]] bool is_register() const { return m_type == Type::Register; }
[[nodiscard]] bool is_local() const { return m_type == Type::Local; }
[[nodiscard]] bool is_constant() const { return m_type == Type::Constant; }
[[nodiscard]] Type type() const { return m_type; }
[[nodiscard]] u32 index() const { return m_index; }
[[nodiscard]] Register as_register() const;
void offset_index_by(u32 offset) { m_index += offset; }
private:
Type m_type {};
u32 m_index { 0 };
};
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Bytecode/RegexTable.h>
namespace JS::Bytecode {
RegexTableIndex RegexTable::insert(ParsedRegex regex)
{
m_regexes.append(move(regex));
return m_regexes.size() - 1;
}
ParsedRegex const& RegexTable::get(RegexTableIndex index) const
{
return m_regexes[index.value()];
}
void RegexTable::dump() const
{
outln("Regex Table:");
for (size_t i = 0; i < m_regexes.size(); i++)
outln("{}: {}", i, m_regexes[i].pattern);
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <AK/DistinctNumeric.h>
#include <AK/Vector.h>
#include <LibRegex/RegexParser.h>
namespace JS::Bytecode {
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(size_t, RegexTableIndex, Comparison);
struct ParsedRegex {
regex::Parser::Result regex;
ByteString pattern;
regex::RegexOptions<ECMAScriptFlags> flags;
};
class RegexTable {
AK_MAKE_NONMOVABLE(RegexTable);
AK_MAKE_NONCOPYABLE(RegexTable);
public:
RegexTable() = default;
RegexTableIndex insert(ParsedRegex);
ParsedRegex const& get(RegexTableIndex) const;
void dump() const;
bool is_empty() const { return m_regexes.is_empty(); }
private:
Vector<ParsedRegex> m_regexes;
};
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Format.h>
namespace JS::Bytecode {
class Register {
public:
constexpr static u32 accumulator_index = 0;
static constexpr Register accumulator()
{
return Register(accumulator_index);
}
constexpr static u32 saved_return_value_index = 1;
static constexpr Register saved_return_value()
{
return Register(saved_return_value_index);
}
static constexpr u32 exception_index = 2;
static constexpr Register exception()
{
return Register(exception_index);
}
static constexpr Register this_value()
{
constexpr u32 this_value_index = 3;
return Register(this_value_index);
}
static constexpr Register return_value()
{
constexpr u32 return_value_index = 4;
return Register(return_value_index);
}
static constexpr Register saved_exception()
{
constexpr u32 saved_exception_index = 5;
return Register(saved_exception_index);
}
static constexpr u32 reserved_register_count = 6;
constexpr explicit Register(u32 index)
: m_index(index)
{
}
constexpr bool operator==(Register reg) const { return m_index == reg.index(); }
constexpr u32 index() const { return m_index; }
private:
u32 m_index;
};
}
template<>
struct AK::Formatter<JS::Bytecode::Register> : AK::Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, JS::Bytecode::Register const& value)
{
if (value.index() == JS::Bytecode::Register::accumulator_index)
return builder.put_string("acc"sv);
return AK::Formatter<FormatString>::format(builder, "${}"sv, value.index());
}
};

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Bytecode/Generator.h>
#include <LibJS/Bytecode/ScopedOperand.h>
namespace JS::Bytecode {
ScopedOperandImpl::~ScopedOperandImpl()
{
if (!m_generator.is_finished() && m_operand.is_register() && m_operand.as_register().index() >= Register::reserved_register_count)
m_generator.free_register(m_operand.as_register());
}
Register Operand::as_register() const
{
return Register { m_index };
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <LibJS/Bytecode/Operand.h>
namespace JS::Bytecode {
class ScopedOperandImpl : public RefCounted<ScopedOperandImpl> {
public:
ScopedOperandImpl(Generator& generator, Operand operand)
: m_generator(generator)
, m_operand(operand)
{
}
~ScopedOperandImpl();
[[nodiscard]] Operand const& operand() const { return m_operand; }
[[nodiscard]] Operand& operand() { return m_operand; }
private:
Generator& m_generator;
Operand m_operand;
};
class ScopedOperand {
public:
explicit ScopedOperand(Generator& generator, Operand operand)
: m_impl(adopt_ref(*new ScopedOperandImpl(generator, operand)))
{
}
[[nodiscard]] Operand const& operand() const { return m_impl->operand(); }
[[nodiscard]] Operand& operand() { return m_impl->operand(); }
operator Operand() const { return operand(); }
[[nodiscard]] bool operator==(ScopedOperand const& other) const { return operand() == other.operand(); }
[[nodiscard]] size_t ref_count() const { return m_impl->ref_count(); }
private:
NonnullRefPtr<ScopedOperandImpl> m_impl;
};
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Bytecode/StringTable.h>
namespace JS::Bytecode {
StringTableIndex StringTable::insert(ByteString string)
{
m_strings.append(move(string));
return m_strings.size() - 1;
}
ByteString const& StringTable::get(StringTableIndex index) const
{
return m_strings[index.value()];
}
void StringTable::dump() const
{
outln("String Table:");
for (size_t i = 0; i < m_strings.size(); i++)
outln("{}: {}", i, m_strings[i]);
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <AK/DistinctNumeric.h>
#include <AK/Vector.h>
namespace JS::Bytecode {
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u32, StringTableIndex, Comparison);
class StringTable {
AK_MAKE_NONMOVABLE(StringTable);
AK_MAKE_NONCOPYABLE(StringTable);
public:
StringTable() = default;
StringTableIndex insert(ByteString);
ByteString const& get(StringTableIndex) const;
void dump() const;
bool is_empty() const { return m_strings.is_empty(); }
private:
Vector<ByteString> m_strings;
};
}