mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 19:05:07 +00:00
LibJS: Add support for SpreadExpressions in array literals for bytecode
For this it adds another opcode `Append $lhs` which appends the accumulator to the Array in $lhs, optionally spreading it.
This commit is contained in:
parent
4235b2020f
commit
ae52ae8f9f
4 changed files with 109 additions and 27 deletions
|
@ -7,6 +7,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/Find.h>
|
||||||
#include <AK/Format.h>
|
#include <AK/Format.h>
|
||||||
#include <LibJS/AST.h>
|
#include <LibJS/AST.h>
|
||||||
#include <LibJS/Bytecode/Generator.h>
|
#include <LibJS/Bytecode/Generator.h>
|
||||||
|
@ -1085,34 +1086,50 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco
|
||||||
|
|
||||||
Bytecode::CodeGenerationErrorOr<void> ArrayExpression::generate_bytecode(Bytecode::Generator& generator) const
|
Bytecode::CodeGenerationErrorOr<void> ArrayExpression::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
{
|
{
|
||||||
Vector<Bytecode::Register> element_regs;
|
if (m_elements.is_empty()) {
|
||||||
for (auto& element : m_elements) {
|
|
||||||
if (element && is<SpreadExpression>(*element)) {
|
|
||||||
return Bytecode::CodeGenerationError {
|
|
||||||
this,
|
|
||||||
"Unimplemented element kind: SpreadExpression"sv,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
element_regs.append(generator.allocate_register());
|
|
||||||
}
|
|
||||||
size_t i = 0;
|
|
||||||
for (auto& element : m_elements) {
|
|
||||||
if (element) {
|
|
||||||
TRY(element->generate_bytecode(generator));
|
|
||||||
|
|
||||||
if (is<SpreadExpression>(*element))
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
} else {
|
|
||||||
generator.emit<Bytecode::Op::LoadImmediate>(Value {});
|
|
||||||
}
|
|
||||||
auto& element_reg = element_regs[i++];
|
|
||||||
generator.emit<Bytecode::Op::Store>(element_reg);
|
|
||||||
}
|
|
||||||
if (element_regs.is_empty()) {
|
|
||||||
generator.emit<Bytecode::Op::NewArray>();
|
generator.emit<Bytecode::Op::NewArray>();
|
||||||
} else {
|
return {};
|
||||||
generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2u, AK::Array { element_regs.first(), element_regs.last() });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto first_spread = find_if(m_elements.begin(), m_elements.end(), [](auto el) { return el && is<SpreadExpression>(*el); });
|
||||||
|
|
||||||
|
Bytecode::Register args_start_reg { 0 };
|
||||||
|
for (auto it = m_elements.begin(); it != first_spread; ++it) {
|
||||||
|
auto reg = generator.allocate_register();
|
||||||
|
if (args_start_reg.index() == 0)
|
||||||
|
args_start_reg = reg;
|
||||||
|
}
|
||||||
|
u32 i = 0;
|
||||||
|
for (auto it = m_elements.begin(); it != first_spread; ++it, ++i) {
|
||||||
|
Bytecode::Register reg { args_start_reg.index() + i };
|
||||||
|
if (!*it)
|
||||||
|
generator.emit<Bytecode::Op::LoadImmediate>(Value {});
|
||||||
|
else {
|
||||||
|
TRY((*it)->generate_bytecode(generator));
|
||||||
|
}
|
||||||
|
generator.emit<Bytecode::Op::Store>(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first_spread.index() != 0)
|
||||||
|
generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2u, AK::Array { args_start_reg, Bytecode::Register { args_start_reg.index() + static_cast<u32>(first_spread.index() - 1) } });
|
||||||
|
else
|
||||||
|
generator.emit<Bytecode::Op::NewArray>();
|
||||||
|
|
||||||
|
if (first_spread != m_elements.end()) {
|
||||||
|
auto array_reg = generator.allocate_register();
|
||||||
|
generator.emit<Bytecode::Op::Store>(array_reg);
|
||||||
|
for (auto it = first_spread; it != m_elements.end(); ++it) {
|
||||||
|
if (!*it) {
|
||||||
|
generator.emit<Bytecode::Op::LoadImmediate>(Value {});
|
||||||
|
generator.emit<Bytecode::Op::Append>(array_reg, false);
|
||||||
|
} else {
|
||||||
|
TRY((*it)->generate_bytecode(generator));
|
||||||
|
generator.emit<Bytecode::Op::Append>(array_reg, *it && is<SpreadExpression>(**it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generator.emit<Bytecode::Op::Load>(array_reg);
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2389,5 +2406,4 @@ Bytecode::CodeGenerationErrorOr<void> MetaProperty::generate_bytecode(Bytecode::
|
||||||
|
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#define ENUMERATE_BYTECODE_OPS(O) \
|
#define ENUMERATE_BYTECODE_OPS(O) \
|
||||||
O(Add) \
|
O(Add) \
|
||||||
|
O(Append) \
|
||||||
O(BitwiseAnd) \
|
O(BitwiseAnd) \
|
||||||
O(BitwiseNot) \
|
O(BitwiseNot) \
|
||||||
O(BitwiseOr) \
|
O(BitwiseOr) \
|
||||||
|
|
|
@ -182,6 +182,46 @@ ThrowCompletionOr<void> NewArray::execute_impl(Bytecode::Interpreter& interprete
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> Append::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
// Note: This OpCode is used to construct array literals containing at least one spread element,
|
||||||
|
// Iterating over such a spread element to unpack it has to be visible by
|
||||||
|
// the user courtesy of
|
||||||
|
// https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation
|
||||||
|
// SpreadElement : ... AssignmentExpression
|
||||||
|
// 1. Let spreadRef be ? Evaluation of AssignmentExpression.
|
||||||
|
// 2. Let spreadObj be ? GetValue(spreadRef).
|
||||||
|
// 3. Let iteratorRecord be ? GetIterator(spreadObj).
|
||||||
|
// 4. Repeat,
|
||||||
|
// a. Let next be ? IteratorStep(iteratorRecord).
|
||||||
|
// b. If next is false, return nextIndex.
|
||||||
|
// c. Let nextValue be ? IteratorValue(next).
|
||||||
|
// d. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(nextIndex)), nextValue).
|
||||||
|
// e. Set nextIndex to nextIndex + 1.
|
||||||
|
|
||||||
|
auto& vm = interpreter.vm();
|
||||||
|
|
||||||
|
// Note: We know from codegen, that lhs is a plain array with only indexed properties
|
||||||
|
auto& lhs = interpreter.reg(m_lhs).as_array();
|
||||||
|
auto lhs_size = lhs.indexed_properties().array_like_size();
|
||||||
|
|
||||||
|
auto rhs = interpreter.accumulator();
|
||||||
|
|
||||||
|
if (m_is_spread) {
|
||||||
|
// ...rhs
|
||||||
|
size_t i = lhs_size;
|
||||||
|
TRY(get_iterator_values(vm, rhs, [&i, &lhs](Value iterator_value) -> Optional<Completion> {
|
||||||
|
lhs.indexed_properties().put(i, iterator_value, default_attributes);
|
||||||
|
++i;
|
||||||
|
return {};
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
lhs.indexed_properties().put(lhs_size, rhs, default_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Since the accumulator is a Value, we store an object there and have to convert back and forth between that an Iterator records. Not great.
|
// FIXME: Since the accumulator is a Value, we store an object there and have to convert back and forth between that an Iterator records. Not great.
|
||||||
// Make sure to put this into the accumulator before the iterator object disappears from the stack to prevent the members from being GC'd.
|
// Make sure to put this into the accumulator before the iterator object disappears from the stack to prevent the members from being GC'd.
|
||||||
static Object* iterator_to_object(VM& vm, Iterator iterator)
|
static Object* iterator_to_object(VM& vm, Iterator iterator)
|
||||||
|
@ -943,6 +983,13 @@ String NewArray::to_string_impl(Bytecode::Executable const&) const
|
||||||
return builder.to_string();
|
return builder.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String Append::to_string_impl(Bytecode::Executable const&) const
|
||||||
|
{
|
||||||
|
if (m_is_spread)
|
||||||
|
return String::formatted("Append lhs: **{}", m_lhs);
|
||||||
|
return String::formatted("Append lhs: {}", m_lhs);
|
||||||
|
}
|
||||||
|
|
||||||
String IteratorToArray::to_string_impl(Bytecode::Executable const&) const
|
String IteratorToArray::to_string_impl(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return "IteratorToArray";
|
return "IteratorToArray";
|
||||||
|
|
|
@ -254,6 +254,24 @@ private:
|
||||||
Register m_elements[];
|
Register m_elements[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Append final : public Instruction {
|
||||||
|
public:
|
||||||
|
Append(Register lhs, bool is_spread)
|
||||||
|
: Instruction(Type::Append)
|
||||||
|
, m_lhs(lhs)
|
||||||
|
, m_is_spread(is_spread)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||||
|
String to_string_impl(Bytecode::Executable const&) const;
|
||||||
|
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Register m_lhs;
|
||||||
|
bool m_is_spread = false;
|
||||||
|
};
|
||||||
|
|
||||||
class IteratorToArray final : public Instruction {
|
class IteratorToArray final : public Instruction {
|
||||||
public:
|
public:
|
||||||
IteratorToArray()
|
IteratorToArray()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue