mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
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:
parent
27859c17b4
commit
bc05f6303f
Notes:
github-actions[bot]
2024-11-09 16:56:05 +00:00
Author: https://github.com/yyny Commit: https://github.com/LadybirdBrowser/ladybird/commit/bc05f6303f5 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2120 Reviewed-by: https://github.com/trflynn89
4 changed files with 56 additions and 21 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) \
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 = {})
|
||||||
|
|
Loading…
Reference in a new issue