mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-19 07:33:20 +00:00
Compare commits
6 commits
1c10421316
...
a76f420207
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a76f420207 | ||
![]() |
755c8d8cd6 | ||
![]() |
7462c10ee2 | ||
![]() |
b47f8f94fe | ||
![]() |
d13b4f3e39 | ||
![]() |
e7a3c4dbad |
8 changed files with 534 additions and 369 deletions
|
@ -645,15 +645,15 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> AssignmentExpression::g
|
|||
|
||||
if (expression.is_computed()) {
|
||||
if (!lhs_is_super_expression)
|
||||
generator.emit_put_by_value(*base, *computed_property, rval, Bytecode::Op::PropertyKind::KeyValue, move(base_identifier));
|
||||
generator.emit_put_by_value(*base, *computed_property, rval, Bytecode::PutKind::Normal, move(base_identifier));
|
||||
else
|
||||
generator.emit_put_by_value_with_this(*base, *computed_property, *this_value, rval, Op::PropertyKind::KeyValue);
|
||||
generator.emit_put_by_value_with_this(*base, *computed_property, *this_value, rval, PutKind::Normal);
|
||||
} else if (expression.property().is_identifier()) {
|
||||
auto identifier_table_ref = generator.intern_identifier(as<Identifier>(expression.property()).string());
|
||||
if (!lhs_is_super_expression)
|
||||
generator.emit_put_by_id(*base, identifier_table_ref, rval, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache(), move(base_identifier));
|
||||
generator.emit_put_by_id(*base, identifier_table_ref, rval, Bytecode::PutKind::Normal, generator.next_property_lookup_cache(), move(base_identifier));
|
||||
else
|
||||
generator.emit<Bytecode::Op::PutByIdWithThis>(*base, *this_value, identifier_table_ref, rval, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
|
||||
generator.emit<Bytecode::Op::PutNormalByIdWithThis>(*base, *this_value, identifier_table_ref, rval, generator.next_property_lookup_cache());
|
||||
} else if (expression.property().is_private_identifier()) {
|
||||
auto identifier_table_ref = generator.intern_identifier(as<PrivateIdentifier>(expression.property()).string());
|
||||
generator.emit<Bytecode::Op::PutPrivateById>(*base, identifier_table_ref, rval);
|
||||
|
@ -1151,19 +1151,19 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ObjectExpression::gener
|
|||
generator.push_home_object(object);
|
||||
|
||||
for (auto& property : m_properties) {
|
||||
Bytecode::Op::PropertyKind property_kind;
|
||||
Bytecode::PutKind property_kind;
|
||||
switch (property->type()) {
|
||||
case ObjectProperty::Type::KeyValue:
|
||||
property_kind = Bytecode::Op::PropertyKind::DirectKeyValue;
|
||||
property_kind = Bytecode::PutKind::Own;
|
||||
break;
|
||||
case ObjectProperty::Type::Getter:
|
||||
property_kind = Bytecode::Op::PropertyKind::Getter;
|
||||
property_kind = Bytecode::PutKind::Getter;
|
||||
break;
|
||||
case ObjectProperty::Type::Setter:
|
||||
property_kind = Bytecode::Op::PropertyKind::Setter;
|
||||
property_kind = Bytecode::PutKind::Setter;
|
||||
break;
|
||||
case ObjectProperty::Type::ProtoSetter:
|
||||
property_kind = Bytecode::Op::PropertyKind::ProtoSetter;
|
||||
property_kind = Bytecode::PutKind::Prototype;
|
||||
break;
|
||||
case ObjectProperty::Type::Spread:
|
||||
generator.emit<Bytecode::Op::PutBySpread>(object, TRY(property->key().generate_bytecode(generator)).value());
|
||||
|
@ -1175,13 +1175,13 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ObjectExpression::gener
|
|||
Bytecode::IdentifierTableIndex key_name = generator.intern_identifier(string_literal.value());
|
||||
|
||||
Optional<ScopedOperand> value;
|
||||
if (property_kind == Bytecode::Op::PropertyKind::ProtoSetter) {
|
||||
if (property_kind == Bytecode::PutKind::Prototype) {
|
||||
value = TRY(property->value().generate_bytecode(generator)).value();
|
||||
} else {
|
||||
auto identifier = string_literal.value();
|
||||
if (property_kind == Bytecode::Op::PropertyKind::Getter)
|
||||
if (property_kind == Bytecode::PutKind::Getter)
|
||||
identifier = Utf16String::formatted("get {}", identifier);
|
||||
else if (property_kind == Bytecode::Op::PropertyKind::Setter)
|
||||
else if (property_kind == Bytecode::PutKind::Setter)
|
||||
identifier = Utf16String::formatted("set {}", identifier);
|
||||
|
||||
auto name = generator.intern_identifier(identifier);
|
||||
|
@ -2540,7 +2540,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TaggedTemplateLiteral::
|
|||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(raw_string_regs.size(), raw_strings_array, raw_string_regs);
|
||||
}
|
||||
|
||||
generator.emit_put_by_id(strings_array, generator.intern_identifier("raw"_utf16_fly_string), raw_strings_array, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
|
||||
generator.emit_put_by_id(strings_array, generator.intern_identifier("raw"_utf16_fly_string), raw_strings_array, Bytecode::PutKind::Normal, generator.next_property_lookup_cache());
|
||||
|
||||
auto arguments = generator.allocate_register();
|
||||
if (!argument_regs.is_empty())
|
||||
|
|
|
@ -761,21 +761,21 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(JS::ASTNode const
|
|||
if (super_reference.referenced_name.has_value()) {
|
||||
// 5. Let propertyKey be ? ToPropertyKey(propertyNameValue).
|
||||
// FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive!
|
||||
emit_put_by_value_with_this(*super_reference.base, *super_reference.referenced_name, *super_reference.this_value, value, Op::PropertyKind::KeyValue);
|
||||
emit_put_by_value_with_this(*super_reference.base, *super_reference.referenced_name, *super_reference.this_value, value, PutKind::Normal);
|
||||
} else {
|
||||
// 3. Let propertyKey be StringValue of IdentifierName.
|
||||
auto identifier_table_ref = intern_identifier(as<Identifier>(expression.property()).string());
|
||||
emit<Bytecode::Op::PutByIdWithThis>(*super_reference.base, *super_reference.this_value, identifier_table_ref, value, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache());
|
||||
emit<Bytecode::Op::PutNormalByIdWithThis>(*super_reference.base, *super_reference.this_value, identifier_table_ref, value, next_property_lookup_cache());
|
||||
}
|
||||
} else {
|
||||
auto object = TRY(expression.object().generate_bytecode(*this)).value();
|
||||
|
||||
if (expression.is_computed()) {
|
||||
auto property = TRY(expression.property().generate_bytecode(*this)).value();
|
||||
emit_put_by_value(object, property, value, Op::PropertyKind::KeyValue, {});
|
||||
emit_put_by_value(object, property, value, PutKind::Normal, {});
|
||||
} else if (expression.property().is_identifier()) {
|
||||
auto identifier_table_ref = intern_identifier(as<Identifier>(expression.property()).string());
|
||||
emit_put_by_id(object, identifier_table_ref, value, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache());
|
||||
emit_put_by_id(object, identifier_table_ref, value, Bytecode::PutKind::Normal, next_property_lookup_cache());
|
||||
} else if (expression.property().is_private_identifier()) {
|
||||
auto identifier_table_ref = intern_identifier(as<PrivateIdentifier>(expression.property()).string());
|
||||
emit<Bytecode::Op::PutPrivateById>(object, identifier_table_ref, value);
|
||||
|
@ -804,15 +804,15 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(ReferenceOperands
|
|||
}
|
||||
if (reference.referenced_identifier.has_value()) {
|
||||
if (reference.base == reference.this_value)
|
||||
emit_put_by_id(*reference.base, *reference.referenced_identifier, value, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache());
|
||||
emit_put_by_id(*reference.base, *reference.referenced_identifier, value, Bytecode::PutKind::Normal, next_property_lookup_cache());
|
||||
else
|
||||
emit<Bytecode::Op::PutByIdWithThis>(*reference.base, *reference.this_value, *reference.referenced_identifier, value, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache());
|
||||
emit<Bytecode::Op::PutNormalByIdWithThis>(*reference.base, *reference.this_value, *reference.referenced_identifier, value, next_property_lookup_cache());
|
||||
return {};
|
||||
}
|
||||
if (reference.base == reference.this_value)
|
||||
emit_put_by_value(*reference.base, *reference.referenced_name, value, Op::PropertyKind::KeyValue, {});
|
||||
emit_put_by_value(*reference.base, *reference.referenced_name, value, PutKind::Normal, {});
|
||||
else
|
||||
emit_put_by_value_with_this(*reference.base, *reference.referenced_name, *reference.this_value, value, Op::PropertyKind::KeyValue);
|
||||
emit_put_by_value_with_this(*reference.base, *reference.referenced_name, *reference.this_value, value, PutKind::Normal);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1153,20 +1153,38 @@ void Generator::emit_get_by_value_with_this(ScopedOperand dst, ScopedOperand bas
|
|||
emit<Op::GetByValueWithThis>(dst, base, property, this_value);
|
||||
}
|
||||
|
||||
void Generator::emit_put_by_id(Operand base, IdentifierTableIndex property, Operand src, Op::PropertyKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier)
|
||||
void Generator::emit_put_by_id(Operand base, IdentifierTableIndex property, Operand src, PutKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier)
|
||||
{
|
||||
auto string = m_identifier_table->get(property);
|
||||
if (!string.is_empty() && !(string.code_unit_at(0) == '0' && string.length_in_code_units() > 1)) {
|
||||
auto property_index = string.to_number<u32>(TrimWhitespace::No);
|
||||
if (property_index.has_value() && property_index.value() < NumericLimits<u32>::max()) {
|
||||
emit<Op::PutByNumericId>(base, property_index.release_value(), src, kind, cache_index, move(base_identifier));
|
||||
#define EMIT_PUT_BY_NUMERIC_ID(kind) \
|
||||
case PutKind::kind: \
|
||||
emit<Op::Put##kind##ByNumericId>(base, property_index.release_value(), src, cache_index, move(base_identifier)); \
|
||||
break;
|
||||
switch (kind) {
|
||||
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_NUMERIC_ID)
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
#undef EMIT_PUT_BY_NUMERIC_ID
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit<Op::PutById>(base, property, src, kind, cache_index, move(base_identifier));
|
||||
#define EMIT_PUT_BY_ID(kind) \
|
||||
case PutKind::kind: \
|
||||
emit<Op::Put##kind##ById>(base, property, src, cache_index, move(base_identifier)); \
|
||||
break;
|
||||
switch (kind) {
|
||||
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_ID)
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
#undef EMIT_PUT_BY_ID
|
||||
}
|
||||
|
||||
void Generator::emit_put_by_value(ScopedOperand base, ScopedOperand property, ScopedOperand src, Bytecode::Op::PropertyKind kind, Optional<IdentifierTableIndex> base_identifier)
|
||||
void Generator::emit_put_by_value(ScopedOperand base, ScopedOperand property, ScopedOperand src, Bytecode::PutKind kind, Optional<IdentifierTableIndex> base_identifier)
|
||||
{
|
||||
if (property.operand().is_constant() && get_constant(property).is_string()) {
|
||||
auto property_key = MUST(get_constant(property).to_property_key(vm()));
|
||||
|
@ -1175,19 +1193,46 @@ void Generator::emit_put_by_value(ScopedOperand base, ScopedOperand property, Sc
|
|||
return;
|
||||
}
|
||||
}
|
||||
emit<Op::PutByValue>(base, property, src, kind, base_identifier);
|
||||
#define EMIT_PUT_BY_VALUE(kind) \
|
||||
case PutKind::kind: \
|
||||
emit<Op::Put##kind##ByValue>(base, property, src, move(base_identifier)); \
|
||||
break;
|
||||
switch (kind) {
|
||||
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_VALUE)
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
#undef EMIT_PUT_BY_VALUE
|
||||
}
|
||||
|
||||
void Generator::emit_put_by_value_with_this(ScopedOperand base, ScopedOperand property, ScopedOperand this_value, ScopedOperand src, Bytecode::Op::PropertyKind kind)
|
||||
void Generator::emit_put_by_value_with_this(ScopedOperand base, ScopedOperand property, ScopedOperand this_value, ScopedOperand src, Bytecode::PutKind kind)
|
||||
{
|
||||
if (property.operand().is_constant() && get_constant(property).is_string()) {
|
||||
auto property_key = MUST(get_constant(property).to_property_key(vm()));
|
||||
if (property_key.is_string()) {
|
||||
emit<Op::PutByIdWithThis>(base, this_value, intern_identifier(property_key.as_string()), src, kind, m_next_property_lookup_cache++);
|
||||
#define EMIT_PUT_BY_ID_WITH_THIS(kind) \
|
||||
case PutKind::kind: \
|
||||
emit<Op::Put##kind##ByIdWithThis>(base, this_value, intern_identifier(property_key.as_string()), src, m_next_property_lookup_cache++); \
|
||||
break;
|
||||
switch (kind) {
|
||||
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_ID_WITH_THIS)
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
#undef EMIT_PUT_BY_ID_WITH_THIS
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit<Bytecode::Op::PutByValueWithThis>(base, property, this_value, src, kind);
|
||||
#define EMIT_PUT_BY_VALUE_WITH_THIS(kind) \
|
||||
case PutKind::kind: \
|
||||
emit<Op::Put##kind##ByValueWithThis>(base, property, this_value, src); \
|
||||
break;
|
||||
switch (kind) {
|
||||
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_VALUE_WITH_THIS)
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
#undef EMIT_PUT_BY_VALUE_WITH_THIS
|
||||
}
|
||||
|
||||
void Generator::emit_iterator_value(ScopedOperand dst, ScopedOperand result)
|
||||
|
|
|
@ -329,10 +329,10 @@ public:
|
|||
void emit_get_by_value(ScopedOperand dst, ScopedOperand base, ScopedOperand property, Optional<IdentifierTableIndex> base_identifier = {});
|
||||
void emit_get_by_value_with_this(ScopedOperand dst, ScopedOperand base, ScopedOperand property, ScopedOperand this_value);
|
||||
|
||||
void emit_put_by_id(Operand base, IdentifierTableIndex property, Operand src, Op::PropertyKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {});
|
||||
void emit_put_by_id(Operand base, IdentifierTableIndex property, Operand src, PutKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {});
|
||||
|
||||
void emit_put_by_value(ScopedOperand base, ScopedOperand property, ScopedOperand src, Bytecode::Op::PropertyKind, Optional<IdentifierTableIndex> base_identifier);
|
||||
void emit_put_by_value_with_this(ScopedOperand base, ScopedOperand property, ScopedOperand this_value, ScopedOperand src, Bytecode::Op::PropertyKind);
|
||||
void emit_put_by_value(ScopedOperand base, ScopedOperand property, ScopedOperand src, Bytecode::PutKind, Optional<IdentifierTableIndex> base_identifier);
|
||||
void emit_put_by_value_with_this(ScopedOperand base, ScopedOperand property, ScopedOperand this_value, ScopedOperand src, Bytecode::PutKind);
|
||||
|
||||
void emit_iterator_value(ScopedOperand dst, ScopedOperand result);
|
||||
void emit_iterator_complete(ScopedOperand dst, ScopedOperand result);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2021-2025, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -10,6 +10,7 @@
|
|||
#include <AK/Function.h>
|
||||
#include <AK/Span.h>
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/PutKind.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/SourceRange.h>
|
||||
|
||||
|
@ -122,12 +123,37 @@
|
|||
O(PrepareYield) \
|
||||
O(PostfixDecrement) \
|
||||
O(PostfixIncrement) \
|
||||
O(PutById) \
|
||||
O(PutByNumericId) \
|
||||
O(PutByIdWithThis) \
|
||||
O(PutNormalById) \
|
||||
O(PutOwnById) \
|
||||
O(PutGetterById) \
|
||||
O(PutSetterById) \
|
||||
O(PutPrototypeById) \
|
||||
O(PutNormalByNumericId) \
|
||||
O(PutOwnByNumericId) \
|
||||
O(PutGetterByNumericId) \
|
||||
O(PutSetterByNumericId) \
|
||||
O(PutPrototypeByNumericId) \
|
||||
O(PutNormalByIdWithThis) \
|
||||
O(PutOwnByIdWithThis) \
|
||||
O(PutGetterByIdWithThis) \
|
||||
O(PutSetterByIdWithThis) \
|
||||
O(PutPrototypeByIdWithThis) \
|
||||
O(PutNormalByNumericIdWithThis) \
|
||||
O(PutOwnByNumericIdWithThis) \
|
||||
O(PutGetterByNumericIdWithThis) \
|
||||
O(PutSetterByNumericIdWithThis) \
|
||||
O(PutPrototypeByNumericIdWithThis) \
|
||||
O(PutBySpread) \
|
||||
O(PutByValue) \
|
||||
O(PutByValueWithThis) \
|
||||
O(PutNormalByValue) \
|
||||
O(PutOwnByValue) \
|
||||
O(PutGetterByValue) \
|
||||
O(PutSetterByValue) \
|
||||
O(PutPrototypeByValue) \
|
||||
O(PutNormalByValueWithThis) \
|
||||
O(PutOwnByValueWithThis) \
|
||||
O(PutGetterByValueWithThis) \
|
||||
O(PutSetterByValueWithThis) \
|
||||
O(PutPrototypeByValueWithThis) \
|
||||
O(PutPrivateById) \
|
||||
O(ResolveSuperBase) \
|
||||
O(ResolveThisBinding) \
|
||||
|
|
|
@ -638,12 +638,22 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
|||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(PrepareYield);
|
||||
HANDLE_INSTRUCTION(PostfixDecrement);
|
||||
HANDLE_INSTRUCTION(PostfixIncrement);
|
||||
HANDLE_INSTRUCTION(PutById);
|
||||
HANDLE_INSTRUCTION(PutByNumericId);
|
||||
HANDLE_INSTRUCTION(PutByIdWithThis);
|
||||
|
||||
#define HANDLE_PUT_KIND_BY_ID(kind) HANDLE_INSTRUCTION(Put##kind##ById);
|
||||
#define HANDLE_PUT_KIND_BY_NUMERIC_ID(kind) HANDLE_INSTRUCTION(Put##kind##ByNumericId);
|
||||
#define HANDLE_PUT_KIND_BY_VALUE(kind) HANDLE_INSTRUCTION(Put##kind##ByValue);
|
||||
#define HANDLE_PUT_KIND_BY_VALUE_WITH_THIS(kind) HANDLE_INSTRUCTION(Put##kind##ByValueWithThis);
|
||||
#define HANDLE_PUT_KIND_BY_ID_WITH_THIS(kind) HANDLE_INSTRUCTION(Put##kind##ByIdWithThis);
|
||||
#define HANDLE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS(kind) HANDLE_INSTRUCTION(Put##kind##ByNumericIdWithThis);
|
||||
|
||||
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_ID)
|
||||
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_NUMERIC_ID)
|
||||
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_ID_WITH_THIS)
|
||||
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS)
|
||||
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_VALUE)
|
||||
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_VALUE_WITH_THIS)
|
||||
|
||||
HANDLE_INSTRUCTION(PutBySpread);
|
||||
HANDLE_INSTRUCTION(PutByValue);
|
||||
HANDLE_INSTRUCTION(PutByValueWithThis);
|
||||
HANDLE_INSTRUCTION(PutPrivateById);
|
||||
HANDLE_INSTRUCTION(ResolveSuperBase);
|
||||
HANDLE_INSTRUCTION(ResolveThisBinding);
|
||||
|
@ -1188,73 +1198,74 @@ inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierT
|
|||
return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, identifier);
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, Optional<Utf16FlyString const&> const& base_identifier, PropertyKey name, Op::PropertyKind kind, PropertyLookupCache* caches = nullptr)
|
||||
template<PutKind kind>
|
||||
ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, Optional<Utf16FlyString const&> const& base_identifier, PropertyKey name, PropertyLookupCache* caches = nullptr)
|
||||
{
|
||||
// Better error message than to_object would give
|
||||
if (vm.in_strict_mode() && base.is_nullish())
|
||||
if (vm.in_strict_mode() && base.is_nullish()) [[unlikely]]
|
||||
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());
|
||||
|
||||
// a. Let baseObj be ? ToObject(V.[[Base]]).
|
||||
auto maybe_object = base.to_object(vm);
|
||||
if (maybe_object.is_error())
|
||||
if (maybe_object.is_error()) [[unlikely]]
|
||||
return throw_null_or_undefined_property_access(vm, base, base_identifier, name);
|
||||
auto object = maybe_object.release_value();
|
||||
|
||||
if (kind == Op::PropertyKind::Getter || kind == Op::PropertyKind::Setter) {
|
||||
if constexpr (kind == PutKind::Getter || kind == PutKind::Setter) {
|
||||
// The generator should only pass us functions for getters and setters.
|
||||
VERIFY(value.is_function());
|
||||
}
|
||||
switch (kind) {
|
||||
case Op::PropertyKind::Getter: {
|
||||
case PutKind::Getter: {
|
||||
auto& function = value.as_function();
|
||||
if (is<ECMAScriptFunctionObject>(function) && static_cast<ECMAScriptFunctionObject const&>(function).name().is_empty())
|
||||
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(Utf16String::formatted("get {}", name));
|
||||
object->define_direct_accessor(name, &function, nullptr, Attribute::Configurable | Attribute::Enumerable);
|
||||
break;
|
||||
}
|
||||
case Op::PropertyKind::Setter: {
|
||||
case PutKind::Setter: {
|
||||
auto& function = value.as_function();
|
||||
if (is<ECMAScriptFunctionObject>(function) && static_cast<ECMAScriptFunctionObject const&>(function).name().is_empty())
|
||||
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(Utf16String::formatted("set {}", name));
|
||||
object->define_direct_accessor(name, nullptr, &function, Attribute::Configurable | Attribute::Enumerable);
|
||||
break;
|
||||
}
|
||||
case Op::PropertyKind::KeyValue: {
|
||||
case PutKind::Normal: {
|
||||
auto this_value_object = MUST(this_value.to_object(vm));
|
||||
auto& from_shape = this_value_object->shape();
|
||||
if (caches) {
|
||||
if (caches) [[likely]] {
|
||||
for (auto& cache : caches->entries) {
|
||||
switch (cache.type) {
|
||||
case PropertyLookupCache::Entry::Type::Empty:
|
||||
break;
|
||||
case PropertyLookupCache::Entry::Type::ChangePropertyInPrototypeChain: {
|
||||
if (!cache.prototype)
|
||||
if (!cache.prototype) [[unlikely]]
|
||||
break;
|
||||
// OPTIMIZATION: If the prototype chain hasn't been mutated in a way that would invalidate the cache, we can use it.
|
||||
bool can_use_cache = [&]() -> bool {
|
||||
if (&object->shape() != cache.shape)
|
||||
if (&object->shape() != cache.shape) [[unlikely]]
|
||||
return false;
|
||||
if (!cache.prototype_chain_validity)
|
||||
if (!cache.prototype_chain_validity) [[unlikely]]
|
||||
return false;
|
||||
if (!cache.prototype_chain_validity->is_valid())
|
||||
if (!cache.prototype_chain_validity->is_valid()) [[unlikely]]
|
||||
return false;
|
||||
return true;
|
||||
}();
|
||||
if (can_use_cache) {
|
||||
auto value_in_prototype = cache.prototype->get_direct(cache.property_offset.value());
|
||||
if (value_in_prototype.is_accessor()) {
|
||||
TRY(call(vm, value_in_prototype.as_accessor().setter(), this_value, value));
|
||||
if (value_in_prototype.is_accessor()) [[unlikely]] {
|
||||
(void)TRY(call(vm, value_in_prototype.as_accessor().setter(), this_value, value));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PropertyLookupCache::Entry::Type::ChangeOwnProperty: {
|
||||
if (cache.shape != &object->shape())
|
||||
if (cache.shape != &object->shape()) [[unlikely]]
|
||||
break;
|
||||
auto value_in_object = object->get_direct(cache.property_offset.value());
|
||||
if (value_in_object.is_accessor()) {
|
||||
TRY(call(vm, value_in_object.as_accessor().setter(), this_value, value));
|
||||
if (value_in_object.is_accessor()) [[unlikely]] {
|
||||
(void)TRY(call(vm, value_in_object.as_accessor().setter(), this_value, value));
|
||||
} else {
|
||||
object->put_direct(*cache.property_offset, value);
|
||||
}
|
||||
|
@ -1263,12 +1274,12 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
|
|||
case PropertyLookupCache::Entry::Type::AddOwnProperty: {
|
||||
// OPTIMIZATION: If the object's shape is the same as the one cached before adding the new property, we can
|
||||
// reuse the resulting shape from the cache.
|
||||
if (cache.from_shape != &object->shape())
|
||||
if (cache.from_shape != &object->shape()) [[unlikely]]
|
||||
break;
|
||||
if (!cache.shape)
|
||||
if (!cache.shape) [[unlikely]]
|
||||
break;
|
||||
// The cache is invalid if the prototype chain has been mutated, since such a mutation could have added a setter for the property.
|
||||
if (cache.prototype_chain_validity && !cache.prototype_chain_validity->is_valid())
|
||||
if (cache.prototype_chain_validity && !cache.prototype_chain_validity->is_valid()) [[unlikely]]
|
||||
break;
|
||||
object->unsafe_set_shape(*cache.shape);
|
||||
object->put_direct(*cache.property_offset, value);
|
||||
|
@ -1338,11 +1349,11 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
|
|||
}
|
||||
break;
|
||||
}
|
||||
case Op::PropertyKind::DirectKeyValue:
|
||||
case PutKind::Own:
|
||||
object->define_direct_property(name, value, Attribute::Enumerable | Attribute::Writable | Attribute::Configurable);
|
||||
break;
|
||||
case Op::PropertyKind::ProtoSetter:
|
||||
if (value.is_object() || value.is_null())
|
||||
case PutKind::Prototype:
|
||||
if (value.is_object() || value.is_null()) [[likely]]
|
||||
MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr));
|
||||
break;
|
||||
}
|
||||
|
@ -1414,10 +1425,11 @@ inline Value new_function(VM& vm, FunctionNode const& function_node, Optional<Id
|
|||
return value;
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16FlyString const&> const& base_identifier, Value property_key_value, Value value, Op::PropertyKind kind)
|
||||
template<PutKind kind>
|
||||
inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16FlyString const&> const& base_identifier, Value property_key_value, Value value)
|
||||
{
|
||||
// OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects.
|
||||
if ((kind == Op::PropertyKind::KeyValue || kind == Op::PropertyKind::DirectKeyValue)
|
||||
if (kind == PutKind::Normal
|
||||
&& base.is_object() && property_key_value.is_int32() && property_key_value.as_i32() >= 0) {
|
||||
auto& object = base.as_object();
|
||||
auto* storage = object.indexed_properties().storage();
|
||||
|
@ -1526,7 +1538,7 @@ inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16Fl
|
|||
}
|
||||
|
||||
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<kind>(vm, base, base, value, base_identifier, property_key));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1991,6 +2003,21 @@ ThrowCompletionOr<void> Mul::execute_impl(Bytecode::Interpreter& interpreter) co
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> Div::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto const lhs = interpreter.get(m_lhs);
|
||||
auto const rhs = interpreter.get(m_rhs);
|
||||
|
||||
if (lhs.is_number() && rhs.is_number()) [[likely]] {
|
||||
interpreter.set(m_dst, Value(lhs.as_double() / rhs.as_double()));
|
||||
return {};
|
||||
}
|
||||
|
||||
interpreter.set(m_dst, TRY(div(vm, lhs, rhs)));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> Sub::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
|
@ -2682,40 +2709,93 @@ ThrowCompletionOr<void> PutBySpread::execute_impl(Bytecode::Interpreter& interpr
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto value = interpreter.get(m_src);
|
||||
auto base = interpreter.get(m_base);
|
||||
auto base_identifier = interpreter.get_identifier(m_base_identifier);
|
||||
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No };
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
|
||||
TRY(put_by_property_key(vm, base, base, value, base_identifier, name, m_kind, &cache));
|
||||
return {};
|
||||
}
|
||||
#define DEFINE_PUT_KIND_BY_ID(kind) \
|
||||
ThrowCompletionOr<void> Put##kind##ById::execute_impl(Bytecode::Interpreter& interpreter) const \
|
||||
{ \
|
||||
auto& vm = interpreter.vm(); \
|
||||
auto value = interpreter.get(m_src); \
|
||||
auto base = interpreter.get(m_base); \
|
||||
auto base_identifier = interpreter.get_identifier(m_base_identifier); \
|
||||
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; \
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
|
||||
TRY(put_by_property_key<PutKind::kind>(vm, base, base, value, base_identifier, name, &cache)); \
|
||||
return {}; \
|
||||
} \
|
||||
ByteString Put##kind##ById::to_byte_string_impl(Bytecode::Executable const& executable) const \
|
||||
{ \
|
||||
return ByteString::formatted("Put" #kind "ById {}, {}, {}", \
|
||||
format_operand("base"sv, m_base, executable), \
|
||||
executable.identifier_table->get(m_property), \
|
||||
format_operand("src"sv, m_src, executable)); \
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> PutByNumericId::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto value = interpreter.get(m_src);
|
||||
auto base = interpreter.get(m_base);
|
||||
auto base_identifier = interpreter.get_identifier(m_base_identifier);
|
||||
PropertyKey name { m_property_index };
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
|
||||
TRY(put_by_property_key(vm, base, base, value, base_identifier, name, m_kind, &cache));
|
||||
return {};
|
||||
}
|
||||
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_ID)
|
||||
|
||||
ThrowCompletionOr<void> PutByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto value = interpreter.get(m_src);
|
||||
auto base = interpreter.get(m_base);
|
||||
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No };
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
|
||||
TRY(put_by_property_key(vm, base, interpreter.get(m_this_value), value, {}, name, m_kind, &cache));
|
||||
return {};
|
||||
}
|
||||
#define DEFINE_PUT_KIND_BY_NUMERIC_ID(kind) \
|
||||
ThrowCompletionOr<void> Put##kind##ByNumericId::execute_impl(Bytecode::Interpreter& interpreter) const \
|
||||
{ \
|
||||
auto& vm = interpreter.vm(); \
|
||||
auto value = interpreter.get(m_src); \
|
||||
auto base = interpreter.get(m_base); \
|
||||
auto base_identifier = interpreter.get_identifier(m_base_identifier); \
|
||||
PropertyKey name { m_property }; \
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
|
||||
TRY(put_by_property_key<PutKind::kind>(vm, base, base, value, base_identifier, name, &cache)); \
|
||||
return {}; \
|
||||
} \
|
||||
ByteString Put##kind##ByNumericId::to_byte_string_impl(Bytecode::Executable const& executable) const \
|
||||
{ \
|
||||
return ByteString::formatted("Put" #kind "ByNumericId {}, {}, {}", \
|
||||
format_operand("base"sv, m_base, executable), \
|
||||
m_property, \
|
||||
format_operand("src"sv, m_src, executable)); \
|
||||
}
|
||||
|
||||
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_NUMERIC_ID)
|
||||
|
||||
#define DEFINE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS(kind) \
|
||||
ThrowCompletionOr<void> Put##kind##ByNumericIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
|
||||
{ \
|
||||
auto& vm = interpreter.vm(); \
|
||||
auto value = interpreter.get(m_src); \
|
||||
auto base = interpreter.get(m_base); \
|
||||
PropertyKey name { m_property }; \
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
|
||||
TRY(put_by_property_key<PutKind::kind>(vm, base, interpreter.get(m_this_value), value, {}, name, &cache)); \
|
||||
return {}; \
|
||||
} \
|
||||
ByteString Put##kind##ByNumericIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
|
||||
{ \
|
||||
return ByteString::formatted("Put" #kind "ByNumericIdWithThis {}, {}, {}, {}", \
|
||||
format_operand("base"sv, m_base, executable), \
|
||||
m_property, \
|
||||
format_operand("src"sv, m_src, executable), \
|
||||
format_operand("this"sv, m_this_value, executable)); \
|
||||
}
|
||||
|
||||
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS)
|
||||
|
||||
#define DEFINE_PUT_KIND_BY_ID_WITH_THIS(kind) \
|
||||
ThrowCompletionOr<void> Put##kind##ByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
|
||||
{ \
|
||||
auto& vm = interpreter.vm(); \
|
||||
auto value = interpreter.get(m_src); \
|
||||
auto base = interpreter.get(m_base); \
|
||||
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; \
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
|
||||
TRY(put_by_property_key<PutKind::kind>(vm, base, interpreter.get(m_this_value), value, {}, name, &cache)); \
|
||||
return {}; \
|
||||
} \
|
||||
ByteString Put##kind##ByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
|
||||
{ \
|
||||
return ByteString::formatted("Put" #kind "ByIdWithThis {}, {}, {}, {}", \
|
||||
format_operand("base"sv, m_base, executable), \
|
||||
executable.identifier_table->get(m_property), \
|
||||
format_operand("src"sv, m_src, executable), \
|
||||
format_operand("this"sv, m_this_value, executable)); \
|
||||
}
|
||||
|
||||
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_ID_WITH_THIS)
|
||||
|
||||
ThrowCompletionOr<void> PutPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
|
@ -3218,24 +3298,48 @@ ThrowCompletionOr<void> GetByValueWithThis::execute_impl(Bytecode::Interpreter&
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> PutByValue::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto value = interpreter.get(m_src);
|
||||
auto base_identifier = interpreter.get_identifier(m_base_identifier);
|
||||
TRY(put_by_value(vm, interpreter.get(m_base), base_identifier, interpreter.get(m_property), value, m_kind));
|
||||
return {};
|
||||
}
|
||||
#define DEFINE_PUT_KIND_BY_VALUE(kind) \
|
||||
ThrowCompletionOr<void> Put##kind##ByValue::execute_impl(Bytecode::Interpreter& interpreter) const \
|
||||
{ \
|
||||
auto& vm = interpreter.vm(); \
|
||||
auto value = interpreter.get(m_src); \
|
||||
auto base = interpreter.get(m_base); \
|
||||
auto base_identifier = interpreter.get_identifier(m_base_identifier); \
|
||||
auto property = interpreter.get(m_property); \
|
||||
TRY(put_by_value<PutKind::kind>(vm, base, base_identifier, property, value)); \
|
||||
return {}; \
|
||||
} \
|
||||
ByteString Put##kind##ByValue::to_byte_string_impl(Bytecode::Executable const& executable) const \
|
||||
{ \
|
||||
return ByteString::formatted("Put" #kind "ByValue {}, {}, {}", \
|
||||
format_operand("base"sv, m_base, executable), \
|
||||
format_operand("property"sv, m_property, executable), \
|
||||
format_operand("src"sv, m_src, executable)); \
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> PutByValueWithThis::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto value = interpreter.get(m_src);
|
||||
auto base = interpreter.get(m_base);
|
||||
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));
|
||||
return {};
|
||||
}
|
||||
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_VALUE)
|
||||
|
||||
#define DEFINE_PUT_KIND_BY_VALUE_WITH_THIS(kind) \
|
||||
ThrowCompletionOr<void> Put##kind##ByValueWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
|
||||
{ \
|
||||
auto& vm = interpreter.vm(); \
|
||||
auto value = interpreter.get(m_src); \
|
||||
auto base = interpreter.get(m_base); \
|
||||
auto this_value = interpreter.get(m_this_value); \
|
||||
auto property_key = TRY(interpreter.get(m_property).to_property_key(vm)); \
|
||||
TRY(put_by_property_key<PutKind::kind>(vm, base, this_value, value, {}, property_key)); \
|
||||
return {}; \
|
||||
} \
|
||||
ByteString Put##kind##ByValueWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
|
||||
{ \
|
||||
return ByteString::formatted("Put" #kind "ByValueWithThis {}, {}, {}, {}", \
|
||||
format_operand("base"sv, m_base, executable), \
|
||||
format_operand("property"sv, m_property, executable), \
|
||||
format_operand("src"sv, m_src, executable), \
|
||||
format_operand("this"sv, m_this_value, executable)); \
|
||||
}
|
||||
|
||||
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_VALUE_WITH_THIS)
|
||||
|
||||
ThrowCompletionOr<void> DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
|
@ -3591,23 +3695,6 @@ ByteString SetVariableBinding::to_byte_string_impl(Bytecode::Executable const& e
|
|||
format_operand("src"sv, src(), executable));
|
||||
}
|
||||
|
||||
static StringView property_kind_to_string(PropertyKind kind)
|
||||
{
|
||||
switch (kind) {
|
||||
case PropertyKind::Getter:
|
||||
return "getter"sv;
|
||||
case PropertyKind::Setter:
|
||||
return "setter"sv;
|
||||
case PropertyKind::KeyValue:
|
||||
return "key-value"sv;
|
||||
case PropertyKind::DirectKeyValue:
|
||||
return "direct-key-value"sv;
|
||||
case PropertyKind::ProtoSetter:
|
||||
return "proto-setter"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ByteString PutBySpread::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("PutBySpread {}, {}",
|
||||
|
@ -3615,46 +3702,13 @@ ByteString PutBySpread::to_byte_string_impl(Bytecode::Executable const& executab
|
|||
format_operand("src"sv, m_src, executable));
|
||||
}
|
||||
|
||||
ByteString PutById::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
auto kind = property_kind_to_string(m_kind);
|
||||
return ByteString::formatted("PutById {}, {}, {}, kind:{}",
|
||||
format_operand("base"sv, m_base, executable),
|
||||
executable.identifier_table->get(m_property),
|
||||
format_operand("src"sv, m_src, executable),
|
||||
kind);
|
||||
}
|
||||
|
||||
ByteString PutByNumericId::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
auto kind = property_kind_to_string(m_kind);
|
||||
return ByteString::formatted("PutByNumericId {}, {}, {}, kind:{}",
|
||||
format_operand("base"sv, m_base, executable),
|
||||
m_property_index,
|
||||
format_operand("src"sv, m_src, executable),
|
||||
kind);
|
||||
}
|
||||
|
||||
ByteString PutByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
auto kind = property_kind_to_string(m_kind);
|
||||
return ByteString::formatted("PutByIdWithThis {}, {}, {}, {}, kind:{}",
|
||||
format_operand("base"sv, m_base, executable),
|
||||
executable.identifier_table->get(m_property),
|
||||
format_operand("src"sv, m_src, executable),
|
||||
format_operand("this"sv, m_this_value, executable),
|
||||
kind);
|
||||
}
|
||||
|
||||
ByteString PutPrivateById::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
auto kind = property_kind_to_string(m_kind);
|
||||
return ByteString::formatted(
|
||||
"PutPrivateById {}, {}, {}, kind:{} ",
|
||||
"PutPrivateById {}, {}, {}",
|
||||
format_operand("base"sv, m_base, executable),
|
||||
executable.identifier_table->get(m_property),
|
||||
format_operand("src"sv, m_src, executable),
|
||||
kind);
|
||||
format_operand("src"sv, m_src, executable));
|
||||
}
|
||||
|
||||
ByteString GetById::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
|
@ -4047,27 +4101,6 @@ ByteString GetByValueWithThis::to_byte_string_impl(Bytecode::Executable const& e
|
|||
format_operand("property"sv, m_property, executable));
|
||||
}
|
||||
|
||||
ByteString PutByValue::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
auto kind = property_kind_to_string(m_kind);
|
||||
return ByteString::formatted("PutByValue {}, {}, {}, kind:{}",
|
||||
format_operand("base"sv, m_base, executable),
|
||||
format_operand("property"sv, m_property, executable),
|
||||
format_operand("src"sv, m_src, executable),
|
||||
kind);
|
||||
}
|
||||
|
||||
ByteString PutByValueWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
auto kind = property_kind_to_string(m_kind);
|
||||
return ByteString::formatted("PutByValueWithThis {}, {}, {}, {}, kind:{}",
|
||||
format_operand("base"sv, m_base, executable),
|
||||
format_operand("property"sv, m_property, executable),
|
||||
format_operand("src"sv, m_src, executable),
|
||||
format_operand("this"sv, m_this_value, executable),
|
||||
kind);
|
||||
}
|
||||
|
||||
ByteString DeleteByValue::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("DeleteByValue {}, {}, {}",
|
||||
|
|
|
@ -113,6 +113,7 @@ private:
|
|||
O(BitwiseAnd, bitwise_and) \
|
||||
O(BitwiseOr, bitwise_or) \
|
||||
O(BitwiseXor, bitwise_xor) \
|
||||
O(Div, div) \
|
||||
O(GreaterThan, greater_than) \
|
||||
O(GreaterThanEquals, greater_than_equals) \
|
||||
O(LeftShift, left_shift) \
|
||||
|
@ -124,7 +125,6 @@ private:
|
|||
O(UnsignedRightShift, unsigned_right_shift)
|
||||
|
||||
#define JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(O) \
|
||||
O(Div, div) \
|
||||
O(Exp, exp) \
|
||||
O(Mod, mod) \
|
||||
O(In, in) \
|
||||
|
@ -1180,14 +1180,6 @@ private:
|
|||
IdentifierTableIndex m_property;
|
||||
};
|
||||
|
||||
enum class PropertyKind {
|
||||
Getter,
|
||||
Setter,
|
||||
KeyValue,
|
||||
DirectKeyValue, // Used for Object expressions. Always sets an own property, never calls a setter.
|
||||
ProtoSetter,
|
||||
};
|
||||
|
||||
class PutBySpread final : public Instruction {
|
||||
public:
|
||||
PutBySpread(Operand base, Operand src)
|
||||
|
@ -1213,118 +1205,161 @@ private:
|
|||
Operand m_src;
|
||||
};
|
||||
|
||||
class PutById final : public Instruction {
|
||||
public:
|
||||
explicit PutById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {})
|
||||
: Instruction(Type::PutById)
|
||||
, m_base(base)
|
||||
, m_property(property)
|
||||
, m_src(src)
|
||||
, m_kind(kind)
|
||||
, m_cache_index(cache_index)
|
||||
, m_base_identifier(move(base_identifier))
|
||||
{
|
||||
}
|
||||
#define DECLARE_PUT_KIND_BY_ID(kind) \
|
||||
class Put##kind##ById final : public Instruction { \
|
||||
public: \
|
||||
explicit Put##kind##ById(Operand base, IdentifierTableIndex property, Operand src, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {}) \
|
||||
: Instruction(Type::Put##kind##ById) \
|
||||
, m_base(base) \
|
||||
, m_property(property) \
|
||||
, m_src(src) \
|
||||
, m_cache_index(cache_index) \
|
||||
, m_base_identifier(move(base_identifier)) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
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; } \
|
||||
IdentifierTableIndex property() const { return m_property; } \
|
||||
Operand src() const { return m_src; } \
|
||||
u32 cache_index() const { return m_cache_index; } \
|
||||
\
|
||||
private: \
|
||||
Operand m_base; \
|
||||
IdentifierTableIndex m_property; \
|
||||
Operand m_src; \
|
||||
u32 m_cache_index { 0 }; \
|
||||
Optional<IdentifierTableIndex> m_base_identifier {}; \
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_ID)
|
||||
|
||||
Operand base() const { return m_base; }
|
||||
IdentifierTableIndex property() const { return m_property; }
|
||||
Operand src() const { return m_src; }
|
||||
PropertyKind kind() const { return m_kind; }
|
||||
u32 cache_index() const { return m_cache_index; }
|
||||
#define DECLARE_PUT_KIND_BY_NUMERIC_ID(kind) \
|
||||
class Put##kind##ByNumericId final : public Instruction { \
|
||||
public: \
|
||||
explicit Put##kind##ByNumericId(Operand base, u32 property, Operand src, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {}) \
|
||||
: Instruction(Type::Put##kind##ByNumericId) \
|
||||
, m_base(base) \
|
||||
, m_property(property) \
|
||||
, m_src(src) \
|
||||
, m_cache_index(cache_index) \
|
||||
, m_base_identifier(move(base_identifier)) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
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; } \
|
||||
u32 property() const { return m_property; } \
|
||||
Operand src() const { return m_src; } \
|
||||
u32 cache_index() const { return m_cache_index; } \
|
||||
\
|
||||
private: \
|
||||
Operand m_base; \
|
||||
u32 m_property; \
|
||||
Operand m_src; \
|
||||
u32 m_cache_index { 0 }; \
|
||||
Optional<IdentifierTableIndex> m_base_identifier {}; \
|
||||
};
|
||||
|
||||
private:
|
||||
Operand m_base;
|
||||
IdentifierTableIndex m_property;
|
||||
Operand m_src;
|
||||
PropertyKind m_kind;
|
||||
u32 m_cache_index { 0 };
|
||||
Optional<IdentifierTableIndex> m_base_identifier {};
|
||||
};
|
||||
JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_NUMERIC_ID)
|
||||
|
||||
class PutByNumericId final : public Instruction {
|
||||
public:
|
||||
explicit PutByNumericId(Operand base, u64 property_index, Operand src, PropertyKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {})
|
||||
: Instruction(Type::PutByNumericId)
|
||||
, m_base(base)
|
||||
, m_property_index(property_index)
|
||||
, m_src(src)
|
||||
, m_kind(kind)
|
||||
, m_cache_index(cache_index)
|
||||
, m_base_identifier(move(base_identifier))
|
||||
{
|
||||
}
|
||||
#define DECLARE_PUT_KIND_BY_ID_WITH_THIS(kind) \
|
||||
class Put##kind##ByIdWithThis final : public Instruction { \
|
||||
public: \
|
||||
Put##kind##ByIdWithThis(Operand base, Operand this_value, IdentifierTableIndex property, Operand src, u32 cache_index) \
|
||||
: Instruction(Type::Put##kind##ByIdWithThis) \
|
||||
, m_base(base) \
|
||||
, m_this_value(this_value) \
|
||||
, m_property(property) \
|
||||
, m_src(src) \
|
||||
, m_cache_index(cache_index) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
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_this_value); \
|
||||
visitor(m_src); \
|
||||
} \
|
||||
\
|
||||
Operand base() const { return m_base; } \
|
||||
Operand this_value() const { return m_this_value; } \
|
||||
IdentifierTableIndex property() const { return m_property; } \
|
||||
Operand src() const { return m_src; } \
|
||||
u32 cache_index() const { return m_cache_index; } \
|
||||
\
|
||||
private: \
|
||||
Operand m_base; \
|
||||
Operand m_this_value; \
|
||||
IdentifierTableIndex m_property; \
|
||||
Operand m_src; \
|
||||
u32 m_cache_index { 0 }; \
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_ID_WITH_THIS)
|
||||
|
||||
private:
|
||||
Operand m_base;
|
||||
u64 m_property_index;
|
||||
Operand m_src;
|
||||
PropertyKind m_kind;
|
||||
u32 m_cache_index { 0 };
|
||||
Optional<IdentifierTableIndex> m_base_identifier {};
|
||||
};
|
||||
#define DECLARE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS(kind) \
|
||||
class Put##kind##ByNumericIdWithThis final : public Instruction { \
|
||||
public: \
|
||||
Put##kind##ByNumericIdWithThis(Operand base, Operand this_value, u32 property, Operand src, u32 cache_index) \
|
||||
: Instruction(Type::Put##kind##ByNumericIdWithThis) \
|
||||
, m_base(base) \
|
||||
, m_this_value(this_value) \
|
||||
, m_property(property) \
|
||||
, m_src(src) \
|
||||
, m_cache_index(cache_index) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
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_this_value); \
|
||||
visitor(m_src); \
|
||||
} \
|
||||
\
|
||||
Operand base() const { return m_base; } \
|
||||
Operand this_value() const { return m_this_value; } \
|
||||
u32 property() const { return m_property; } \
|
||||
Operand src() const { return m_src; } \
|
||||
u32 cache_index() const { return m_cache_index; } \
|
||||
\
|
||||
private: \
|
||||
Operand m_base; \
|
||||
Operand m_this_value; \
|
||||
u32 m_property; \
|
||||
Operand m_src; \
|
||||
u32 m_cache_index { 0 }; \
|
||||
};
|
||||
|
||||
class PutByIdWithThis final : public Instruction {
|
||||
public:
|
||||
PutByIdWithThis(Operand base, Operand this_value, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index)
|
||||
: Instruction(Type::PutByIdWithThis)
|
||||
, m_base(base)
|
||||
, m_this_value(this_value)
|
||||
, m_property(property)
|
||||
, m_src(src)
|
||||
, m_kind(kind)
|
||||
, m_cache_index(cache_index)
|
||||
{
|
||||
}
|
||||
|
||||
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_this_value);
|
||||
visitor(m_src);
|
||||
}
|
||||
|
||||
Operand base() const { return m_base; }
|
||||
Operand this_value() const { return m_this_value; }
|
||||
IdentifierTableIndex property() const { return m_property; }
|
||||
Operand src() const { return m_src; }
|
||||
PropertyKind kind() const { return m_kind; }
|
||||
u32 cache_index() const { return m_cache_index; }
|
||||
|
||||
private:
|
||||
Operand m_base;
|
||||
Operand m_this_value;
|
||||
IdentifierTableIndex m_property;
|
||||
Operand m_src;
|
||||
PropertyKind m_kind;
|
||||
u32 m_cache_index { 0 };
|
||||
};
|
||||
JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS)
|
||||
|
||||
class PutPrivateById final : public Instruction {
|
||||
public:
|
||||
explicit PutPrivateById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind = PropertyKind::KeyValue)
|
||||
explicit PutPrivateById(Operand base, IdentifierTableIndex property, Operand src)
|
||||
: Instruction(Type::PutPrivateById)
|
||||
, m_base(base)
|
||||
, m_property(property)
|
||||
, m_src(src)
|
||||
, m_kind(kind)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1344,7 +1379,6 @@ private:
|
|||
Operand m_base;
|
||||
IdentifierTableIndex m_property;
|
||||
Operand m_src;
|
||||
PropertyKind m_kind;
|
||||
};
|
||||
|
||||
class DeleteById final : public Instruction {
|
||||
|
@ -1471,75 +1505,75 @@ private:
|
|||
Operand m_this_value;
|
||||
};
|
||||
|
||||
class PutByValue final : public Instruction {
|
||||
public:
|
||||
PutByValue(Operand base, Operand property, Operand src, PropertyKind kind = PropertyKind::KeyValue, Optional<IdentifierTableIndex> base_identifier = {})
|
||||
: Instruction(Type::PutByValue)
|
||||
, m_base(base)
|
||||
, m_property(property)
|
||||
, m_src(src)
|
||||
, m_kind(kind)
|
||||
, m_base_identifier(move(base_identifier))
|
||||
{
|
||||
}
|
||||
#define DECLARE_PUT_KIND_BY_VALUE(kind) \
|
||||
class Put##kind##ByValue final : public Instruction { \
|
||||
public: \
|
||||
Put##kind##ByValue(Operand base, Operand property, Operand src, Optional<IdentifierTableIndex> base_identifier = {}) \
|
||||
: Instruction(Type::Put##kind##ByValue) \
|
||||
, m_base(base) \
|
||||
, m_property(property) \
|
||||
, m_src(src) \
|
||||
, m_base_identifier(move(base_identifier)) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
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_property); \
|
||||
visitor(m_src); \
|
||||
} \
|
||||
\
|
||||
Operand base() const { return m_base; } \
|
||||
Operand property() const { return m_property; } \
|
||||
Operand src() const { return m_src; } \
|
||||
\
|
||||
private: \
|
||||
Operand m_base; \
|
||||
Operand m_property; \
|
||||
Operand m_src; \
|
||||
Optional<IdentifierTableIndex> m_base_identifier; \
|
||||
};
|
||||
|
||||
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_property);
|
||||
visitor(m_src);
|
||||
}
|
||||
JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_VALUE)
|
||||
|
||||
Operand base() const { return m_base; }
|
||||
Operand property() const { return m_property; }
|
||||
Operand src() const { return m_src; }
|
||||
PropertyKind kind() const { return m_kind; }
|
||||
#define DECLARE_PUT_KIND_BY_VALUE_WITH_THIS(kind) \
|
||||
class Put##kind##ByValueWithThis final : public Instruction { \
|
||||
public: \
|
||||
Put##kind##ByValueWithThis(Operand base, Operand property, Operand this_value, Operand src) \
|
||||
: Instruction(Type::Put##kind##ByValueWithThis) \
|
||||
, m_base(base) \
|
||||
, m_property(property) \
|
||||
, m_this_value(this_value) \
|
||||
, 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_property); \
|
||||
visitor(m_this_value); \
|
||||
visitor(m_src); \
|
||||
} \
|
||||
\
|
||||
Operand base() const { return m_base; } \
|
||||
Operand property() const { return m_property; } \
|
||||
Operand this_value() const { return m_this_value; } \
|
||||
Operand src() const { return m_src; } \
|
||||
\
|
||||
private: \
|
||||
Operand m_base; \
|
||||
Operand m_property; \
|
||||
Operand m_this_value; \
|
||||
Operand m_src; \
|
||||
};
|
||||
|
||||
private:
|
||||
Operand m_base;
|
||||
Operand m_property;
|
||||
Operand m_src;
|
||||
PropertyKind m_kind;
|
||||
Optional<IdentifierTableIndex> m_base_identifier;
|
||||
};
|
||||
|
||||
class PutByValueWithThis final : public Instruction {
|
||||
public:
|
||||
PutByValueWithThis(Operand base, Operand property, Operand this_value, Operand src, PropertyKind kind = PropertyKind::KeyValue)
|
||||
: Instruction(Type::PutByValueWithThis)
|
||||
, m_base(base)
|
||||
, m_property(property)
|
||||
, m_this_value(this_value)
|
||||
, m_src(src)
|
||||
, m_kind(kind)
|
||||
{
|
||||
}
|
||||
|
||||
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_property);
|
||||
visitor(m_this_value);
|
||||
visitor(m_src);
|
||||
}
|
||||
|
||||
Operand base() const { return m_base; }
|
||||
Operand property() const { return m_property; }
|
||||
Operand this_value() const { return m_this_value; }
|
||||
Operand src() const { return m_src; }
|
||||
PropertyKind kind() const { return m_kind; }
|
||||
|
||||
private:
|
||||
Operand m_base;
|
||||
Operand m_property;
|
||||
Operand m_this_value;
|
||||
Operand m_src;
|
||||
PropertyKind m_kind;
|
||||
};
|
||||
JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_VALUE_WITH_THIS)
|
||||
|
||||
class DeleteByValue final : public Instruction {
|
||||
public:
|
||||
|
|
27
Libraries/LibJS/Bytecode/PutKind.h
Normal file
27
Libraries/LibJS/Bytecode/PutKind.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
// PutKind indicates how a property is being set.
|
||||
// `Normal` is a normal `o.foo = x` or `o[foo] = x` operation.
|
||||
// The others are used for object expressions.
|
||||
#define JS_ENUMERATE_PUT_KINDS(X) \
|
||||
X(Normal) \
|
||||
X(Getter) \
|
||||
X(Setter) \
|
||||
X(Prototype) \
|
||||
X(Own) // Always sets an own property, never calls a setter.
|
||||
|
||||
enum class PutKind {
|
||||
#define __JS_ENUMERATE_PUT_KIND(name) name,
|
||||
JS_ENUMERATE_PUT_KINDS(__JS_ENUMERATE_PUT_KIND)
|
||||
#undef __JS_ENUMERATE_PUT_KIND
|
||||
};
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@ inline bool Value::to_boolean() const
|
|||
inline ThrowCompletionOr<Value> Value::to_number(VM& vm) const
|
||||
{
|
||||
// OPTIMIZATION: Fast path for when this value is already a number.
|
||||
if (is_number())
|
||||
if (is_number()) [[likely]]
|
||||
return *this;
|
||||
|
||||
return to_number_slow_case(vm);
|
||||
|
@ -35,7 +35,7 @@ inline ThrowCompletionOr<Value> Value::to_number(VM& vm) const
|
|||
inline ThrowCompletionOr<Value> Value::to_numeric(VM& vm) const
|
||||
{
|
||||
// OPTIMIZATION: Fast path for when this value is already a number.
|
||||
if (is_number())
|
||||
if (is_number()) [[likely]]
|
||||
return *this;
|
||||
|
||||
return to_numeric_slow_case(vm);
|
||||
|
@ -43,14 +43,14 @@ inline ThrowCompletionOr<Value> Value::to_numeric(VM& vm) const
|
|||
|
||||
inline ThrowCompletionOr<Value> Value::to_primitive(VM& vm, PreferredType preferred_type) const
|
||||
{
|
||||
if (!is_object())
|
||||
if (!is_object()) [[likely]]
|
||||
return *this;
|
||||
return to_primitive_slow_case(vm, preferred_type);
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<GC::Ref<Object>> Value::to_object(VM& vm) const
|
||||
{
|
||||
if (is_object())
|
||||
if (is_object()) [[likely]]
|
||||
return const_cast<Object&>(as_object());
|
||||
return to_object_slow(vm);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue