mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 10:37:41 +00:00
LibJS: Implement bytecode generation for all ObjectExpression properties
This commit is contained in:
parent
698fb3957a
commit
007ffcd763
3 changed files with 94 additions and 17 deletions
|
@ -817,25 +817,42 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco
|
||||||
generator.emit<Bytecode::Op::Store>(object_reg);
|
generator.emit<Bytecode::Op::Store>(object_reg);
|
||||||
|
|
||||||
for (auto& property : m_properties) {
|
for (auto& property : m_properties) {
|
||||||
if (property.type() != ObjectProperty::Type::KeyValue)
|
Bytecode::Op::PropertyKind property_kind;
|
||||||
return Bytecode::CodeGenerationError {
|
switch (property.type()) {
|
||||||
this,
|
case ObjectProperty::Type::KeyValue:
|
||||||
"Unimplemented property kind"sv,
|
property_kind = Bytecode::Op::PropertyKind::KeyValue;
|
||||||
};
|
break;
|
||||||
|
case ObjectProperty::Type::Getter:
|
||||||
|
property_kind = Bytecode::Op::PropertyKind::Getter;
|
||||||
|
break;
|
||||||
|
case ObjectProperty::Type::Setter:
|
||||||
|
property_kind = Bytecode::Op::PropertyKind::Setter;
|
||||||
|
break;
|
||||||
|
case ObjectProperty::Type::Spread:
|
||||||
|
property_kind = Bytecode::Op::PropertyKind::Spread;
|
||||||
|
break;
|
||||||
|
case ObjectProperty::Type::ProtoSetter:
|
||||||
|
property_kind = Bytecode::Op::PropertyKind::ProtoSetter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (is<StringLiteral>(property.key())) {
|
if (is<StringLiteral>(property.key())) {
|
||||||
auto& string_literal = static_cast<StringLiteral const&>(property.key());
|
auto& string_literal = static_cast<StringLiteral const&>(property.key());
|
||||||
Bytecode::IdentifierTableIndex key_name = generator.intern_identifier(string_literal.value());
|
Bytecode::IdentifierTableIndex key_name = generator.intern_identifier(string_literal.value());
|
||||||
|
|
||||||
TRY(property.value().generate_bytecode(generator));
|
if (property_kind != Bytecode::Op::PropertyKind::Spread)
|
||||||
generator.emit<Bytecode::Op::PutById>(object_reg, key_name);
|
TRY(property.value().generate_bytecode(generator));
|
||||||
|
|
||||||
|
generator.emit<Bytecode::Op::PutById>(object_reg, key_name, property_kind);
|
||||||
} else {
|
} else {
|
||||||
TRY(property.key().generate_bytecode(generator));
|
TRY(property.key().generate_bytecode(generator));
|
||||||
auto property_reg = generator.allocate_register();
|
auto property_reg = generator.allocate_register();
|
||||||
generator.emit<Bytecode::Op::Store>(property_reg);
|
generator.emit<Bytecode::Op::Store>(property_reg);
|
||||||
|
|
||||||
TRY(property.value().generate_bytecode(generator));
|
if (property_kind != Bytecode::Op::PropertyKind::Spread)
|
||||||
generator.emit<Bytecode::Op::PutByValue>(object_reg, property_reg);
|
TRY(property.value().generate_bytecode(generator));
|
||||||
|
|
||||||
|
generator.emit<Bytecode::Op::PutByValue>(object_reg, property_reg, property_kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,42 @@ String Instruction::to_string(Bytecode::Executable const& executable) const
|
||||||
|
|
||||||
namespace JS::Bytecode::Op {
|
namespace JS::Bytecode::Op {
|
||||||
|
|
||||||
|
static ThrowCompletionOr<void> put_by_property_key(Object* object, Value value, PropertyKey name, Bytecode::Interpreter& interpreter, PropertyKind kind)
|
||||||
|
{
|
||||||
|
if (kind == PropertyKind::Getter || kind == PropertyKind::Setter) {
|
||||||
|
// The generator should only pass us functions for getters and setters.
|
||||||
|
VERIFY(value.is_function());
|
||||||
|
}
|
||||||
|
switch (kind) {
|
||||||
|
case PropertyKind::Getter: {
|
||||||
|
auto& function = value.as_function();
|
||||||
|
if (function.name().is_empty() && is<ECMAScriptFunctionObject>(function))
|
||||||
|
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(String::formatted("get {}", name));
|
||||||
|
object->define_direct_accessor(name, &function, nullptr, Attribute::Configurable | Attribute::Enumerable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PropertyKind::Setter: {
|
||||||
|
auto& function = value.as_function();
|
||||||
|
if (function.name().is_empty() && is<ECMAScriptFunctionObject>(function))
|
||||||
|
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(String::formatted("set {}", name));
|
||||||
|
object->define_direct_accessor(name, nullptr, &function, Attribute::Configurable | Attribute::Enumerable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PropertyKind::KeyValue:
|
||||||
|
TRY(object->set(name, interpreter.accumulator(), Object::ShouldThrowExceptions::Yes));
|
||||||
|
break;
|
||||||
|
case PropertyKind::Spread:
|
||||||
|
TRY(object->copy_data_properties(value, {}, interpreter.global_object()));
|
||||||
|
break;
|
||||||
|
case PropertyKind::ProtoSetter:
|
||||||
|
if (value.is_object() || value.is_null())
|
||||||
|
MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> Load::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> Load::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.accumulator() = interpreter.reg(m_src);
|
interpreter.accumulator() = interpreter.reg(m_src);
|
||||||
|
@ -350,8 +386,9 @@ ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter
|
||||||
ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto* object = TRY(interpreter.reg(m_base).to_object(interpreter.global_object()));
|
auto* object = TRY(interpreter.reg(m_base).to_object(interpreter.global_object()));
|
||||||
TRY(object->set(interpreter.current_executable().get_identifier(m_property), interpreter.accumulator(), Object::ShouldThrowExceptions::Yes));
|
PropertyKey name = interpreter.current_executable().get_identifier(m_property);
|
||||||
return {};
|
auto value = interpreter.accumulator();
|
||||||
|
return put_by_property_key(object, value, name, interpreter, m_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
@ -592,8 +629,7 @@ ThrowCompletionOr<void> PutByValue::execute_impl(Bytecode::Interpreter& interpre
|
||||||
auto* object = TRY(interpreter.reg(m_base).to_object(interpreter.global_object()));
|
auto* object = TRY(interpreter.reg(m_base).to_object(interpreter.global_object()));
|
||||||
|
|
||||||
auto property_key = TRY(interpreter.reg(m_property).to_property_key(interpreter.global_object()));
|
auto property_key = TRY(interpreter.reg(m_property).to_property_key(interpreter.global_object()));
|
||||||
TRY(object->set(property_key, interpreter.accumulator(), Object::ShouldThrowExceptions::Yes));
|
return put_by_property_key(object, interpreter.accumulator(), property_key, interpreter, m_kind);
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
@ -839,7 +875,13 @@ String SetVariable::to_string_impl(Bytecode::Executable const& executable) const
|
||||||
|
|
||||||
String PutById::to_string_impl(Bytecode::Executable const& executable) const
|
String PutById::to_string_impl(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
return String::formatted("PutById base:{}, property:{} ({})", m_base, m_property, executable.identifier_table->get(m_property));
|
auto kind = m_kind == PropertyKind::Getter
|
||||||
|
? "getter"
|
||||||
|
: m_kind == PropertyKind::Setter
|
||||||
|
? "setter"
|
||||||
|
: "property";
|
||||||
|
|
||||||
|
return String::formatted("PutById kind:{} base:{}, property:{} ({})", kind, m_base, m_property, executable.identifier_table->get(m_property));
|
||||||
}
|
}
|
||||||
|
|
||||||
String GetById::to_string_impl(Bytecode::Executable const& executable) const
|
String GetById::to_string_impl(Bytecode::Executable const& executable) const
|
||||||
|
@ -985,7 +1027,13 @@ String GetByValue::to_string_impl(const Bytecode::Executable&) const
|
||||||
|
|
||||||
String PutByValue::to_string_impl(const Bytecode::Executable&) const
|
String PutByValue::to_string_impl(const Bytecode::Executable&) const
|
||||||
{
|
{
|
||||||
return String::formatted("PutByValue base:{}, property:{}", m_base, m_property);
|
auto kind = m_kind == PropertyKind::Getter
|
||||||
|
? "getter"
|
||||||
|
: m_kind == PropertyKind::Setter
|
||||||
|
? "setter"
|
||||||
|
: "property";
|
||||||
|
|
||||||
|
return String::formatted("PutByValue kind:{} base:{}, property:{}", kind, m_base, m_property);
|
||||||
}
|
}
|
||||||
|
|
||||||
String DeleteByValue::to_string_impl(Bytecode::Executable const&) const
|
String DeleteByValue::to_string_impl(Bytecode::Executable const&) const
|
||||||
|
|
|
@ -410,12 +410,21 @@ private:
|
||||||
IdentifierTableIndex m_property;
|
IdentifierTableIndex m_property;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class PropertyKind {
|
||||||
|
Getter,
|
||||||
|
Setter,
|
||||||
|
KeyValue,
|
||||||
|
Spread,
|
||||||
|
ProtoSetter,
|
||||||
|
};
|
||||||
|
|
||||||
class PutById final : public Instruction {
|
class PutById final : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit PutById(Register base, IdentifierTableIndex property)
|
explicit PutById(Register base, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue)
|
||||||
: Instruction(Type::PutById)
|
: Instruction(Type::PutById)
|
||||||
, m_base(base)
|
, m_base(base)
|
||||||
, m_property(property)
|
, m_property(property)
|
||||||
|
, m_kind(kind)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,6 +435,7 @@ public:
|
||||||
private:
|
private:
|
||||||
Register m_base;
|
Register m_base;
|
||||||
IdentifierTableIndex m_property;
|
IdentifierTableIndex m_property;
|
||||||
|
PropertyKind m_kind;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeleteById final : public Instruction {
|
class DeleteById final : public Instruction {
|
||||||
|
@ -462,10 +472,11 @@ private:
|
||||||
|
|
||||||
class PutByValue final : public Instruction {
|
class PutByValue final : public Instruction {
|
||||||
public:
|
public:
|
||||||
PutByValue(Register base, Register property)
|
PutByValue(Register base, Register property, PropertyKind kind = PropertyKind::KeyValue)
|
||||||
: Instruction(Type::PutByValue)
|
: Instruction(Type::PutByValue)
|
||||||
, m_base(base)
|
, m_base(base)
|
||||||
, m_property(property)
|
, m_property(property)
|
||||||
|
, m_kind(kind)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,6 +487,7 @@ public:
|
||||||
private:
|
private:
|
||||||
Register m_base;
|
Register m_base;
|
||||||
Register m_property;
|
Register m_property;
|
||||||
|
PropertyKind m_kind;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeleteByValue final : public Instruction {
|
class DeleteByValue final : public Instruction {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue