mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 18:17:44 +00:00
LibJS: Support array rest elements in the bytecode interpreter
This commit is contained in:
parent
7983324639
commit
57b9a228ab
4 changed files with 172 additions and 77 deletions
|
@ -729,11 +729,61 @@ static void generate_array_binding_pattern_bytecode(Bytecode::Generator& generat
|
||||||
|
|
||||||
auto temp_iterator_result_reg = generator.allocate_register();
|
auto temp_iterator_result_reg = generator.allocate_register();
|
||||||
|
|
||||||
|
auto assign_accumulator_to_alias = [&](auto& alias) {
|
||||||
|
alias.visit(
|
||||||
|
[&](Empty) {
|
||||||
|
// This element is an elision
|
||||||
|
},
|
||||||
|
[&](NonnullRefPtr<Identifier> const& identifier) {
|
||||||
|
auto interned_index = generator.intern_string(identifier->string());
|
||||||
|
generator.emit<Bytecode::Op::SetVariable>(interned_index);
|
||||||
|
},
|
||||||
|
[&](NonnullRefPtr<BindingPattern> const& pattern) {
|
||||||
|
// Store the accumulator value in a permanent register
|
||||||
|
auto target_reg = generator.allocate_register();
|
||||||
|
generator.emit<Bytecode::Op::Store>(target_reg);
|
||||||
|
generate_binding_pattern_bytecode(generator, pattern, target_reg);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
for (auto& [name, alias, initializer, is_rest] : pattern.entries) {
|
for (auto& [name, alias, initializer, is_rest] : pattern.entries) {
|
||||||
VERIFY(name.has<Empty>());
|
VERIFY(name.has<Empty>());
|
||||||
|
|
||||||
if (is_rest)
|
if (is_rest) {
|
||||||
TODO();
|
if (first) {
|
||||||
|
// The iterator has not been called, and is thus known to be not exhausted
|
||||||
|
generator.emit<Bytecode::Op::Load>(iterator_reg);
|
||||||
|
generator.emit<Bytecode::Op::IteratorToArray>();
|
||||||
|
} else {
|
||||||
|
auto& if_exhausted_block = generator.make_block();
|
||||||
|
auto& if_not_exhausted_block = generator.make_block();
|
||||||
|
auto& continuation_block = generator.make_block();
|
||||||
|
|
||||||
|
generator.emit<Bytecode::Op::Load>(is_iterator_exhausted_register);
|
||||||
|
generator.emit<Bytecode::Op::JumpConditional>().set_targets(
|
||||||
|
Bytecode::Label { if_exhausted_block },
|
||||||
|
Bytecode::Label { if_not_exhausted_block });
|
||||||
|
|
||||||
|
generator.switch_to_basic_block(if_exhausted_block);
|
||||||
|
generator.emit<Bytecode::Op::NewArray>();
|
||||||
|
generator.emit<Bytecode::Op::Jump>().set_targets(
|
||||||
|
Bytecode::Label { continuation_block },
|
||||||
|
{});
|
||||||
|
|
||||||
|
generator.switch_to_basic_block(if_not_exhausted_block);
|
||||||
|
generator.emit<Bytecode::Op::Load>(iterator_reg);
|
||||||
|
generator.emit<Bytecode::Op::IteratorToArray>();
|
||||||
|
generator.emit<Bytecode::Op::Jump>().set_targets(
|
||||||
|
Bytecode::Label { continuation_block },
|
||||||
|
{});
|
||||||
|
|
||||||
|
generator.switch_to_basic_block(continuation_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
assign_accumulator_to_alias(alias);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// In the first iteration of the loop, a few things are true which can save
|
// In the first iteration of the loop, a few things are true which can save
|
||||||
// us some bytecode:
|
// us some bytecode:
|
||||||
|
@ -790,20 +840,7 @@ static void generate_array_binding_pattern_bytecode(Bytecode::Generator& generat
|
||||||
// pattern if necessary.
|
// pattern if necessary.
|
||||||
generator.switch_to_basic_block(create_binding_block);
|
generator.switch_to_basic_block(create_binding_block);
|
||||||
|
|
||||||
alias.visit(
|
assign_accumulator_to_alias(alias);
|
||||||
[&](Empty) {
|
|
||||||
// This element is an elision
|
|
||||||
},
|
|
||||||
[&](NonnullRefPtr<Identifier> const& identifier) {
|
|
||||||
auto interned_index = generator.intern_string(identifier->string());
|
|
||||||
generator.emit<Bytecode::Op::SetVariable>(interned_index);
|
|
||||||
},
|
|
||||||
[&](NonnullRefPtr<BindingPattern> const& pattern) {
|
|
||||||
// Store the accumulator value in a permanent register
|
|
||||||
auto target_reg = generator.allocate_register();
|
|
||||||
generator.emit<Bytecode::Op::Store>(target_reg);
|
|
||||||
generate_binding_pattern_bytecode(generator, pattern, target_reg);
|
|
||||||
});
|
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,67 +9,68 @@
|
||||||
#include <AK/Forward.h>
|
#include <AK/Forward.h>
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
|
|
||||||
#define ENUMERATE_BYTECODE_OPS(O) \
|
#define ENUMERATE_BYTECODE_OPS(O) \
|
||||||
O(Load) \
|
O(Load) \
|
||||||
O(LoadImmediate) \
|
O(LoadImmediate) \
|
||||||
O(Store) \
|
O(Store) \
|
||||||
O(Add) \
|
O(Add) \
|
||||||
O(Sub) \
|
O(Sub) \
|
||||||
O(Mul) \
|
O(Mul) \
|
||||||
O(Div) \
|
O(Div) \
|
||||||
O(Mod) \
|
O(Mod) \
|
||||||
O(Exp) \
|
O(Exp) \
|
||||||
O(GreaterThan) \
|
O(GreaterThan) \
|
||||||
O(GreaterThanEquals) \
|
O(GreaterThanEquals) \
|
||||||
O(LessThan) \
|
O(LessThan) \
|
||||||
O(LessThanEquals) \
|
O(LessThanEquals) \
|
||||||
O(AbstractInequals) \
|
O(AbstractInequals) \
|
||||||
O(AbstractEquals) \
|
O(AbstractEquals) \
|
||||||
O(TypedInequals) \
|
O(TypedInequals) \
|
||||||
O(TypedEquals) \
|
O(TypedEquals) \
|
||||||
O(NewBigInt) \
|
O(NewBigInt) \
|
||||||
O(NewArray) \
|
O(NewArray) \
|
||||||
O(NewString) \
|
O(IteratorToArray) \
|
||||||
O(NewObject) \
|
O(NewString) \
|
||||||
O(GetVariable) \
|
O(NewObject) \
|
||||||
O(SetVariable) \
|
O(GetVariable) \
|
||||||
O(PutById) \
|
O(SetVariable) \
|
||||||
O(GetById) \
|
O(PutById) \
|
||||||
O(PutByValue) \
|
O(GetById) \
|
||||||
O(GetByValue) \
|
O(PutByValue) \
|
||||||
O(Jump) \
|
O(GetByValue) \
|
||||||
O(JumpConditional) \
|
O(Jump) \
|
||||||
O(JumpNullish) \
|
O(JumpConditional) \
|
||||||
O(JumpUndefined) \
|
O(JumpNullish) \
|
||||||
O(Call) \
|
O(JumpUndefined) \
|
||||||
O(NewFunction) \
|
O(Call) \
|
||||||
O(Return) \
|
O(NewFunction) \
|
||||||
O(BitwiseAnd) \
|
O(Return) \
|
||||||
O(BitwiseOr) \
|
O(BitwiseAnd) \
|
||||||
O(BitwiseXor) \
|
O(BitwiseOr) \
|
||||||
O(BitwiseNot) \
|
O(BitwiseXor) \
|
||||||
O(Not) \
|
O(BitwiseNot) \
|
||||||
O(UnaryPlus) \
|
O(Not) \
|
||||||
O(UnaryMinus) \
|
O(UnaryPlus) \
|
||||||
O(Typeof) \
|
O(UnaryMinus) \
|
||||||
O(LeftShift) \
|
O(Typeof) \
|
||||||
O(RightShift) \
|
O(LeftShift) \
|
||||||
O(UnsignedRightShift) \
|
O(RightShift) \
|
||||||
O(In) \
|
O(UnsignedRightShift) \
|
||||||
O(InstanceOf) \
|
O(In) \
|
||||||
O(ConcatString) \
|
O(InstanceOf) \
|
||||||
O(Increment) \
|
O(ConcatString) \
|
||||||
O(Decrement) \
|
O(Increment) \
|
||||||
O(Throw) \
|
O(Decrement) \
|
||||||
O(PushLexicalEnvironment) \
|
O(Throw) \
|
||||||
O(LoadArgument) \
|
O(PushLexicalEnvironment) \
|
||||||
O(EnterUnwindContext) \
|
O(LoadArgument) \
|
||||||
O(LeaveUnwindContext) \
|
O(EnterUnwindContext) \
|
||||||
O(ContinuePendingUnwind) \
|
O(LeaveUnwindContext) \
|
||||||
O(Yield) \
|
O(ContinuePendingUnwind) \
|
||||||
O(GetIterator) \
|
O(Yield) \
|
||||||
O(IteratorNext) \
|
O(GetIterator) \
|
||||||
O(IteratorResultDone) \
|
O(IteratorNext) \
|
||||||
|
O(IteratorResultDone) \
|
||||||
O(IteratorResultValue)
|
O(IteratorResultValue)
|
||||||
|
|
||||||
namespace JS::Bytecode {
|
namespace JS::Bytecode {
|
||||||
|
|
|
@ -124,6 +124,40 @@ void NewArray::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
interpreter.accumulator() = Array::create_from(interpreter.global_object(), elements);
|
interpreter.accumulator() = Array::create_from(interpreter.global_object(), elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IteratorToArray::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
auto& global_object = interpreter.global_object();
|
||||||
|
auto& vm = interpreter.vm();
|
||||||
|
auto iterator = interpreter.accumulator().to_object(global_object);
|
||||||
|
if (vm.exception())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto array = Array::create(global_object);
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
auto iterator_result = iterator_next(*iterator);
|
||||||
|
if (!iterator_result)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto complete = iterator_complete(global_object, *iterator_result);
|
||||||
|
if (vm.exception())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (complete) {
|
||||||
|
interpreter.accumulator() = array;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto value = iterator_value(global_object, *iterator_result);
|
||||||
|
if (vm.exception())
|
||||||
|
return;
|
||||||
|
|
||||||
|
array->put(index, value);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NewString::execute_impl(Bytecode::Interpreter& interpreter) const
|
void NewString::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.accumulator() = js_string(interpreter.vm(), interpreter.current_executable().get_string(m_string));
|
interpreter.accumulator() = js_string(interpreter.vm(), interpreter.current_executable().get_string(m_string));
|
||||||
|
@ -418,6 +452,11 @@ String NewArray::to_string_impl(Bytecode::Executable const&) const
|
||||||
return builder.to_string();
|
return builder.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String IteratorToArray::to_string_impl(const Bytecode::Executable&) const
|
||||||
|
{
|
||||||
|
return "IteratorToArray";
|
||||||
|
}
|
||||||
|
|
||||||
String NewString::to_string_impl(Bytecode::Executable const& executable) const
|
String NewString::to_string_impl(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
return String::formatted("NewString {} (\"{}\")", m_string, executable.string_table->get(m_string));
|
return String::formatted("NewString {} (\"{}\")", m_string, executable.string_table->get(m_string));
|
||||||
|
|
|
@ -181,6 +181,12 @@ private:
|
||||||
// NOTE: This instruction is variable-width depending on the number of elements!
|
// NOTE: This instruction is variable-width depending on the number of elements!
|
||||||
class NewArray final : public Instruction {
|
class NewArray final : public Instruction {
|
||||||
public:
|
public:
|
||||||
|
NewArray()
|
||||||
|
: Instruction(Type::NewArray)
|
||||||
|
, m_element_count(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
explicit NewArray(Vector<Register> const& elements)
|
explicit NewArray(Vector<Register> const& elements)
|
||||||
: Instruction(Type::NewArray)
|
: Instruction(Type::NewArray)
|
||||||
, m_element_count(elements.size())
|
, m_element_count(elements.size())
|
||||||
|
@ -203,6 +209,18 @@ private:
|
||||||
Register m_elements[];
|
Register m_elements[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IteratorToArray final : public Instruction {
|
||||||
|
public:
|
||||||
|
IteratorToArray()
|
||||||
|
: Instruction(Type::IteratorToArray)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute_impl(Bytecode::Interpreter&) const;
|
||||||
|
String to_string_impl(Bytecode::Executable const&) const;
|
||||||
|
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||||
|
};
|
||||||
|
|
||||||
class ConcatString final : public Instruction {
|
class ConcatString final : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit ConcatString(Register lhs)
|
explicit ConcatString(Register lhs)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue