mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 20:29:18 +00:00
Everywhere: Hoist the Libraries folder to the top-level
This commit is contained in:
parent
950e819ee7
commit
93712b24bf
Notes:
github-actions[bot]
2024-11-10 11:51:52 +00:00
Author: https://github.com/trflynn89
Commit: 93712b24bf
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2256
Reviewed-by: https://github.com/sideshowbarker
4547 changed files with 104 additions and 113 deletions
3577
Libraries/LibJS/Bytecode/ASTCodegen.cpp
Normal file
3577
Libraries/LibJS/Bytecode/ASTCodegen.cpp
Normal file
File diff suppressed because it is too large
Load diff
40
Libraries/LibJS/Bytecode/BasicBlock.cpp
Normal file
40
Libraries/LibJS/Bytecode/BasicBlock.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
83
Libraries/LibJS/Bytecode/BasicBlock.h
Normal file
83
Libraries/LibJS/Bytecode/BasicBlock.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
26
Libraries/LibJS/Bytecode/Builtins.cpp
Normal file
26
Libraries/LibJS/Bytecode/Builtins.cpp
Normal 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 {};
|
||||
}
|
||||
|
||||
}
|
74
Libraries/LibJS/Bytecode/Builtins.h
Normal file
74
Libraries/LibJS/Bytecode/Builtins.h
Normal 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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
17
Libraries/LibJS/Bytecode/CodeGenerationError.cpp
Normal file
17
Libraries/LibJS/Bytecode/CodeGenerationError.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
26
Libraries/LibJS/Bytecode/CodeGenerationError.h
Normal file
26
Libraries/LibJS/Bytecode/CodeGenerationError.h
Normal 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>;
|
||||
|
||||
}
|
117
Libraries/LibJS/Bytecode/Executable.cpp
Normal file
117
Libraries/LibJS/Bytecode/Executable.cpp
Normal 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,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
111
Libraries/LibJS/Bytecode/Executable.h
Normal file
111
Libraries/LibJS/Bytecode/Executable.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
1243
Libraries/LibJS/Bytecode/Generator.cpp
Normal file
1243
Libraries/LibJS/Bytecode/Generator.cpp
Normal file
File diff suppressed because it is too large
Load diff
409
Libraries/LibJS/Bytecode/Generator.h
Normal file
409
Libraries/LibJS/Bytecode/Generator.h
Normal 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 = █
|
||||
}
|
||||
|
||||
[[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;
|
||||
};
|
||||
|
||||
}
|
30
Libraries/LibJS/Bytecode/IdentifierTable.cpp
Normal file
30
Libraries/LibJS/Bytecode/IdentifierTable.cpp
Normal 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]);
|
||||
}
|
||||
|
||||
}
|
36
Libraries/LibJS/Bytecode/IdentifierTable.h
Normal file
36
Libraries/LibJS/Bytecode/IdentifierTable.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
119
Libraries/LibJS/Bytecode/Instruction.cpp
Normal file
119
Libraries/LibJS/Bytecode/Instruction.cpp
Normal 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())
|
||||
{
|
||||
}
|
||||
|
||||
}
|
219
Libraries/LibJS/Bytecode/Instruction.h
Normal file
219
Libraries/LibJS/Bytecode/Instruction.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
3776
Libraries/LibJS/Bytecode/Interpreter.cpp
Normal file
3776
Libraries/LibJS/Bytecode/Interpreter.cpp
Normal file
File diff suppressed because it is too large
Load diff
115
Libraries/LibJS/Bytecode/Interpreter.h
Normal file
115
Libraries/LibJS/Bytecode/Interpreter.h
Normal 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&);
|
||||
|
||||
}
|
17
Libraries/LibJS/Bytecode/Label.cpp
Normal file
17
Libraries/LibJS/Bytecode/Label.cpp
Normal 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())
|
||||
{
|
||||
}
|
||||
|
||||
}
|
44
Libraries/LibJS/Bytecode/Label.h
Normal file
44
Libraries/LibJS/Bytecode/Label.h
Normal 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());
|
||||
}
|
||||
};
|
2859
Libraries/LibJS/Bytecode/Op.h
Normal file
2859
Libraries/LibJS/Bytecode/Op.h
Normal file
File diff suppressed because it is too large
Load diff
48
Libraries/LibJS/Bytecode/Operand.h
Normal file
48
Libraries/LibJS/Bytecode/Operand.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
29
Libraries/LibJS/Bytecode/RegexTable.cpp
Normal file
29
Libraries/LibJS/Bytecode/RegexTable.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
40
Libraries/LibJS/Bytecode/RegexTable.h
Normal file
40
Libraries/LibJS/Bytecode/RegexTable.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
79
Libraries/LibJS/Bytecode/Register.h
Normal file
79
Libraries/LibJS/Bytecode/Register.h
Normal 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());
|
||||
}
|
||||
};
|
23
Libraries/LibJS/Bytecode/ScopedOperand.cpp
Normal file
23
Libraries/LibJS/Bytecode/ScopedOperand.cpp
Normal 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 };
|
||||
}
|
||||
|
||||
}
|
51
Libraries/LibJS/Bytecode/ScopedOperand.h
Normal file
51
Libraries/LibJS/Bytecode/ScopedOperand.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
29
Libraries/LibJS/Bytecode/StringTable.cpp
Normal file
29
Libraries/LibJS/Bytecode/StringTable.cpp
Normal 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]);
|
||||
}
|
||||
|
||||
}
|
33
Libraries/LibJS/Bytecode/StringTable.h
Normal file
33
Libraries/LibJS/Bytecode/StringTable.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue