mirror of
https://github.com/RGBCube/serenity
synced 2025-05-23 15:55:08 +00:00
LibJS: Support object rest elements in the bytecode interpreter
This commit is contained in:
parent
57b9a228ab
commit
25baefdd1e
4 changed files with 107 additions and 3 deletions
|
@ -643,20 +643,51 @@ static void generate_binding_pattern_bytecode(Bytecode::Generator& generator, Bi
|
||||||
|
|
||||||
static void generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Register const& value_reg)
|
static void generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Register const& value_reg)
|
||||||
{
|
{
|
||||||
|
Vector<Bytecode::Register> excluded_property_names;
|
||||||
|
auto has_rest = false;
|
||||||
|
if (pattern.entries.size() > 0)
|
||||||
|
has_rest = pattern.entries[pattern.entries.size() - 1].is_rest;
|
||||||
|
|
||||||
for (auto& [name, alias, initializer, is_rest] : pattern.entries) {
|
for (auto& [name, alias, initializer, is_rest] : pattern.entries) {
|
||||||
if (is_rest)
|
if (is_rest) {
|
||||||
TODO();
|
VERIFY(name.has<NonnullRefPtr<Identifier>>());
|
||||||
|
VERIFY(alias.has<Empty>());
|
||||||
|
VERIFY(!initializer);
|
||||||
|
|
||||||
|
auto identifier = name.get<NonnullRefPtr<Identifier>>()->string();
|
||||||
|
auto interned_identifier = generator.intern_string(identifier);
|
||||||
|
|
||||||
|
generator.emit_with_extra_register_slots<Bytecode::Op::CopyObjectExcludingProperties>(excluded_property_names.size(), value_reg, excluded_property_names);
|
||||||
|
generator.emit<Bytecode::Op::SetVariable>(interned_identifier);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Bytecode::StringTableIndex name_index;
|
Bytecode::StringTableIndex name_index;
|
||||||
|
|
||||||
if (name.has<NonnullRefPtr<Identifier>>()) {
|
if (name.has<NonnullRefPtr<Identifier>>()) {
|
||||||
auto identifier = name.get<NonnullRefPtr<Identifier>>()->string();
|
auto identifier = name.get<NonnullRefPtr<Identifier>>()->string();
|
||||||
name_index = generator.intern_string(identifier);
|
name_index = generator.intern_string(identifier);
|
||||||
|
|
||||||
|
if (has_rest) {
|
||||||
|
auto excluded_name_reg = generator.allocate_register();
|
||||||
|
excluded_property_names.append(excluded_name_reg);
|
||||||
|
generator.emit<Bytecode::Op::NewString>(name_index);
|
||||||
|
generator.emit<Bytecode::Op::Store>(excluded_name_reg);
|
||||||
|
}
|
||||||
|
|
||||||
generator.emit<Bytecode::Op::Load>(value_reg);
|
generator.emit<Bytecode::Op::Load>(value_reg);
|
||||||
generator.emit<Bytecode::Op::GetById>(name_index);
|
generator.emit<Bytecode::Op::GetById>(name_index);
|
||||||
} else {
|
} else {
|
||||||
auto expression = name.get<NonnullRefPtr<Expression>>();
|
auto expression = name.get<NonnullRefPtr<Expression>>();
|
||||||
expression->generate_bytecode(generator);
|
expression->generate_bytecode(generator);
|
||||||
|
|
||||||
|
if (has_rest) {
|
||||||
|
auto excluded_name_reg = generator.allocate_register();
|
||||||
|
excluded_property_names.append(excluded_name_reg);
|
||||||
|
generator.emit<Bytecode::Op::Store>(excluded_name_reg);
|
||||||
|
}
|
||||||
|
|
||||||
generator.emit<Bytecode::Op::GetByValue>(value_reg);
|
generator.emit<Bytecode::Op::GetByValue>(value_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
O(IteratorToArray) \
|
O(IteratorToArray) \
|
||||||
O(NewString) \
|
O(NewString) \
|
||||||
O(NewObject) \
|
O(NewObject) \
|
||||||
|
O(CopyObjectExcludingProperties) \
|
||||||
O(GetVariable) \
|
O(GetVariable) \
|
||||||
O(SetVariable) \
|
O(SetVariable) \
|
||||||
O(PutById) \
|
O(PutById) \
|
||||||
|
@ -103,5 +104,4 @@ protected:
|
||||||
private:
|
private:
|
||||||
Type m_type {};
|
Type m_type {};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/HashTable.h>
|
||||||
#include <LibJS/AST.h>
|
#include <LibJS/AST.h>
|
||||||
#include <LibJS/Bytecode/Interpreter.h>
|
#include <LibJS/Bytecode/Interpreter.h>
|
||||||
#include <LibJS/Bytecode/Op.h>
|
#include <LibJS/Bytecode/Op.h>
|
||||||
|
@ -168,6 +169,36 @@ void NewObject::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
interpreter.accumulator() = Object::create(interpreter.global_object(), interpreter.global_object().object_prototype());
|
interpreter.accumulator() = Object::create(interpreter.global_object(), interpreter.global_object().object_prototype());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CopyObjectExcludingProperties::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
auto* from_object = interpreter.reg(m_from_object).to_object(interpreter.global_object());
|
||||||
|
if (interpreter.vm().exception())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto* to_object = Object::create(interpreter.global_object(), interpreter.global_object().object_prototype());
|
||||||
|
|
||||||
|
HashTable<Value, ValueTraits> excluded_names;
|
||||||
|
for (size_t i = 0; i < m_excluded_names_count; ++i) {
|
||||||
|
excluded_names.set(interpreter.reg(m_excluded_names[i]));
|
||||||
|
if (interpreter.vm().exception())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto own_keys = from_object->get_own_properties(Object::PropertyKind::Key, true);
|
||||||
|
|
||||||
|
for (auto& key : own_keys) {
|
||||||
|
if (!excluded_names.contains(key)) {
|
||||||
|
auto property_name = PropertyName(key.to_property_key(interpreter.global_object()));
|
||||||
|
auto property_value = from_object->get(property_name);
|
||||||
|
if (interpreter.vm().exception())
|
||||||
|
return;
|
||||||
|
to_object->define_property(property_name, property_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interpreter.accumulator() = to_object;
|
||||||
|
}
|
||||||
|
|
||||||
void ConcatString::execute_impl(Bytecode::Interpreter& interpreter) const
|
void ConcatString::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.reg(m_lhs) = add(interpreter.global_object(), interpreter.reg(m_lhs), interpreter.accumulator());
|
interpreter.reg(m_lhs) = add(interpreter.global_object(), interpreter.reg(m_lhs), interpreter.accumulator());
|
||||||
|
@ -467,6 +498,22 @@ String NewObject::to_string_impl(Bytecode::Executable const&) const
|
||||||
return "NewObject";
|
return "NewObject";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String CopyObjectExcludingProperties::to_string_impl(const Bytecode::Executable&) const
|
||||||
|
{
|
||||||
|
StringBuilder builder;
|
||||||
|
builder.appendff("CopyObjectExcludingProperties from:{}", m_from_object);
|
||||||
|
if (m_excluded_names_count != 0) {
|
||||||
|
builder.append(" excluding:[");
|
||||||
|
for (size_t i = 0; i < m_excluded_names_count; ++i) {
|
||||||
|
builder.appendff("{}", m_excluded_names[i]);
|
||||||
|
if (i != m_excluded_names_count - 1)
|
||||||
|
builder.append(',');
|
||||||
|
}
|
||||||
|
builder.append(']');
|
||||||
|
}
|
||||||
|
return builder.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
String ConcatString::to_string_impl(Bytecode::Executable const&) const
|
String ConcatString::to_string_impl(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return String::formatted("ConcatString {}", m_lhs);
|
return String::formatted("ConcatString {}", m_lhs);
|
||||||
|
|
|
@ -162,6 +162,30 @@ public:
|
||||||
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NOTE: This instruction is variable-width depending on the number of excluded names
|
||||||
|
class CopyObjectExcludingProperties final : public Instruction {
|
||||||
|
public:
|
||||||
|
CopyObjectExcludingProperties(Register from_object, Vector<Register> const& excluded_names)
|
||||||
|
: Instruction(Type::CopyObjectExcludingProperties)
|
||||||
|
, m_from_object(from_object)
|
||||||
|
, m_excluded_names_count(excluded_names.size())
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_excluded_names_count; i++)
|
||||||
|
m_excluded_names[i] = excluded_names[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute_impl(Bytecode::Interpreter&) const;
|
||||||
|
String to_string_impl(Bytecode::Executable const&) const;
|
||||||
|
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||||
|
|
||||||
|
size_t length_impl() const { return sizeof(*this) + sizeof(Register) * m_excluded_names_count; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Register m_from_object;
|
||||||
|
size_t m_excluded_names_count { 0 };
|
||||||
|
Register m_excluded_names[];
|
||||||
|
};
|
||||||
|
|
||||||
class NewBigInt final : public Instruction {
|
class NewBigInt final : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit NewBigInt(Crypto::SignedBigInteger bigint)
|
explicit NewBigInt(Crypto::SignedBigInteger bigint)
|
||||||
|
@ -713,6 +737,8 @@ ALWAYS_INLINE size_t Instruction::length() const
|
||||||
return static_cast<Op::Call const&>(*this).length_impl();
|
return static_cast<Op::Call const&>(*this).length_impl();
|
||||||
else if (type() == Type::NewArray)
|
else if (type() == Type::NewArray)
|
||||||
return static_cast<Op::NewArray const&>(*this).length_impl();
|
return static_cast<Op::NewArray const&>(*this).length_impl();
|
||||||
|
else if (type() == Type::CopyObjectExcludingProperties)
|
||||||
|
return static_cast<Op::CopyObjectExcludingProperties const&>(*this).length_impl();
|
||||||
|
|
||||||
#define __BYTECODE_OP(op) \
|
#define __BYTECODE_OP(op) \
|
||||||
case Type::op: \
|
case Type::op: \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue