LibJS: Add basic monomorphic caching for PutById property access

This patch makes it possible for JS::Object::internal_set() to populate
a CacheablePropertyMetadata, and uses this to implement a basic
monomorphic cache for the most common form of property write access.
This commit is contained in:
Andreas Kling 2023-11-08 20:51:26 +01:00
parent 28118623f5
commit b1b2ca1485
Notes: sideshowbarker 2024-07-17 00:47:29 +09:00
28 changed files with 99 additions and 54 deletions

View file

@ -129,7 +129,7 @@ JS::ThrowCompletionOr<JS::Value> SheetGlobalObject::internal_get(const JS::Prope
return Base::internal_get(property_name, receiver); return Base::internal_get(property_name, receiver);
} }
JS::ThrowCompletionOr<bool> SheetGlobalObject::internal_set(const JS::PropertyKey& property_name, JS::Value value, JS::Value receiver) JS::ThrowCompletionOr<bool> SheetGlobalObject::internal_set(const JS::PropertyKey& property_name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*)
{ {
if (property_name.is_string()) { if (property_name.is_string()) {
if (auto pos = m_sheet.parse_cell_name(property_name.as_string()); pos.has_value()) { if (auto pos = m_sheet.parse_cell_name(property_name.as_string()); pos.has_value()) {

View file

@ -29,7 +29,7 @@ public:
virtual JS::ThrowCompletionOr<bool> internal_has_property(JS::PropertyKey const& name) const override; virtual JS::ThrowCompletionOr<bool> internal_has_property(JS::PropertyKey const& name) const override;
virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*) const override; virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*) const override;
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver) override; virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*) override;
JS_DECLARE_NATIVE_FUNCTION(get_real_cell_contents); JS_DECLARE_NATIVE_FUNCTION(get_real_cell_contents);
JS_DECLARE_NATIVE_FUNCTION(set_real_cell_contents); JS_DECLARE_NATIVE_FUNCTION(set_real_cell_contents);

View file

@ -482,9 +482,9 @@ Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(By
} else if (expression.property().is_identifier()) { } else if (expression.property().is_identifier()) {
auto identifier_table_ref = generator.intern_identifier(verify_cast<Identifier>(expression.property()).string()); auto identifier_table_ref = generator.intern_identifier(verify_cast<Identifier>(expression.property()).string());
if (!lhs_is_super_expression) if (!lhs_is_super_expression)
generator.emit<Bytecode::Op::PutById>(*base_object_register, identifier_table_ref); generator.emit<Bytecode::Op::PutById>(*base_object_register, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
else else
generator.emit<Bytecode::Op::PutByIdWithThis>(*base_object_register, *this_value_register, identifier_table_ref); generator.emit<Bytecode::Op::PutByIdWithThis>(*base_object_register, *this_value_register, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
} else if (expression.property().is_private_identifier()) { } else if (expression.property().is_private_identifier()) {
auto identifier_table_ref = generator.intern_identifier(verify_cast<PrivateIdentifier>(expression.property()).string()); auto identifier_table_ref = generator.intern_identifier(verify_cast<PrivateIdentifier>(expression.property()).string());
generator.emit<Bytecode::Op::PutPrivateById>(*base_object_register, identifier_table_ref); generator.emit<Bytecode::Op::PutPrivateById>(*base_object_register, identifier_table_ref);
@ -985,7 +985,7 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco
TRY(generator.emit_named_evaluation_if_anonymous_function(property->value(), name)); TRY(generator.emit_named_evaluation_if_anonymous_function(property->value(), name));
} }
generator.emit<Bytecode::Op::PutById>(object_reg, key_name, property_kind); generator.emit<Bytecode::Op::PutById>(object_reg, key_name, property_kind, generator.next_property_lookup_cache());
} 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();
@ -1695,7 +1695,7 @@ static void generate_yield(Bytecode::Generator& generator, Bytecode::Label conti
// 5. Return Completion Record { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }. // 5. Return Completion Record { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
generator.emit<Bytecode::Op::LoadImmediate>(Value(to_underlying(Completion::Type::Return))); generator.emit<Bytecode::Op::LoadImmediate>(Value(to_underlying(Completion::Type::Return)));
generator.emit<Bytecode::Op::PutById>(received_completion_register, type_identifier); generator.emit<Bytecode::Op::PutById>(received_completion_register, type_identifier, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { load_completion_and_jump_to_continuation_label_block }); generator.emit<Bytecode::Op::Jump>(Bytecode::Label { load_completion_and_jump_to_continuation_label_block });
generator.switch_to_basic_block(load_completion_and_jump_to_continuation_label_block); generator.switch_to_basic_block(load_completion_and_jump_to_continuation_label_block);
@ -2240,7 +2240,7 @@ Bytecode::CodeGenerationErrorOr<void> TaggedTemplateLiteral::generate_bytecode(B
auto raw_strings_reg = generator.allocate_register(); auto raw_strings_reg = generator.allocate_register();
generator.emit<Bytecode::Op::Store>(raw_strings_reg); generator.emit<Bytecode::Op::Store>(raw_strings_reg);
generator.emit<Bytecode::Op::PutById>(strings_reg, generator.intern_identifier("raw")); generator.emit<Bytecode::Op::PutById>(strings_reg, generator.intern_identifier("raw"), Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined()); generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
auto this_reg = generator.allocate_register(); auto this_reg = generator.allocate_register();

View file

@ -137,7 +137,7 @@ ThrowCompletionOr<Value> get_global(Bytecode::Interpreter& interpreter, Deprecat
return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, identifier); return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, identifier);
} }
ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind) ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind, PropertyLookupCache* cache)
{ {
// Better error message than to_object would give // Better error message than to_object would give
if (vm.in_strict_mode() && base.is_nullish()) if (vm.in_strict_mode() && base.is_nullish())
@ -165,7 +165,22 @@ ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value
break; break;
} }
case Op::PropertyKind::KeyValue: { case Op::PropertyKind::KeyValue: {
bool succeeded = TRY(object->internal_set(name, value, this_value)); if (cache
&& cache->shape == &object->shape()
&& (!object->shape().is_unique() || object->shape().unique_shape_serial_number() == cache->unique_shape_serial_number)) {
object->put_direct(*cache->property_offset, value);
return {};
}
CacheablePropertyMetadata cacheable_metadata;
bool succeeded = TRY(object->internal_set(name, value, this_value, &cacheable_metadata));
if (succeeded && cache && cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) {
cache->shape = object->shape();
cache->property_offset = cacheable_metadata.property_offset.value();
cache->unique_shape_serial_number = object->shape().unique_shape_serial_number();
}
if (!succeeded && vm.in_strict_mode()) { if (!succeeded && vm.in_strict_mode()) {
if (base.is_object()) if (base.is_object())
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects()); return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());

View file

@ -16,7 +16,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> base_object_for_get(VM&, Value base_valu
ThrowCompletionOr<Value> get_by_id(VM&, DeprecatedFlyString const& property, Value base_value, Value this_value, PropertyLookupCache&); ThrowCompletionOr<Value> get_by_id(VM&, DeprecatedFlyString const& property, Value base_value, Value this_value, PropertyLookupCache&);
ThrowCompletionOr<Value> get_by_value(VM&, Value base_value, Value property_key_value); ThrowCompletionOr<Value> get_by_value(VM&, Value base_value, Value property_key_value);
ThrowCompletionOr<Value> get_global(Bytecode::Interpreter&, DeprecatedFlyString const& identifier, GlobalVariableCache&); ThrowCompletionOr<Value> get_global(Bytecode::Interpreter&, DeprecatedFlyString const& identifier, GlobalVariableCache&);
ThrowCompletionOr<void> put_by_property_key(VM&, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind); ThrowCompletionOr<void> put_by_property_key(VM&, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind, PropertyLookupCache* = nullptr);
ThrowCompletionOr<Value> perform_call(Interpreter&, Value this_value, Op::CallType, Value callee, MarkedVector<Value> argument_values); ThrowCompletionOr<Value> perform_call(Interpreter&, Value this_value, Op::CallType, Value callee, MarkedVector<Value> argument_values);
ThrowCompletionOr<void> throw_if_needed_for_call(Interpreter&, Value callee, Op::CallType, Optional<StringTableIndex> const& expression_string); ThrowCompletionOr<void> throw_if_needed_for_call(Interpreter&, Value callee, Op::CallType, Optional<StringTableIndex> const& expression_string);
ThrowCompletionOr<Value> typeof_variable(VM&, DeprecatedFlyString const&); ThrowCompletionOr<Value> typeof_variable(VM&, DeprecatedFlyString const&);

View file

@ -297,7 +297,7 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(JS::ASTNode const
} else { } else {
// 3. Let propertyKey be StringValue of IdentifierName. // 3. Let propertyKey be StringValue of IdentifierName.
auto identifier_table_ref = intern_identifier(verify_cast<Identifier>(expression.property()).string()); auto identifier_table_ref = intern_identifier(verify_cast<Identifier>(expression.property()).string());
emit<Bytecode::Op::PutByIdWithThis>(super_reference.base, super_reference.this_value, identifier_table_ref); emit<Bytecode::Op::PutByIdWithThis>(super_reference.base, super_reference.this_value, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache());
} }
} else { } else {
TRY(expression.object().generate_bytecode(*this)); TRY(expression.object().generate_bytecode(*this));
@ -314,7 +314,7 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(JS::ASTNode const
} else if (expression.property().is_identifier()) { } else if (expression.property().is_identifier()) {
emit<Bytecode::Op::Load>(value_reg); emit<Bytecode::Op::Load>(value_reg);
auto identifier_table_ref = intern_identifier(verify_cast<Identifier>(expression.property()).string()); auto identifier_table_ref = intern_identifier(verify_cast<Identifier>(expression.property()).string());
emit<Bytecode::Op::PutById>(object_reg, identifier_table_ref); emit<Bytecode::Op::PutById>(object_reg, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache());
} else if (expression.property().is_private_identifier()) { } else if (expression.property().is_private_identifier()) {
emit<Bytecode::Op::Load>(value_reg); emit<Bytecode::Op::Load>(value_reg);
auto identifier_table_ref = intern_identifier(verify_cast<PrivateIdentifier>(expression.property()).string()); auto identifier_table_ref = intern_identifier(verify_cast<PrivateIdentifier>(expression.property()).string());

View file

@ -236,6 +236,7 @@ public:
[[nodiscard]] size_t next_global_variable_cache() { return m_next_global_variable_cache++; } [[nodiscard]] size_t next_global_variable_cache() { return m_next_global_variable_cache++; }
[[nodiscard]] size_t next_environment_variable_cache() { return m_next_environment_variable_cache++; } [[nodiscard]] size_t next_environment_variable_cache() { return m_next_environment_variable_cache++; }
[[nodiscard]] size_t next_property_lookup_cache() { return m_next_property_lookup_cache++; }
private: private:
enum class JumpType { enum class JumpType {

View file

@ -811,7 +811,8 @@ ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter
auto value = interpreter.accumulator(); auto value = interpreter.accumulator();
auto base = interpreter.reg(m_base); auto base = interpreter.reg(m_base);
PropertyKey name = interpreter.current_executable().get_identifier(m_property); PropertyKey name = interpreter.current_executable().get_identifier(m_property);
TRY(put_by_property_key(vm, base, base, value, name, m_kind)); auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
TRY(put_by_property_key(vm, base, base, value, name, m_kind, &cache));
interpreter.accumulator() = value; interpreter.accumulator() = value;
return {}; return {};
} }
@ -823,7 +824,8 @@ ThrowCompletionOr<void> PutByIdWithThis::execute_impl(Bytecode::Interpreter& int
auto value = interpreter.accumulator(); auto value = interpreter.accumulator();
auto base = interpreter.reg(m_base); auto base = interpreter.reg(m_base);
PropertyKey name = interpreter.current_executable().get_identifier(m_property); PropertyKey name = interpreter.current_executable().get_identifier(m_property);
TRY(put_by_property_key(vm, base, interpreter.reg(m_this_value), value, name, m_kind)); auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
TRY(put_by_property_key(vm, base, interpreter.reg(m_this_value), value, name, m_kind, &cache));
interpreter.accumulator() = value; interpreter.accumulator() = value;
return {}; return {};
} }

View file

@ -668,11 +668,12 @@ enum class PropertyKind {
class PutById final : public Instruction { class PutById final : public Instruction {
public: public:
explicit PutById(Register base, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue) explicit PutById(Register base, IdentifierTableIndex property, PropertyKind kind, u32 cache_index)
: Instruction(Type::PutById, sizeof(*this)) : Instruction(Type::PutById, sizeof(*this))
, m_base(base) , m_base(base)
, m_property(property) , m_property(property)
, m_kind(kind) , m_kind(kind)
, m_cache_index(cache_index)
{ {
} }
@ -682,21 +683,24 @@ public:
Register base() const { return m_base; } Register base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; } IdentifierTableIndex property() const { return m_property; }
PropertyKind kind() const { return m_kind; } PropertyKind kind() const { return m_kind; }
u32 cache_index() const { return m_cache_index; }
private: private:
Register m_base; Register m_base;
IdentifierTableIndex m_property; IdentifierTableIndex m_property;
PropertyKind m_kind; PropertyKind m_kind;
u32 m_cache_index { 0 };
}; };
class PutByIdWithThis final : public Instruction { class PutByIdWithThis final : public Instruction {
public: public:
PutByIdWithThis(Register base, Register this_value, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue) PutByIdWithThis(Register base, Register this_value, IdentifierTableIndex property, PropertyKind kind, u32 cache_index)
: Instruction(Type::PutByIdWithThis, sizeof(*this)) : Instruction(Type::PutByIdWithThis, sizeof(*this))
, m_base(base) , m_base(base)
, m_this_value(this_value) , m_this_value(this_value)
, m_property(property) , m_property(property)
, m_kind(kind) , m_kind(kind)
, m_cache_index(cache_index)
{ {
} }
@ -707,12 +711,14 @@ public:
Register this_value() const { return m_this_value; } Register this_value() const { return m_this_value; }
IdentifierTableIndex property() const { return m_property; } IdentifierTableIndex property() const { return m_property; }
PropertyKind kind() const { return m_kind; } PropertyKind kind() const { return m_kind; }
u32 cache_index() const { return m_cache_index; }
private: private:
Register m_base; Register m_base;
Register m_this_value; Register m_this_value;
IdentifierTableIndex m_property; IdentifierTableIndex m_property;
PropertyKind m_kind; PropertyKind m_kind;
u32 m_cache_index { 0 };
}; };
class PutPrivateById final : public Instruction { class PutPrivateById final : public Instruction {

View file

@ -1341,10 +1341,9 @@ void Compiler::compile_resolve_this_binding(Bytecode::Op::ResolveThisBinding con
end.link(m_assembler); end.link(m_assembler);
} }
static Value cxx_put_by_id(VM& vm, Value base, Bytecode::IdentifierTableIndex property, Value value, Bytecode::Op::PropertyKind kind) static Value cxx_put_by_id(VM& vm, Value base, DeprecatedFlyString const& property, Value value, Bytecode::Op::PropertyKind kind, Bytecode::PropertyLookupCache& cache)
{ {
PropertyKey name = vm.bytecode_interpreter().current_executable().get_identifier(property); TRY_OR_SET_EXCEPTION(Bytecode::put_by_property_key(vm, base, base, value, property, kind, &cache));
TRY_OR_SET_EXCEPTION(Bytecode::put_by_property_key(vm, base, base, value, name, kind));
return value; return value;
} }
@ -1353,11 +1352,14 @@ void Compiler::compile_put_by_id(Bytecode::Op::PutById const& op)
load_vm_register(ARG1, op.base()); load_vm_register(ARG1, op.base());
m_assembler.mov( m_assembler.mov(
Assembler::Operand::Register(ARG2), Assembler::Operand::Register(ARG2),
Assembler::Operand::Imm(op.property().value())); Assembler::Operand::Imm(bit_cast<u64>(&m_bytecode_executable.get_identifier(op.property()))));
load_accumulator(ARG3); load_accumulator(ARG3);
m_assembler.mov( m_assembler.mov(
Assembler::Operand::Register(ARG4), Assembler::Operand::Register(ARG4),
Assembler::Operand::Imm(to_underlying(op.kind()))); Assembler::Operand::Imm(to_underlying(op.kind())));
m_assembler.mov(
Assembler::Operand::Register(ARG5),
Assembler::Operand::Imm(bit_cast<u64>(&m_bytecode_executable.property_lookup_caches[op.cache_index()])));
native_call((void*)cxx_put_by_id); native_call((void*)cxx_put_by_id);
store_accumulator(RET); store_accumulator(RET);
check_exception(); check_exception();
@ -1896,9 +1898,9 @@ void Compiler::compile_delete_by_id_with_this(Bytecode::Op::DeleteByIdWithThis c
store_accumulator(RET); store_accumulator(RET);
} }
static Value cxx_put_by_id_with_this(VM& vm, Value base, Value value, DeprecatedFlyString const& name, Value this_value, Bytecode::Op::PropertyKind kind) static Value cxx_put_by_id_with_this(VM& vm, Value base, Value value, DeprecatedFlyString const& name, Value this_value, Bytecode::Op::PropertyKind kind, Bytecode::PropertyLookupCache& cache)
{ {
TRY_OR_SET_EXCEPTION(Bytecode::put_by_property_key(vm, base, this_value, value, name, kind)); TRY_OR_SET_EXCEPTION(Bytecode::put_by_property_key(vm, base, this_value, value, name, kind, &cache));
return {}; return {};
} }
@ -1913,7 +1915,10 @@ void Compiler::compile_put_by_id_with_this(Bytecode::Op::PutByIdWithThis const&
m_assembler.mov( m_assembler.mov(
Assembler::Operand::Register(ARG5), Assembler::Operand::Register(ARG5),
Assembler::Operand::Imm(to_underlying(op.kind()))); Assembler::Operand::Imm(to_underlying(op.kind())));
native_call((void*)cxx_put_by_id_with_this); m_assembler.mov(
Assembler::Operand::Register(GPR0),
Assembler::Operand::Imm(bit_cast<u64>(&m_bytecode_executable.property_lookup_caches[op.cache_index()])));
native_call((void*)cxx_put_by_id_with_this, { Assembler::Operand::Register(GPR0) });
check_exception(); check_exception();
} }

View file

@ -52,7 +52,7 @@ ThrowCompletionOr<Value> ArgumentsObject::internal_get(PropertyKey const& proper
} }
// 10.4.4.4 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver // 10.4.4.4 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver
ThrowCompletionOr<bool> ArgumentsObject::internal_set(PropertyKey const& property_key, Value value, Value receiver) ThrowCompletionOr<bool> ArgumentsObject::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*)
{ {
bool is_mapped = false; bool is_mapped = false;

View file

@ -24,7 +24,7 @@ public:
virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const&) const override; virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const&) const override;
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override; virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override;
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata*) const override; virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata*) const override;
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver) override; virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata*) override;
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override; virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
virtual bool may_interfere_with_indexed_property_access() const final { return true; } virtual bool may_interfere_with_indexed_property_access() const final { return true; }

View file

@ -1153,6 +1153,8 @@ template void async_function_start(VM&, PromiseCapability const&, NonnullRefPtr<
template void async_block_start(VM&, SafeFunction<Completion()> const& async_body, PromiseCapability const&, ExecutionContext&); template void async_block_start(VM&, SafeFunction<Completion()> const& async_body, PromiseCapability const&, ExecutionContext&);
template void async_function_start(VM&, PromiseCapability const&, SafeFunction<Completion()> const& async_function_body); template void async_function_start(VM&, PromiseCapability const&, SafeFunction<Completion()> const& async_function_body);
static HashMap<NonnullRefPtr<Statement const>, RefPtr<Bytecode::Executable>> executable_cache;
// 10.2.1.4 OrdinaryCallEvaluateBody ( F, argumentsList ), https://tc39.es/ecma262/#sec-ordinarycallevaluatebody // 10.2.1.4 OrdinaryCallEvaluateBody ( F, argumentsList ), https://tc39.es/ecma262/#sec-ordinarycallevaluatebody
// 15.8.4 Runtime Semantics: EvaluateAsyncFunctionBody, https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody // 15.8.4 Runtime Semantics: EvaluateAsyncFunctionBody, https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody
Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
@ -1166,8 +1168,11 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
// This is why FunctionDeclarationInstantiation is invoked in the middle. // This is why FunctionDeclarationInstantiation is invoked in the middle.
// The issue is that FunctionDeclarationInstantiation may mark certain functions as hoisted // The issue is that FunctionDeclarationInstantiation may mark certain functions as hoisted
// per Annex B. This affects code generation for FunctionDeclaration nodes. // per Annex B. This affects code generation for FunctionDeclaration nodes.
if (!m_bytecode_executable) { if (!m_bytecode_executable) {
auto maybe_cached_executable = executable_cache.get(m_ecmascript_code);
if (maybe_cached_executable.has_value()) {
m_bytecode_executable = maybe_cached_executable.value();
} else {
size_t default_parameter_index = 0; size_t default_parameter_index = 0;
for (auto& parameter : m_formal_parameters) { for (auto& parameter : m_formal_parameters) {
if (!parameter.default_value) if (!parameter.default_value)
@ -1176,6 +1181,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
m_default_parameter_bytecode_executables.append(move(executable)); m_default_parameter_bytecode_executables.append(move(executable));
} }
} }
}
auto declaration_result = function_declaration_instantiation(); auto declaration_result = function_declaration_instantiation();
@ -1184,8 +1190,10 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
return declaration_result.release_error(); return declaration_result.release_error();
} }
if (!m_bytecode_executable) if (!m_bytecode_executable) {
m_bytecode_executable = TRY(Bytecode::compile(vm, *m_ecmascript_code, m_kind, m_name)); m_bytecode_executable = TRY(Bytecode::compile(vm, *m_ecmascript_code, m_kind, m_name));
executable_cache.set(m_ecmascript_code, m_bytecode_executable);
}
if (m_kind == FunctionKind::Async) { if (m_kind == FunctionKind::Async) {
if (declaration_result.is_throw_completion()) { if (declaration_result.is_throw_completion()) {

View file

@ -182,7 +182,7 @@ ThrowCompletionOr<Value> ModuleNamespaceObject::internal_get(PropertyKey const&
} }
// 10.4.6.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-set-p-v-receiver // 10.4.6.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-set-p-v-receiver
ThrowCompletionOr<bool> ModuleNamespaceObject::internal_set(PropertyKey const&, Value, Value) ThrowCompletionOr<bool> ModuleNamespaceObject::internal_set(PropertyKey const&, Value, Value, CacheablePropertyMetadata*)
{ {
// 1. Return false. // 1. Return false.
return false; return false;

View file

@ -26,7 +26,7 @@ public:
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override; virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override;
virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const override; virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const override;
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata* = nullptr) const override; virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata* = nullptr) const override;
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver) override; virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata*) override;
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override; virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
virtual ThrowCompletionOr<MarkedVector<Value>> internal_own_property_keys() const override; virtual ThrowCompletionOr<MarkedVector<Value>> internal_own_property_keys() const override;
virtual void initialize(Realm&) override; virtual void initialize(Realm&) override;

View file

@ -887,7 +887,7 @@ ThrowCompletionOr<Value> Object::internal_get(PropertyKey const& property_key, V
} }
// 10.1.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver // 10.1.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver
ThrowCompletionOr<bool> Object::internal_set(PropertyKey const& property_key, Value value, Value receiver) ThrowCompletionOr<bool> Object::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata* cacheable_metadata)
{ {
VERIFY(property_key.is_valid()); VERIFY(property_key.is_valid());
VERIFY(!value.is_empty()); VERIFY(!value.is_empty());
@ -897,11 +897,11 @@ ThrowCompletionOr<bool> Object::internal_set(PropertyKey const& property_key, Va
auto own_descriptor = TRY(internal_get_own_property(property_key)); auto own_descriptor = TRY(internal_get_own_property(property_key));
// 3. Return ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc). // 3. Return ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).
return ordinary_set_with_own_descriptor(property_key, value, receiver, own_descriptor); return ordinary_set_with_own_descriptor(property_key, value, receiver, own_descriptor, cacheable_metadata);
} }
// 10.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc ), https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor // 10.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc ), https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor
ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey const& property_key, Value value, Value receiver, Optional<PropertyDescriptor> own_descriptor) ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey const& property_key, Value value, Value receiver, Optional<PropertyDescriptor> own_descriptor, CacheablePropertyMetadata* cacheable_metadata)
{ {
VERIFY(property_key.is_valid()); VERIFY(property_key.is_valid());
VERIFY(!value.is_empty()); VERIFY(!value.is_empty());
@ -957,6 +957,13 @@ ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey con
// iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }. // iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }.
auto value_descriptor = PropertyDescriptor { .value = value }; auto value_descriptor = PropertyDescriptor { .value = value };
if (cacheable_metadata && own_descriptor.has_value() && own_descriptor->property_offset.has_value()) {
*cacheable_metadata = CacheablePropertyMetadata {
.type = CacheablePropertyMetadata::Type::OwnProperty,
.property_offset = own_descriptor->property_offset.value(),
};
}
// iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc). // iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
return TRY(receiver.as_object().internal_define_own_property(property_key, value_descriptor)); return TRY(receiver.as_object().internal_define_own_property(property_key, value_descriptor));
} }

View file

@ -131,7 +131,7 @@ public:
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&); virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&);
virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const; virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const;
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata* = nullptr) const; virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata* = nullptr) const;
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver); virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata* = nullptr);
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&); virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&);
virtual ThrowCompletionOr<MarkedVector<Value>> internal_own_property_keys() const; virtual ThrowCompletionOr<MarkedVector<Value>> internal_own_property_keys() const;
@ -141,7 +141,7 @@ public:
// might not hold when property access behaves differently. // might not hold when property access behaves differently.
virtual bool may_interfere_with_indexed_property_access() const { return false; } virtual bool may_interfere_with_indexed_property_access() const { return false; }
ThrowCompletionOr<bool> ordinary_set_with_own_descriptor(PropertyKey const&, Value, Value, Optional<PropertyDescriptor>); ThrowCompletionOr<bool> ordinary_set_with_own_descriptor(PropertyKey const&, Value, Value, Optional<PropertyDescriptor>, CacheablePropertyMetadata* = nullptr);
// 10.4.7 Immutable Prototype Exotic Objects, https://tc39.es/ecma262/#sec-immutable-prototype-exotic-objects // 10.4.7 Immutable Prototype Exotic Objects, https://tc39.es/ecma262/#sec-immutable-prototype-exotic-objects
@ -193,6 +193,7 @@ public:
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;
Value get_direct(size_t index) const { return m_storage[index]; } Value get_direct(size_t index) const { return m_storage[index]; }
void put_direct(size_t index, Value value) { m_storage[index] = value; }
IndexedProperties const& indexed_properties() const { return m_indexed_properties; } IndexedProperties const& indexed_properties() const { return m_indexed_properties; }
IndexedProperties& indexed_properties() { return m_indexed_properties; } IndexedProperties& indexed_properties() { return m_indexed_properties; }

View file

@ -525,7 +525,7 @@ ThrowCompletionOr<Value> ProxyObject::internal_get(PropertyKey const& property_k
} }
// 10.5.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver // 10.5.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
ThrowCompletionOr<bool> ProxyObject::internal_set(PropertyKey const& property_key, Value value, Value receiver) ThrowCompletionOr<bool> ProxyObject::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*)
{ {
auto& vm = this->vm(); auto& vm = this->vm();

View file

@ -39,7 +39,7 @@ public:
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override; virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override;
virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const override; virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const override;
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata*) const override; virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata*) const override;
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver) override; virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata*) override;
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override; virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
virtual ThrowCompletionOr<MarkedVector<Value>> internal_own_property_keys() const override; virtual ThrowCompletionOr<MarkedVector<Value>> internal_own_property_keys() const override;
virtual ThrowCompletionOr<Value> internal_call(Value this_argument, MarkedVector<Value> arguments_list) override; virtual ThrowCompletionOr<Value> internal_call(Value this_argument, MarkedVector<Value> arguments_list) override;

View file

@ -314,7 +314,7 @@ public:
} }
// 10.4.5.5 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver // 10.4.5.5 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const& property_key, Value value, Value receiver) override virtual ThrowCompletionOr<bool> internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*) override
{ {
VERIFY(!value.is_empty()); VERIFY(!value.is_empty());
VERIFY(!receiver.is_empty()); VERIFY(!receiver.is_empty());

View file

@ -156,7 +156,7 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> LegacyPlatformObject::in
} }
// https://webidl.spec.whatwg.org/#legacy-platform-object-set // https://webidl.spec.whatwg.org/#legacy-platform-object-set
JS::ThrowCompletionOr<bool> LegacyPlatformObject::internal_set(JS::PropertyKey const& property_name, JS::Value value, JS::Value receiver) JS::ThrowCompletionOr<bool> LegacyPlatformObject::internal_set(JS::PropertyKey const& property_name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*)
{ {
auto& vm = this->vm(); auto& vm = this->vm();

View file

@ -23,7 +23,7 @@ public:
virtual ~LegacyPlatformObject() override; virtual ~LegacyPlatformObject() override;
virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override; virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override;
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value, JS::Value) override; virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value, JS::Value, JS::CacheablePropertyMetadata*) override;
virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor const&) override; virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor const&) override;
virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override; virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override;
virtual JS::ThrowCompletionOr<bool> internal_prevent_extensions() override; virtual JS::ThrowCompletionOr<bool> internal_prevent_extensions() override;

View file

@ -419,14 +419,14 @@ JS::ThrowCompletionOr<JS::Value> CSSStyleDeclaration::internal_get(JS::PropertyK
return { JS::PrimitiveString::create(vm(), String {}) }; return { JS::PrimitiveString::create(vm(), String {}) };
} }
JS::ThrowCompletionOr<bool> CSSStyleDeclaration::internal_set(JS::PropertyKey const& name, JS::Value value, JS::Value receiver) JS::ThrowCompletionOr<bool> CSSStyleDeclaration::internal_set(JS::PropertyKey const& name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata)
{ {
auto& vm = this->vm(); auto& vm = this->vm();
if (!name.is_string()) if (!name.is_string())
return Base::internal_set(name, value, receiver); return Base::internal_set(name, value, receiver, cacheable_metadata);
auto property_id = property_id_from_name(name.to_string()); auto property_id = property_id_from_name(name.to_string());
if (property_id == CSS::PropertyID::Invalid) if (property_id == CSS::PropertyID::Invalid)
return Base::internal_set(name, value, receiver); return Base::internal_set(name, value, receiver, cacheable_metadata);
auto css_text = TRY(value.to_deprecated_string(vm)); auto css_text = TRY(value.to_deprecated_string(vm));

View file

@ -42,7 +42,7 @@ public:
virtual JS::ThrowCompletionOr<bool> internal_has_property(JS::PropertyKey const& name) const override; virtual JS::ThrowCompletionOr<bool> internal_has_property(JS::PropertyKey const& name) const override;
virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*) const override; virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*) const override;
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver) override; virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*) override;
protected: protected:
explicit CSSStyleDeclaration(JS::Realm&); explicit CSSStyleDeclaration(JS::Realm&);

View file

@ -507,13 +507,13 @@ JS::ThrowCompletionOr<JS::Value> Location::internal_get(JS::PropertyKey const& p
} }
// 7.10.5.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-set // 7.10.5.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-set
JS::ThrowCompletionOr<bool> Location::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver) JS::ThrowCompletionOr<bool> Location::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata)
{ {
auto& vm = this->vm(); auto& vm = this->vm();
// 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinarySet(this, P, V, Receiver). // 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinarySet(this, P, V, Receiver).
if (HTML::is_platform_object_same_origin(*this)) if (HTML::is_platform_object_same_origin(*this))
return JS::Object::internal_set(property_key, value, receiver); return JS::Object::internal_set(property_key, value, receiver, cacheable_metadata);
// 2. Return ? CrossOriginSet(this, P, V, Receiver). // 2. Return ? CrossOriginSet(this, P, V, Receiver).
return HTML::cross_origin_set(vm, static_cast<JS::Object&>(*this), property_key, value, receiver); return HTML::cross_origin_set(vm, static_cast<JS::Object&>(*this), property_key, value, receiver);

View file

@ -60,7 +60,7 @@ public:
virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override; virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override;
virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor const&) override; virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor const&) override;
virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*) const override; virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*) const override;
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver) override; virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*) override;
virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override; virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override;
virtual JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> internal_own_property_keys() const override; virtual JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> internal_own_property_keys() const override;

View file

@ -169,7 +169,7 @@ JS::ThrowCompletionOr<JS::Value> WindowProxy::internal_get(JS::PropertyKey const
} }
// 7.4.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set // 7.4.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set
JS::ThrowCompletionOr<bool> WindowProxy::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver) JS::ThrowCompletionOr<bool> WindowProxy::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*)
{ {
auto& vm = this->vm(); auto& vm = this->vm();

View file

@ -27,7 +27,7 @@ public:
virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override; virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override;
virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor const&) override; virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor const&) override;
virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*) const override; virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*) const override;
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver) override; virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*) override;
virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override; virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override;
virtual JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> internal_own_property_keys() const override; virtual JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> internal_own_property_keys() const override;