LibJS: Add PutBySpread instruction

This object property kind had completely different behaviour.
By adding an instruction for it, we can remove a bunch of special
casing, and avoid creating dummy `PropertyKey` values.
This commit is contained in:
Jonne Ransijn 2024-11-01 22:00:32 +01:00 committed by Andreas Kling
parent 27859c17b4
commit bc05f6303f
Notes: github-actions[bot] 2024-11-09 16:56:05 +00:00
4 changed files with 56 additions and 21 deletions

View file

@ -1132,12 +1132,12 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ObjectExpression::gener
case ObjectProperty::Type::Setter: case ObjectProperty::Type::Setter:
property_kind = Bytecode::Op::PropertyKind::Setter; property_kind = Bytecode::Op::PropertyKind::Setter;
break; break;
case ObjectProperty::Type::Spread:
property_kind = Bytecode::Op::PropertyKind::Spread;
break;
case ObjectProperty::Type::ProtoSetter: case ObjectProperty::Type::ProtoSetter:
property_kind = Bytecode::Op::PropertyKind::ProtoSetter; property_kind = Bytecode::Op::PropertyKind::ProtoSetter;
break; break;
case ObjectProperty::Type::Spread:
generator.emit<Bytecode::Op::PutBySpread>(object, TRY(property->key().generate_bytecode(generator)).value());
continue;
} }
if (is<StringLiteral>(property->key())) { if (is<StringLiteral>(property->key())) {
@ -1147,7 +1147,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ObjectExpression::gener
Optional<ScopedOperand> value; Optional<ScopedOperand> value;
if (property_kind == Bytecode::Op::PropertyKind::ProtoSetter) { if (property_kind == Bytecode::Op::PropertyKind::ProtoSetter) {
value = TRY(property->value().generate_bytecode(generator)).value(); value = TRY(property->value().generate_bytecode(generator)).value();
} else if (property_kind != Bytecode::Op::PropertyKind::Spread) { } else {
ByteString identifier = string_literal.value(); ByteString identifier = string_literal.value();
if (property_kind == Bytecode::Op::PropertyKind::Getter) if (property_kind == Bytecode::Op::PropertyKind::Getter)
identifier = ByteString::formatted("get {}", identifier); identifier = ByteString::formatted("get {}", identifier);
@ -1155,21 +1155,14 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ObjectExpression::gener
identifier = ByteString::formatted("set {}", identifier); identifier = ByteString::formatted("set {}", identifier);
auto name = generator.intern_identifier(identifier); auto name = generator.intern_identifier(identifier);
value = TRY(generator.emit_named_evaluation_if_anonymous_function(property->value(), name)).value(); value = TRY(generator.emit_named_evaluation_if_anonymous_function(property->value(), name)).value();
} else {
// Spread the key.
value = TRY(property->key().generate_bytecode(generator)).value();
} }
generator.emit<Bytecode::Op::PutById>(object, key_name, *value, property_kind, generator.next_property_lookup_cache()); generator.emit<Bytecode::Op::PutById>(object, key_name, *value, property_kind, generator.next_property_lookup_cache());
} else { } else {
auto property_name = TRY(property->key().generate_bytecode(generator)).value(); auto property_name = TRY(property->key().generate_bytecode(generator)).value();
Optional<ScopedOperand> value; auto value = TRY(property->value().generate_bytecode(generator)).value();
if (property_kind != Bytecode::Op::PropertyKind::Spread)
value = TRY(property->value().generate_bytecode(generator)).value();
else
value = property_name;
generator.emit<Bytecode::Op::PutByValue>(object, property_name, *value, property_kind); generator.emit<Bytecode::Op::PutByValue>(object, property_name, value, property_kind);
} }
} }

View file

@ -120,6 +120,7 @@
O(PostfixIncrement) \ O(PostfixIncrement) \
O(PutById) \ O(PutById) \
O(PutByIdWithThis) \ O(PutByIdWithThis) \
O(PutBySpread) \
O(PutByValue) \ O(PutByValue) \
O(PutByValueWithThis) \ O(PutByValueWithThis) \
O(PutPrivateById) \ O(PutPrivateById) \

View file

@ -649,6 +649,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
HANDLE_INSTRUCTION(PostfixIncrement); HANDLE_INSTRUCTION(PostfixIncrement);
HANDLE_INSTRUCTION(PutById); HANDLE_INSTRUCTION(PutById);
HANDLE_INSTRUCTION(PutByIdWithThis); HANDLE_INSTRUCTION(PutByIdWithThis);
HANDLE_INSTRUCTION(PutBySpread);
HANDLE_INSTRUCTION(PutByValue); HANDLE_INSTRUCTION(PutByValue);
HANDLE_INSTRUCTION(PutByValueWithThis); HANDLE_INSTRUCTION(PutByValueWithThis);
HANDLE_INSTRUCTION(PutPrivateById); HANDLE_INSTRUCTION(PutPrivateById);
@ -1213,9 +1214,6 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
case Op::PropertyKind::DirectKeyValue: case Op::PropertyKind::DirectKeyValue:
object->define_direct_property(name, value, Attribute::Enumerable | Attribute::Writable | Attribute::Configurable); object->define_direct_property(name, value, Attribute::Enumerable | Attribute::Writable | Attribute::Configurable);
break; break;
case Op::PropertyKind::Spread:
TRY(object->copy_data_properties(vm, value, {}));
break;
case Op::PropertyKind::ProtoSetter: case Op::PropertyKind::ProtoSetter:
if (value.is_object() || value.is_null()) if (value.is_object() || value.is_null())
MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr)); MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr));
@ -1362,7 +1360,7 @@ inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Depreca
} }
} }
auto property_key = kind != Op::PropertyKind::Spread ? TRY(property_key_value.to_property_key(vm)) : PropertyKey { 0 }; auto property_key = TRY(property_key_value.to_property_key(vm));
TRY(put_by_property_key(vm, base, base, value, base_identifier, property_key, kind)); TRY(put_by_property_key(vm, base, base, value, base_identifier, property_key, kind));
return {}; return {};
} }
@ -2417,6 +2415,20 @@ ThrowCompletionOr<void> HasPrivateId::execute_impl(Bytecode::Interpreter& interp
return {}; return {};
} }
ThrowCompletionOr<void> PutBySpread::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
auto value = interpreter.get(m_src);
auto base = interpreter.get(m_base);
// a. Let baseObj be ? ToObject(V.[[Base]]).
auto object = TRY(base.to_object(vm));
TRY(object->copy_data_properties(vm, value, {}));
return {};
}
ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
@ -2807,7 +2819,7 @@ ThrowCompletionOr<void> PutByValueWithThis::execute_impl(Bytecode::Interpreter&
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
auto value = interpreter.get(m_src); auto value = interpreter.get(m_src);
auto base = interpreter.get(m_base); auto base = interpreter.get(m_base);
auto property_key = m_kind != PropertyKind::Spread ? TRY(interpreter.get(m_property).to_property_key(vm)) : PropertyKey { 0 }; auto property_key = TRY(interpreter.get(m_property).to_property_key(vm));
TRY(put_by_property_key(vm, base, interpreter.get(m_this_value), value, {}, property_key, m_kind)); TRY(put_by_property_key(vm, base, interpreter.get(m_this_value), value, {}, property_key, m_kind));
return {}; return {};
} }
@ -3158,14 +3170,19 @@ static StringView property_kind_to_string(PropertyKind kind)
return "key-value"sv; return "key-value"sv;
case PropertyKind::DirectKeyValue: case PropertyKind::DirectKeyValue:
return "direct-key-value"sv; return "direct-key-value"sv;
case PropertyKind::Spread:
return "spread"sv;
case PropertyKind::ProtoSetter: case PropertyKind::ProtoSetter:
return "proto-setter"sv; return "proto-setter"sv;
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
ByteString PutBySpread::to_byte_string_impl(Bytecode::Executable const& executable) const
{
return ByteString::formatted("PutBySpread {}, {}",
format_operand("base"sv, m_base, executable),
format_operand("src"sv, m_src, executable));
}
ByteString PutById::to_byte_string_impl(Bytecode::Executable const& executable) const ByteString PutById::to_byte_string_impl(Bytecode::Executable const& executable) const
{ {
auto kind = property_kind_to_string(m_kind); auto kind = property_kind_to_string(m_kind);

View file

@ -1120,10 +1120,34 @@ enum class PropertyKind {
Setter, Setter,
KeyValue, KeyValue,
DirectKeyValue, // Used for Object expressions. Always sets an own property, never calls a setter. DirectKeyValue, // Used for Object expressions. Always sets an own property, never calls a setter.
Spread,
ProtoSetter, ProtoSetter,
}; };
class PutBySpread final : public Instruction {
public:
PutBySpread(Operand base, Operand src)
: Instruction(Type::PutBySpread)
, m_base(base)
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_src);
}
Operand base() const { return m_base; }
Operand src() const { return m_src; }
private:
Operand m_base;
Operand m_src;
};
class PutById final : public Instruction { class PutById final : public Instruction {
public: public:
explicit PutById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {}) explicit PutById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {})