mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 13:48:12 +00:00
LibJS/JIT: Compile the PutById bytecode instruction
This commit is contained in:
parent
10bf25999c
commit
580249d650
6 changed files with 78 additions and 43 deletions
|
@ -6,7 +6,9 @@
|
|||
|
||||
#include <LibJS/Bytecode/CommonImplementations.h>
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibJS/Bytecode/Op.h>
|
||||
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||
#include <LibJS/Runtime/GlobalEnvironment.h>
|
||||
#include <LibJS/Runtime/ObjectEnvironment.h>
|
||||
|
||||
|
@ -139,4 +141,47 @@ ThrowCompletionOr<Value> get_global(Bytecode::Interpreter& interpreter, Identifi
|
|||
return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, name);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind)
|
||||
{
|
||||
auto object = TRY(base.to_object(vm));
|
||||
if (kind == Op::PropertyKind::Getter || kind == Op::PropertyKind::Setter) {
|
||||
// The generator should only pass us functions for getters and setters.
|
||||
VERIFY(value.is_function());
|
||||
}
|
||||
switch (kind) {
|
||||
case Op::PropertyKind::Getter: {
|
||||
auto& function = value.as_function();
|
||||
if (function.name().is_empty() && is<ECMAScriptFunctionObject>(function))
|
||||
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(DeprecatedString::formatted("get {}", name));
|
||||
object->define_direct_accessor(name, &function, nullptr, Attribute::Configurable | Attribute::Enumerable);
|
||||
break;
|
||||
}
|
||||
case Op::PropertyKind::Setter: {
|
||||
auto& function = value.as_function();
|
||||
if (function.name().is_empty() && is<ECMAScriptFunctionObject>(function))
|
||||
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(DeprecatedString::formatted("set {}", name));
|
||||
object->define_direct_accessor(name, nullptr, &function, Attribute::Configurable | Attribute::Enumerable);
|
||||
break;
|
||||
}
|
||||
case Op::PropertyKind::KeyValue: {
|
||||
bool succeeded = TRY(object->internal_set(name, value, this_value));
|
||||
if (!succeeded && vm.in_strict_mode())
|
||||
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());
|
||||
break;
|
||||
}
|
||||
case Op::PropertyKind::DirectKeyValue:
|
||||
object->define_direct_property(name, value, Attribute::Enumerable | Attribute::Writable | Attribute::Configurable);
|
||||
break;
|
||||
case Op::PropertyKind::Spread:
|
||||
TRY(object->copy_data_properties(vm, value, {}));
|
||||
break;
|
||||
case Op::PropertyKind::ProtoSetter:
|
||||
if (value.is_object() || value.is_null())
|
||||
MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr));
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibJS/Bytecode/IdentifierTable.h>
|
||||
#include <LibJS/Bytecode/Op.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
@ -15,5 +16,6 @@ ThrowCompletionOr<NonnullGCPtr<Object>> base_object_for_get(Bytecode::Interprete
|
|||
ThrowCompletionOr<Value> get_by_id(Bytecode::Interpreter&, IdentifierTableIndex, Value base_value, Value this_value, u32 cache_index);
|
||||
ThrowCompletionOr<Value> get_by_value(Bytecode::Interpreter&, Value base_value, Value property_key_value);
|
||||
ThrowCompletionOr<Value> get_global(Bytecode::Interpreter&, IdentifierTableIndex, u32 cache_index);
|
||||
ThrowCompletionOr<void> put_by_property_key(VM&, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind);
|
||||
|
||||
}
|
||||
|
|
|
@ -479,49 +479,6 @@ DeprecatedString Instruction::to_deprecated_string(Bytecode::Executable const& e
|
|||
|
||||
namespace JS::Bytecode::Op {
|
||||
|
||||
static ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, PropertyKey name, PropertyKind kind)
|
||||
{
|
||||
auto object = TRY(base.to_object(vm));
|
||||
if (kind == PropertyKind::Getter || kind == PropertyKind::Setter) {
|
||||
// The generator should only pass us functions for getters and setters.
|
||||
VERIFY(value.is_function());
|
||||
}
|
||||
switch (kind) {
|
||||
case PropertyKind::Getter: {
|
||||
auto& function = value.as_function();
|
||||
if (function.name().is_empty() && is<ECMAScriptFunctionObject>(function))
|
||||
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(DeprecatedString::formatted("get {}", name));
|
||||
object->define_direct_accessor(name, &function, nullptr, Attribute::Configurable | Attribute::Enumerable);
|
||||
break;
|
||||
}
|
||||
case PropertyKind::Setter: {
|
||||
auto& function = value.as_function();
|
||||
if (function.name().is_empty() && is<ECMAScriptFunctionObject>(function))
|
||||
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(DeprecatedString::formatted("set {}", name));
|
||||
object->define_direct_accessor(name, nullptr, &function, Attribute::Configurable | Attribute::Enumerable);
|
||||
break;
|
||||
}
|
||||
case PropertyKind::KeyValue: {
|
||||
bool succeeded = TRY(object->internal_set(name, value, this_value));
|
||||
if (!succeeded && vm.in_strict_mode())
|
||||
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());
|
||||
break;
|
||||
}
|
||||
case PropertyKind::DirectKeyValue:
|
||||
object->define_direct_property(name, value, Attribute::Enumerable | Attribute::Writable | Attribute::Configurable);
|
||||
break;
|
||||
case PropertyKind::Spread:
|
||||
TRY(object->copy_data_properties(vm, value, {}));
|
||||
break;
|
||||
case PropertyKind::ProtoSetter:
|
||||
if (value.is_object() || value.is_null())
|
||||
MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr));
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> Load::execute_impl(Bytecode::Interpreter&) const
|
||||
{
|
||||
// Handled in the interpreter loop.
|
||||
|
|
|
@ -641,6 +641,10 @@ public:
|
|||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
||||
|
||||
Register base() const { return m_base; }
|
||||
IdentifierTableIndex property() const { return m_property; }
|
||||
PropertyKind kind() const { return m_kind; }
|
||||
|
||||
private:
|
||||
Register m_base;
|
||||
IdentifierTableIndex m_property;
|
||||
|
|
|
@ -459,6 +459,27 @@ void Compiler::compile_resolve_this_binding(Bytecode::Op::ResolveThisBinding con
|
|||
check_exception();
|
||||
}
|
||||
|
||||
static Value cxx_put_by_id(VM& vm, Value base, Bytecode::IdentifierTableIndex property, Value value, Bytecode::Op::PropertyKind kind)
|
||||
{
|
||||
PropertyKey name = vm.bytecode_interpreter().current_executable().get_identifier(property);
|
||||
TRY_OR_SET_EXCEPTION(Bytecode::put_by_property_key(vm, base, base, value, name, kind));
|
||||
return {};
|
||||
}
|
||||
|
||||
void Compiler::compile_put_by_id(Bytecode::Op::PutById const& op)
|
||||
{
|
||||
load_vm_register(ARG1, op.base());
|
||||
m_assembler.mov(
|
||||
Assembler::Operand::Register(ARG2),
|
||||
Assembler::Operand::Imm64(op.property().value()));
|
||||
load_vm_register(ARG3, Bytecode::Register::accumulator());
|
||||
m_assembler.mov(
|
||||
Assembler::Operand::Register(ARG4),
|
||||
Assembler::Operand::Imm64(to_underlying(op.kind())));
|
||||
m_assembler.native_call((void*)cxx_put_by_id);
|
||||
check_exception();
|
||||
}
|
||||
|
||||
OwnPtr<NativeExecutable> Compiler::compile(Bytecode::Executable& bytecode_executable)
|
||||
{
|
||||
if (getenv("LIBJS_NO_JIT"))
|
||||
|
@ -535,6 +556,9 @@ OwnPtr<NativeExecutable> Compiler::compile(Bytecode::Executable& bytecode_execut
|
|||
case Bytecode::Instruction::Type::GetGlobal:
|
||||
compiler.compile_get_global(static_cast<Bytecode::Op::GetGlobal const&>(op));
|
||||
break;
|
||||
case Bytecode::Instruction::Type::PutById:
|
||||
compiler.compile_put_by_id(static_cast<Bytecode::Op::PutById const&>(op));
|
||||
break;
|
||||
case Bytecode::Instruction::Type::ToNumeric:
|
||||
compiler.compile_to_numeric(static_cast<Bytecode::Op::ToNumeric const&>(op));
|
||||
break;
|
||||
|
|
|
@ -24,6 +24,7 @@ private:
|
|||
static constexpr auto ARG1 = Assembler::Reg::RSI;
|
||||
static constexpr auto ARG2 = Assembler::Reg::RDX;
|
||||
static constexpr auto ARG3 = Assembler::Reg::RCX;
|
||||
static constexpr auto ARG4 = Assembler::Reg::R8;
|
||||
static constexpr auto RET = Assembler::Reg::RAX;
|
||||
static constexpr auto STACK_POINTER = Assembler::Reg::RSP;
|
||||
static constexpr auto REGISTER_ARRAY_BASE = Assembler::Reg::R13;
|
||||
|
@ -57,6 +58,8 @@ private:
|
|||
void compile_get_by_value(Bytecode::Op::GetByValue const&);
|
||||
void compile_get_global(Bytecode::Op::GetGlobal const&);
|
||||
|
||||
void compile_put_by_id(Bytecode::Op::PutById const&);
|
||||
|
||||
void store_vm_register(Bytecode::Register, Assembler::Reg);
|
||||
void load_vm_register(Assembler::Reg, Bytecode::Register);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue