diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 8d124774a4..3c145f20b0 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -241,6 +241,10 @@ public: return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Register) * excluded_names_count); } + Register from_object() const { return m_from_object; } + size_t excluded_names_count() const { return m_excluded_names_count; } + Register const* excluded_names() const { return m_excluded_names; } + private: Register m_from_object; size_t m_excluded_names_count { 0 }; diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index bb319f01d2..ca675abbb1 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -1679,6 +1679,44 @@ void Compiler::compile_put_by_value_with_this(Bytecode::Op::PutByValueWithThis c check_exception(); } +static Value cxx_copy_object_excluding_properties(VM& vm, Value from_object, u64 excluded_names_count, Value* excluded_names) +{ + auto& realm = *vm.current_realm(); + auto to_object = Object::create(realm, realm.intrinsics().object_prototype()); + + HashTable excluded_names_table; + for (size_t i = 0; i < excluded_names_count; ++i) { + excluded_names_table.set(TRY_OR_SET_EXCEPTION(excluded_names[i].to_property_key(vm))); + } + TRY_OR_SET_EXCEPTION(to_object->copy_data_properties(vm, from_object, excluded_names_table)); + return to_object; +} + +void Compiler::compile_copy_object_excluding_properties(Bytecode::Op::CopyObjectExcludingProperties const& op) +{ + load_vm_register(ARG1, op.from_object()); + m_assembler.mov( + Assembler::Operand::Register(ARG2), + Assembler::Operand::Imm(op.excluded_names_count())); + + // Build `Value arg3[op.excluded_names_count()] {...}` on the stack. + auto stack_space = align_up_to(op.excluded_names_count() * sizeof(Value), 16); + m_assembler.sub(Assembler::Operand::Register(STACK_POINTER), Assembler::Operand::Imm(stack_space)); + m_assembler.mov(Assembler::Operand::Register(ARG3), Assembler::Operand::Register(STACK_POINTER)); + for (size_t i = 0; i < op.excluded_names_count(); ++i) { + load_vm_register(GPR0, op.excluded_names()[i]); + m_assembler.mov(Assembler::Operand::Mem64BaseAndOffset(ARG3, i * sizeof(Value)), Assembler::Operand::Register(GPR0)); + } + + native_call((void*)cxx_copy_object_excluding_properties); + + // Restore the stack pointer / discard array. + m_assembler.add(Assembler::Operand::Register(STACK_POINTER), Assembler::Operand::Imm(stack_space)); + + store_vm_register(Bytecode::Register::accumulator(), RET); + check_exception(); +} + void Compiler::jump_to_exit() { m_assembler.jump(m_exit_label); diff --git a/Userland/Libraries/LibJS/JIT/Compiler.h b/Userland/Libraries/LibJS/JIT/Compiler.h index 4fa03fadd1..367debf903 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.h +++ b/Userland/Libraries/LibJS/JIT/Compiler.h @@ -138,7 +138,8 @@ private: O(GetMethod, get_method) \ O(GetNewTarget, get_new_target) \ O(HasPrivateId, has_private_id) \ - O(PutByValueWithThis, put_by_value_with_this) + O(PutByValueWithThis, put_by_value_with_this) \ + O(CopyObjectExcludingProperties, copy_object_excluding_properties) # define DECLARE_COMPILE_OP(OpTitleCase, op_snake_case, ...) \ void compile_##op_snake_case(Bytecode::Op::OpTitleCase const&);