mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:18:11 +00:00
LibJS: Store strings in a string table
Instead of using Strings in the bytecode ops this adds a global string table to the Executable struct which individual operations can refer to using indices. This brings bytecode ops one step closer to being pointer free.
This commit is contained in:
parent
4efccbd030
commit
6a0d1fa259
16 changed files with 173 additions and 82 deletions
|
@ -208,12 +208,12 @@ void BigIntLiteral::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
|
|
||||||
void StringLiteral::generate_bytecode(Bytecode::Generator& generator) const
|
void StringLiteral::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
{
|
{
|
||||||
generator.emit<Bytecode::Op::NewString>(m_value);
|
generator.emit<Bytecode::Op::NewString>(generator.intern_string(m_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Identifier::generate_bytecode(Bytecode::Generator& generator) const
|
void Identifier::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
{
|
{
|
||||||
generator.emit<Bytecode::Op::GetVariable>(m_string);
|
generator.emit<Bytecode::Op::GetVariable>(generator.intern_string(m_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const
|
void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
|
@ -223,7 +223,7 @@ void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) con
|
||||||
|
|
||||||
if (m_op == AssignmentOp::Assignment) {
|
if (m_op == AssignmentOp::Assignment) {
|
||||||
m_rhs->generate_bytecode(generator);
|
m_rhs->generate_bytecode(generator);
|
||||||
generator.emit<Bytecode::Op::SetVariable>(identifier.string());
|
generator.emit<Bytecode::Op::SetVariable>(generator.intern_string(identifier.string()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) con
|
||||||
TODO();
|
TODO();
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.emit<Bytecode::Op::SetVariable>(identifier.string());
|
generator.emit<Bytecode::Op::SetVariable>(generator.intern_string(identifier.string()));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -289,7 +289,8 @@ void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) con
|
||||||
} else {
|
} else {
|
||||||
VERIFY(is<Identifier>(expression.property()));
|
VERIFY(is<Identifier>(expression.property()));
|
||||||
m_rhs->generate_bytecode(generator);
|
m_rhs->generate_bytecode(generator);
|
||||||
generator.emit<Bytecode::Op::PutById>(object_reg, static_cast<Identifier const&>(expression.property()).string());
|
auto identifier_table_ref = generator.intern_string(static_cast<Identifier const&>(expression.property()).string());
|
||||||
|
generator.emit<Bytecode::Op::PutById>(object_reg, identifier_table_ref);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,7 +488,8 @@ void MemberExpression::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
TODO();
|
TODO();
|
||||||
} else {
|
} else {
|
||||||
VERIFY(is<Identifier>(property()));
|
VERIFY(is<Identifier>(property()));
|
||||||
generator.emit<Bytecode::Op::GetById>(static_cast<Identifier const&>(property()).string());
|
auto identifier_table_ref = generator.intern_string(static_cast<Identifier const&>(property()).string());
|
||||||
|
generator.emit<Bytecode::Op::GetById>(identifier_table_ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,7 +645,7 @@ void UpdateExpression::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
{
|
{
|
||||||
if (is<Identifier>(*m_argument)) {
|
if (is<Identifier>(*m_argument)) {
|
||||||
auto& identifier = static_cast<Identifier const&>(*m_argument);
|
auto& identifier = static_cast<Identifier const&>(*m_argument);
|
||||||
generator.emit<Bytecode::Op::GetVariable>(identifier.string());
|
generator.emit<Bytecode::Op::GetVariable>(generator.intern_string(identifier.string()));
|
||||||
|
|
||||||
Optional<Bytecode::Register> previous_value_for_postfix_reg;
|
Optional<Bytecode::Register> previous_value_for_postfix_reg;
|
||||||
if (!m_prefixed) {
|
if (!m_prefixed) {
|
||||||
|
@ -656,7 +658,7 @@ void UpdateExpression::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
else
|
else
|
||||||
generator.emit<Bytecode::Op::Decrement>();
|
generator.emit<Bytecode::Op::Decrement>();
|
||||||
|
|
||||||
generator.emit<Bytecode::Op::SetVariable>(identifier.string());
|
generator.emit<Bytecode::Op::SetVariable>(generator.intern_string(identifier.string()));
|
||||||
|
|
||||||
if (!m_prefixed)
|
if (!m_prefixed)
|
||||||
generator.emit<Bytecode::Op::Load>(*previous_value_for_postfix_reg);
|
generator.emit<Bytecode::Op::Load>(*previous_value_for_postfix_reg);
|
||||||
|
|
|
@ -47,13 +47,13 @@ void BasicBlock::seal()
|
||||||
// It also doesn't work because instructions that have String members use RefPtr internally which must be in writable memory.
|
// It also doesn't work because instructions that have String members use RefPtr internally which must be in writable memory.
|
||||||
}
|
}
|
||||||
|
|
||||||
void BasicBlock::dump() const
|
void BasicBlock::dump(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
Bytecode::InstructionStreamIterator it(instruction_stream());
|
Bytecode::InstructionStreamIterator it(instruction_stream());
|
||||||
if (!m_name.is_empty())
|
if (!m_name.is_empty())
|
||||||
warnln("{}:", m_name);
|
warnln("{}:", m_name);
|
||||||
while (!it.at_end()) {
|
while (!it.at_end()) {
|
||||||
warnln("[{:4x}] {}", it.offset(), (*it).to_string());
|
warnln("[{:4x}] {}", it.offset(), (*it).to_string(executable));
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
|
|
||||||
void seal();
|
void seal();
|
||||||
|
|
||||||
void dump() const;
|
void dump(Executable const&) const;
|
||||||
ReadonlyBytes instruction_stream() const { return ReadonlyBytes { m_buffer, m_buffer_size }; }
|
ReadonlyBytes instruction_stream() const { return ReadonlyBytes { m_buffer, m_buffer_size }; }
|
||||||
|
|
||||||
void* next_slot() { return m_buffer + m_buffer_size; }
|
void* next_slot() { return m_buffer + m_buffer_size; }
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
namespace JS::Bytecode {
|
namespace JS::Bytecode {
|
||||||
|
|
||||||
Generator::Generator()
|
Generator::Generator()
|
||||||
|
: m_string_table(make<StringTable>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ Executable Generator::generate(ASTNode const& node)
|
||||||
Generator generator;
|
Generator generator;
|
||||||
generator.switch_to_basic_block(generator.make_block());
|
generator.switch_to_basic_block(generator.make_block());
|
||||||
node.generate_bytecode(generator);
|
node.generate_bytecode(generator);
|
||||||
return { move(generator.m_root_basic_blocks), generator.m_next_register };
|
return { move(generator.m_root_basic_blocks), move(generator.m_string_table), generator.m_next_register };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Generator::grow(size_t additional_size)
|
void Generator::grow(size_t additional_size)
|
||||||
|
|
|
@ -12,13 +12,17 @@
|
||||||
#include <LibJS/Bytecode/BasicBlock.h>
|
#include <LibJS/Bytecode/BasicBlock.h>
|
||||||
#include <LibJS/Bytecode/Label.h>
|
#include <LibJS/Bytecode/Label.h>
|
||||||
#include <LibJS/Bytecode/Register.h>
|
#include <LibJS/Bytecode/Register.h>
|
||||||
|
#include <LibJS/Bytecode/StringTable.h>
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
|
|
||||||
namespace JS::Bytecode {
|
namespace JS::Bytecode {
|
||||||
|
|
||||||
struct Executable {
|
struct Executable {
|
||||||
NonnullOwnPtrVector<BasicBlock> basic_blocks;
|
NonnullOwnPtrVector<BasicBlock> basic_blocks;
|
||||||
|
NonnullOwnPtr<StringTable> string_table;
|
||||||
size_t number_of_registers { 0 };
|
size_t number_of_registers { 0 };
|
||||||
|
|
||||||
|
String const& get_string(StringTableIndex index) const { return string_table->get(index); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Generator {
|
class Generator {
|
||||||
|
@ -74,6 +78,11 @@ public:
|
||||||
return m_current_basic_block->is_terminated();
|
return m_current_basic_block->is_terminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringTableIndex intern_string(StringView const& string)
|
||||||
|
{
|
||||||
|
return m_string_table->insert(string);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Generator();
|
Generator();
|
||||||
~Generator();
|
~Generator();
|
||||||
|
@ -83,6 +92,7 @@ private:
|
||||||
|
|
||||||
BasicBlock* m_current_basic_block { nullptr };
|
BasicBlock* m_current_basic_block { nullptr };
|
||||||
NonnullOwnPtrVector<BasicBlock> m_root_basic_blocks;
|
NonnullOwnPtrVector<BasicBlock> m_root_basic_blocks;
|
||||||
|
NonnullOwnPtr<StringTable> m_string_table;
|
||||||
|
|
||||||
u32 m_next_register { 1 };
|
u32 m_next_register { 1 };
|
||||||
u32 m_next_block { 1 };
|
u32 m_next_block { 1 };
|
||||||
|
|
|
@ -73,7 +73,7 @@ public:
|
||||||
|
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
size_t length() const;
|
size_t length() const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
static void destroy(Instruction&);
|
static void destroy(Instruction&);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
|
#include <AK/TemporaryChange.h>
|
||||||
#include <LibJS/Bytecode/BasicBlock.h>
|
#include <LibJS/Bytecode/BasicBlock.h>
|
||||||
#include <LibJS/Bytecode/Instruction.h>
|
#include <LibJS/Bytecode/Instruction.h>
|
||||||
#include <LibJS/Bytecode/Interpreter.h>
|
#include <LibJS/Bytecode/Interpreter.h>
|
||||||
|
@ -38,6 +39,8 @@ Value Interpreter::run(Executable const& executable)
|
||||||
{
|
{
|
||||||
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable);
|
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable);
|
||||||
|
|
||||||
|
TemporaryChange restore_executable { m_current_executable, &executable };
|
||||||
|
|
||||||
CallFrame global_call_frame;
|
CallFrame global_call_frame;
|
||||||
if (vm().call_stack().is_empty()) {
|
if (vm().call_stack().is_empty()) {
|
||||||
global_call_frame.this_value = &global_object();
|
global_call_frame.this_value = &global_object();
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
}
|
}
|
||||||
void do_return(Value return_value) { m_return_value = return_value; }
|
void do_return(Value return_value) { m_return_value = return_value; }
|
||||||
|
|
||||||
|
Executable const& current_executable() { return *m_current_executable; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RegisterWindow& registers() { return m_register_windows.last(); }
|
RegisterWindow& registers() { return m_register_windows.last(); }
|
||||||
|
|
||||||
|
@ -47,6 +49,7 @@ private:
|
||||||
NonnullOwnPtrVector<RegisterWindow> m_register_windows;
|
NonnullOwnPtrVector<RegisterWindow> m_register_windows;
|
||||||
Optional<BasicBlock const*> m_pending_jump;
|
Optional<BasicBlock const*> m_pending_jump;
|
||||||
Value m_return_value;
|
Value m_return_value;
|
||||||
|
Executable const* m_current_executable { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
|
|
||||||
namespace JS::Bytecode {
|
namespace JS::Bytecode {
|
||||||
|
|
||||||
String Instruction::to_string() const
|
String Instruction::to_string(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
#define __BYTECODE_OP(op) \
|
#define __BYTECODE_OP(op) \
|
||||||
case Instruction::Type::op: \
|
case Instruction::Type::op: \
|
||||||
return static_cast<Bytecode::Op::op const&>(*this).to_string();
|
return static_cast<Bytecode::Op::op const&>(*this).to_string(executable);
|
||||||
|
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
|
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
|
||||||
|
@ -77,7 +77,7 @@ static Value typed_equals(GlobalObject&, Value src1, Value src2)
|
||||||
auto rhs = interpreter.accumulator(); \
|
auto rhs = interpreter.accumulator(); \
|
||||||
interpreter.accumulator() = op_snake_case(interpreter.global_object(), lhs, rhs); \
|
interpreter.accumulator() = op_snake_case(interpreter.global_object(), lhs, rhs); \
|
||||||
} \
|
} \
|
||||||
String OpTitleCase::to_string() const \
|
String OpTitleCase::to_string(Bytecode::Executable const&) const \
|
||||||
{ \
|
{ \
|
||||||
return String::formatted(#OpTitleCase " {}", m_lhs_reg); \
|
return String::formatted(#OpTitleCase " {}", m_lhs_reg); \
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ static Value typeof_(GlobalObject& global_object, Value value)
|
||||||
{ \
|
{ \
|
||||||
interpreter.accumulator() = op_snake_case(interpreter.global_object(), interpreter.accumulator()); \
|
interpreter.accumulator() = op_snake_case(interpreter.global_object(), interpreter.accumulator()); \
|
||||||
} \
|
} \
|
||||||
String OpTitleCase::to_string() const \
|
String OpTitleCase::to_string(Bytecode::Executable const&) const \
|
||||||
{ \
|
{ \
|
||||||
return #OpTitleCase; \
|
return #OpTitleCase; \
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ void NewArray::execute(Bytecode::Interpreter& interpreter) const
|
||||||
|
|
||||||
void NewString::execute(Bytecode::Interpreter& interpreter) const
|
void NewString::execute(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.accumulator() = js_string(interpreter.vm(), m_string);
|
interpreter.accumulator() = js_string(interpreter.vm(), interpreter.current_executable().get_string(m_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewObject::execute(Bytecode::Interpreter& interpreter) const
|
void NewObject::execute(Bytecode::Interpreter& interpreter) const
|
||||||
|
@ -137,24 +137,24 @@ void ConcatString::execute(Bytecode::Interpreter& interpreter) const
|
||||||
|
|
||||||
void GetVariable::execute(Bytecode::Interpreter& interpreter) const
|
void GetVariable::execute(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.accumulator() = interpreter.vm().get_variable(m_identifier, interpreter.global_object());
|
interpreter.accumulator() = interpreter.vm().get_variable(interpreter.current_executable().get_string(m_identifier), interpreter.global_object());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetVariable::execute(Bytecode::Interpreter& interpreter) const
|
void SetVariable::execute(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.vm().set_variable(m_identifier, interpreter.accumulator(), interpreter.global_object());
|
interpreter.vm().set_variable(interpreter.current_executable().get_string(m_identifier), interpreter.accumulator(), interpreter.global_object());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetById::execute(Bytecode::Interpreter& interpreter) const
|
void GetById::execute(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
if (auto* object = interpreter.accumulator().to_object(interpreter.global_object()))
|
if (auto* object = interpreter.accumulator().to_object(interpreter.global_object()))
|
||||||
interpreter.accumulator() = object->get(m_property);
|
interpreter.accumulator() = object->get(interpreter.current_executable().get_string(m_property));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PutById::execute(Bytecode::Interpreter& interpreter) const
|
void PutById::execute(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
if (auto* object = interpreter.reg(m_base).to_object(interpreter.global_object()))
|
if (auto* object = interpreter.reg(m_base).to_object(interpreter.global_object()))
|
||||||
object->put(m_property, interpreter.accumulator());
|
object->put(interpreter.current_executable().get_string(m_property), interpreter.accumulator());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Jump::execute(Bytecode::Interpreter& interpreter) const
|
void Jump::execute(Bytecode::Interpreter& interpreter) const
|
||||||
|
@ -255,27 +255,27 @@ void Decrement::execute(Bytecode::Interpreter& interpreter) const
|
||||||
interpreter.accumulator() = js_bigint(interpreter.vm().heap(), old_value.as_bigint().big_integer().minus(Crypto::SignedBigInteger { 1 }));
|
interpreter.accumulator() = js_bigint(interpreter.vm().heap(), old_value.as_bigint().big_integer().minus(Crypto::SignedBigInteger { 1 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
String Load::to_string() const
|
String Load::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return String::formatted("Load {}", m_src);
|
return String::formatted("Load {}", m_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
String LoadImmediate::to_string() const
|
String LoadImmediate::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return String::formatted("LoadImmediate {}", m_value);
|
return String::formatted("LoadImmediate {}", m_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
String Store::to_string() const
|
String Store::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return String::formatted("Store {}", m_dst);
|
return String::formatted("Store {}", m_dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
String NewBigInt::to_string() const
|
String NewBigInt::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return String::formatted("NewBigInt \"{}\"", m_bigint.to_base10());
|
return String::formatted("NewBigInt \"{}\"", m_bigint.to_base10());
|
||||||
}
|
}
|
||||||
|
|
||||||
String NewArray::to_string() const
|
String NewArray::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.append("NewArray");
|
builder.append("NewArray");
|
||||||
|
@ -291,63 +291,63 @@ String NewArray::to_string() const
|
||||||
return builder.to_string();
|
return builder.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
String NewString::to_string() const
|
String NewString::to_string(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
return String::formatted("NewString \"{}\"", m_string);
|
return String::formatted("NewString {} (\"{}\")", m_string, executable.string_table->get(m_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
String NewObject::to_string() const
|
String NewObject::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return "NewObject";
|
return "NewObject";
|
||||||
}
|
}
|
||||||
|
|
||||||
String ConcatString::to_string() const
|
String ConcatString::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return String::formatted("ConcatString {}", m_lhs);
|
return String::formatted("ConcatString {}", m_lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
String GetVariable::to_string() const
|
String GetVariable::to_string(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
return String::formatted("GetVariable {}", m_identifier);
|
return String::formatted("GetVariable {} ({})", m_identifier, executable.string_table->get(m_identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
String SetVariable::to_string() const
|
String SetVariable::to_string(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
return String::formatted("SetVariable {}", m_identifier);
|
return String::formatted("SetVariable {} ({})", m_identifier, executable.string_table->get(m_identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
String PutById::to_string() const
|
String PutById::to_string(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
return String::formatted("PutById base:{}, property:{}", m_base, m_property);
|
return String::formatted("PutById base:{}, property:{} ({})", m_base, m_property, executable.string_table->get(m_property));
|
||||||
}
|
}
|
||||||
|
|
||||||
String GetById::to_string() const
|
String GetById::to_string(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
return String::formatted("GetById {}", m_property);
|
return String::formatted("GetById {} ({})", m_property, executable.string_table->get(m_property));
|
||||||
}
|
}
|
||||||
|
|
||||||
String Jump::to_string() const
|
String Jump::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
if (m_true_target.has_value())
|
if (m_true_target.has_value())
|
||||||
return String::formatted("Jump {}", *m_true_target);
|
return String::formatted("Jump {}", *m_true_target);
|
||||||
return String::formatted("Jump <empty>");
|
return String::formatted("Jump <empty>");
|
||||||
}
|
}
|
||||||
|
|
||||||
String JumpConditional::to_string() const
|
String JumpConditional::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
auto true_string = m_true_target.has_value() ? String::formatted("{}", *m_true_target) : "<empty>";
|
auto true_string = m_true_target.has_value() ? String::formatted("{}", *m_true_target) : "<empty>";
|
||||||
auto false_string = m_false_target.has_value() ? String::formatted("{}", *m_false_target) : "<empty>";
|
auto false_string = m_false_target.has_value() ? String::formatted("{}", *m_false_target) : "<empty>";
|
||||||
return String::formatted("JumpConditional true:{} false:{}", true_string, false_string);
|
return String::formatted("JumpConditional true:{} false:{}", true_string, false_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
String JumpNullish::to_string() const
|
String JumpNullish::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
auto true_string = m_true_target.has_value() ? String::formatted("{}", *m_true_target) : "<empty>";
|
auto true_string = m_true_target.has_value() ? String::formatted("{}", *m_true_target) : "<empty>";
|
||||||
auto false_string = m_false_target.has_value() ? String::formatted("{}", *m_false_target) : "<empty>";
|
auto false_string = m_false_target.has_value() ? String::formatted("{}", *m_false_target) : "<empty>";
|
||||||
return String::formatted("JumpNullish null:{} nonnull:{}", true_string, false_string);
|
return String::formatted("JumpNullish null:{} nonnull:{}", true_string, false_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
String Call::to_string() const
|
String Call::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.appendff("Call callee:{}, this:{}", m_callee, m_this_value);
|
builder.appendff("Call callee:{}, this:{}", m_callee, m_this_value);
|
||||||
|
@ -363,22 +363,22 @@ String Call::to_string() const
|
||||||
return builder.to_string();
|
return builder.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
String EnterScope::to_string() const
|
String EnterScope::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return "EnterScope";
|
return "EnterScope";
|
||||||
}
|
}
|
||||||
|
|
||||||
String Return::to_string() const
|
String Return::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return "Return";
|
return "Return";
|
||||||
}
|
}
|
||||||
|
|
||||||
String Increment::to_string() const
|
String Increment::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return "Increment";
|
return "Increment";
|
||||||
}
|
}
|
||||||
|
|
||||||
String Decrement::to_string() const
|
String Decrement::to_string(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return "Decrement";
|
return "Decrement";
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/FlyString.h>
|
|
||||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||||
#include <LibJS/Bytecode/Instruction.h>
|
#include <LibJS/Bytecode/Instruction.h>
|
||||||
#include <LibJS/Bytecode/Label.h>
|
#include <LibJS/Bytecode/Label.h>
|
||||||
#include <LibJS/Bytecode/Register.h>
|
#include <LibJS/Bytecode/Register.h>
|
||||||
|
#include <LibJS/Bytecode/StringTable.h>
|
||||||
#include <LibJS/Heap/Cell.h>
|
#include <LibJS/Heap/Cell.h>
|
||||||
#include <LibJS/Runtime/Value.h>
|
#include <LibJS/Runtime/Value.h>
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Register m_src;
|
Register m_src;
|
||||||
|
@ -41,7 +41,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Value m_value;
|
Value m_value;
|
||||||
|
@ -56,7 +56,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Register m_dst;
|
Register m_dst;
|
||||||
|
@ -96,7 +96,7 @@ private:
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
void execute(Bytecode::Interpreter&) const; \
|
void execute(Bytecode::Interpreter&) const; \
|
||||||
String to_string() const; \
|
String to_string(Bytecode::Executable const&) const; \
|
||||||
\
|
\
|
||||||
private: \
|
private: \
|
||||||
Register m_lhs_reg; \
|
Register m_lhs_reg; \
|
||||||
|
@ -121,7 +121,7 @@ JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP)
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
void execute(Bytecode::Interpreter&) const; \
|
void execute(Bytecode::Interpreter&) const; \
|
||||||
String to_string() const; \
|
String to_string(Bytecode::Executable const&) const; \
|
||||||
};
|
};
|
||||||
|
|
||||||
JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP)
|
JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP)
|
||||||
|
@ -129,17 +129,17 @@ JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP)
|
||||||
|
|
||||||
class NewString final : public Instruction {
|
class NewString final : public Instruction {
|
||||||
public:
|
public:
|
||||||
NewString(String string)
|
NewString(StringTableIndex string)
|
||||||
: Instruction(Type::NewString)
|
: Instruction(Type::NewString)
|
||||||
, m_string(move(string))
|
, m_string(move(string))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String m_string;
|
StringTableIndex m_string;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NewObject final : public Instruction {
|
class NewObject final : public Instruction {
|
||||||
|
@ -150,7 +150,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NewBigInt final : public Instruction {
|
class NewBigInt final : public Instruction {
|
||||||
|
@ -162,7 +162,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Crypto::SignedBigInteger m_bigint;
|
Crypto::SignedBigInteger m_bigint;
|
||||||
|
@ -180,7 +180,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
size_t length() const { return sizeof(*this) + sizeof(Register) * m_element_count; }
|
size_t length() const { return sizeof(*this) + sizeof(Register) * m_element_count; }
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Register m_lhs;
|
Register m_lhs;
|
||||||
|
@ -206,52 +206,52 @@ private:
|
||||||
|
|
||||||
class SetVariable final : public Instruction {
|
class SetVariable final : public Instruction {
|
||||||
public:
|
public:
|
||||||
SetVariable(FlyString identifier)
|
SetVariable(StringTableIndex identifier)
|
||||||
: Instruction(Type::SetVariable)
|
: Instruction(Type::SetVariable)
|
||||||
, m_identifier(move(identifier))
|
, m_identifier(move(identifier))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FlyString m_identifier;
|
StringTableIndex m_identifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GetVariable final : public Instruction {
|
class GetVariable final : public Instruction {
|
||||||
public:
|
public:
|
||||||
GetVariable(FlyString identifier)
|
GetVariable(StringTableIndex identifier)
|
||||||
: Instruction(Type::GetVariable)
|
: Instruction(Type::GetVariable)
|
||||||
, m_identifier(move(identifier))
|
, m_identifier(move(identifier))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FlyString m_identifier;
|
StringTableIndex m_identifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GetById final : public Instruction {
|
class GetById final : public Instruction {
|
||||||
public:
|
public:
|
||||||
GetById(FlyString property)
|
GetById(StringTableIndex property)
|
||||||
: Instruction(Type::GetById)
|
: Instruction(Type::GetById)
|
||||||
, m_property(move(property))
|
, m_property(move(property))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FlyString m_property;
|
StringTableIndex m_property;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PutById final : public Instruction {
|
class PutById final : public Instruction {
|
||||||
public:
|
public:
|
||||||
PutById(Register base, FlyString property)
|
PutById(Register base, StringTableIndex property)
|
||||||
: Instruction(Type::PutById)
|
: Instruction(Type::PutById)
|
||||||
, m_base(base)
|
, m_base(base)
|
||||||
, m_property(move(property))
|
, m_property(move(property))
|
||||||
|
@ -259,11 +259,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Register m_base;
|
Register m_base;
|
||||||
FlyString m_property;
|
StringTableIndex m_property;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Jump : public Instruction {
|
class Jump : public Instruction {
|
||||||
|
@ -291,7 +291,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Optional<Label> m_true_target;
|
Optional<Label> m_true_target;
|
||||||
|
@ -306,7 +306,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class JumpNullish final : public Jump {
|
class JumpNullish final : public Jump {
|
||||||
|
@ -317,7 +317,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: This instruction is variable-width depending on the number of arguments!
|
// NOTE: This instruction is variable-width depending on the number of arguments!
|
||||||
|
@ -334,7 +334,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
size_t length() const { return sizeof(*this) + sizeof(Register) * m_argument_count; }
|
size_t length() const { return sizeof(*this) + sizeof(Register) * m_argument_count; }
|
||||||
|
|
||||||
|
@ -354,7 +354,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScopeNode const& m_scope_node;
|
ScopeNode const& m_scope_node;
|
||||||
|
@ -370,7 +370,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Increment final : public Instruction {
|
class Increment final : public Instruction {
|
||||||
|
@ -381,7 +381,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Decrement final : public Instruction {
|
class Decrement final : public Instruction {
|
||||||
|
@ -392,7 +392,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(Bytecode::Interpreter&) const;
|
void execute(Bytecode::Interpreter&) const;
|
||||||
String to_string() const;
|
String to_string(Bytecode::Executable const&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
33
Userland/Libraries/LibJS/Bytecode/StringTable.cpp
Normal file
33
Userland/Libraries/LibJS/Bytecode/StringTable.cpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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(StringView string)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_strings.size(); i++) {
|
||||||
|
if (m_strings[i] == string)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
m_strings.append(string);
|
||||||
|
return m_strings.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String 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
Userland/Libraries/LibJS/Bytecode/StringTable.h
Normal file
33
Userland/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/DistinctNumeric.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
|
||||||
|
namespace JS::Bytecode {
|
||||||
|
|
||||||
|
TYPEDEF_DISTINCT_NUMERIC_GENERAL(size_t, false, true, false, false, false, false, StringTableIndex);
|
||||||
|
|
||||||
|
class StringTable {
|
||||||
|
AK_MAKE_NONMOVABLE(StringTable);
|
||||||
|
AK_MAKE_NONCOPYABLE(StringTable);
|
||||||
|
|
||||||
|
public:
|
||||||
|
StringTable() = default;
|
||||||
|
|
||||||
|
StringTableIndex insert(StringView string);
|
||||||
|
String const& get(StringTableIndex) const;
|
||||||
|
void dump() const;
|
||||||
|
bool is_empty() const { return m_strings.is_empty(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<String> m_strings;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ set(SOURCES
|
||||||
Bytecode/Instruction.cpp
|
Bytecode/Instruction.cpp
|
||||||
Bytecode/Interpreter.cpp
|
Bytecode/Interpreter.cpp
|
||||||
Bytecode/Op.cpp
|
Bytecode/Op.cpp
|
||||||
|
Bytecode/StringTable.cpp
|
||||||
Console.cpp
|
Console.cpp
|
||||||
Heap/CellAllocator.cpp
|
Heap/CellAllocator.cpp
|
||||||
Heap/BlockAllocator.cpp
|
Heap/BlockAllocator.cpp
|
||||||
|
|
|
@ -166,6 +166,7 @@ class Handle;
|
||||||
|
|
||||||
namespace Bytecode {
|
namespace Bytecode {
|
||||||
class BasicBlock;
|
class BasicBlock;
|
||||||
|
struct Executable;
|
||||||
class Generator;
|
class Generator;
|
||||||
class Instruction;
|
class Instruction;
|
||||||
class Interpreter;
|
class Interpreter;
|
||||||
|
|
|
@ -156,7 +156,7 @@ Value ScriptFunction::execute_function_body()
|
||||||
if constexpr (JS_BYTECODE_DEBUG) {
|
if constexpr (JS_BYTECODE_DEBUG) {
|
||||||
dbgln("Compiled Bytecode::Block for function '{}':", m_name);
|
dbgln("Compiled Bytecode::Block for function '{}':", m_name);
|
||||||
for (auto& block : m_bytecode_executable->basic_blocks)
|
for (auto& block : m_bytecode_executable->basic_blocks)
|
||||||
block.dump();
|
block.dump(*m_bytecode_executable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bytecode_interpreter->run(*m_bytecode_executable);
|
return bytecode_interpreter->run(*m_bytecode_executable);
|
||||||
|
|
|
@ -516,7 +516,11 @@ static bool parse_and_run(JS::Interpreter& interpreter, const StringView& source
|
||||||
auto unit = JS::Bytecode::Generator::generate(*program);
|
auto unit = JS::Bytecode::Generator::generate(*program);
|
||||||
if (s_dump_bytecode) {
|
if (s_dump_bytecode) {
|
||||||
for (auto& block : unit.basic_blocks)
|
for (auto& block : unit.basic_blocks)
|
||||||
block.dump();
|
block.dump(unit);
|
||||||
|
if (!unit.string_table->is_empty()) {
|
||||||
|
outln();
|
||||||
|
unit.string_table->dump();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s_run_bytecode) {
|
if (s_run_bytecode) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue