diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 5ed17e1e1dc..3e4fd051a12 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1712,9 +1712,11 @@ Bytecode::CodeGenerationErrorOr> CallExpression::generat Optional original_callee; auto this_value = generator.add_constant(js_undefined()); + Bytecode::Op::CallType call_type = Bytecode::Op::CallType::Call; if (is(this)) { original_callee = TRY(m_callee->generate_bytecode(generator)).value(); + call_type = Bytecode::Op::CallType::Construct; } else if (is(*m_callee)) { auto& member_expression = static_cast(*m_callee); auto base_and_value = TRY(get_base_and_value_from_member_expression(generator, member_expression)); @@ -1733,6 +1735,9 @@ Bytecode::CodeGenerationErrorOr> CallExpression::generat // NOTE: If the identifier refers to a known "local" or "global", we know it can't be // a `with` binding, so we can skip this. auto& identifier = static_cast(*m_callee); + if (identifier.string() == "eval"sv) { + call_type = Bytecode::Op::CallType::DirectEval; + } if (identifier.is_local()) { auto local = generator.local(identifier.local_variable_index()); if (!generator.is_local_initialized(local.operand().index())) { @@ -1758,15 +1763,6 @@ Bytecode::CodeGenerationErrorOr> CallExpression::generat // to avoid overwriting it while evaluating arguments. auto callee = generator.copy_if_needed_to_preserve_evaluation_order(original_callee.value()); - Bytecode::Op::CallType call_type; - if (is(*this)) { - call_type = Bytecode::Op::CallType::Construct; - } else if (m_callee->is_identifier() && static_cast(*m_callee).string() == "eval"sv) { - call_type = Bytecode::Op::CallType::DirectEval; - } else { - call_type = Bytecode::Op::CallType::Call; - } - Optional expression_string_index; if (auto expression_string = this->expression_string(); expression_string.has_value()) expression_string_index = generator.intern_string(expression_string.release_value()); @@ -1784,15 +1780,41 @@ Bytecode::CodeGenerationErrorOr> CallExpression::generat auto argument_value = TRY(argument.value->generate_bytecode(generator)).value(); argument_operands.append(generator.copy_if_needed_to_preserve_evaluation_order(argument_value)); } - generator.emit_with_extra_operand_slots( - argument_operands.size(), - call_type, - dst, - callee, - this_value, - argument_operands, - expression_string_index, - builtin); + if (builtin.has_value()) { + VERIFY(call_type == Op::CallType::Call); + generator.emit_with_extra_operand_slots( + argument_operands.size(), + dst, + callee, + this_value, + argument_operands, + builtin.value(), + expression_string_index); + } else if (call_type == Op::CallType::Construct) { + generator.emit_with_extra_operand_slots( + argument_operands.size(), + dst, + callee, + this_value, + argument_operands, + expression_string_index); + } else if (call_type == Op::CallType::DirectEval) { + generator.emit_with_extra_operand_slots( + argument_operands.size(), + dst, + callee, + this_value, + argument_operands, + expression_string_index); + } else { + generator.emit_with_extra_operand_slots( + argument_operands.size(), + dst, + callee, + this_value, + argument_operands, + expression_string_index); + } } return dst; diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 5ba0db7bab6..a132e1e82b9 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -25,6 +25,9 @@ O(BitwiseXor) \ O(BlockDeclarationInstantiation) \ O(Call) \ + O(CallBuiltin) \ + O(CallConstruct) \ + O(CallDirectEval) \ O(CallWithArgumentArray) \ O(Catch) \ O(ConcatString) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 5495d5b5dc5..7b2d1457457 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -569,6 +569,9 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) HANDLE_INSTRUCTION(BitwiseXor); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(BlockDeclarationInstantiation); HANDLE_INSTRUCTION(Call); + HANDLE_INSTRUCTION(CallBuiltin); + HANDLE_INSTRUCTION(CallConstruct); + HANDLE_INSTRUCTION(CallDirectEval); HANDLE_INSTRUCTION(CallWithArgumentArray); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(Catch); HANDLE_INSTRUCTION(ConcatString); @@ -2535,20 +2538,57 @@ ThrowCompletionOr Call::execute_impl(Bytecode::Interpreter& interpreter) c { auto callee = interpreter.get(m_callee); - TRY(throw_if_needed_for_call(interpreter, callee, call_type(), expression_string())); + TRY(throw_if_needed_for_call(interpreter, callee, CallType::Call, expression_string())); + + auto argument_values = interpreter.allocate_argument_values(m_argument_count); + for (size_t i = 0; i < m_argument_count; ++i) + argument_values[i] = interpreter.get(m_arguments[i]); + interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), CallType::Call, callee, argument_values))); + return {}; +} + +ThrowCompletionOr CallConstruct::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto callee = interpreter.get(m_callee); + + TRY(throw_if_needed_for_call(interpreter, callee, CallType::Construct, expression_string())); + + auto argument_values = interpreter.allocate_argument_values(m_argument_count); + for (size_t i = 0; i < m_argument_count; ++i) + argument_values[i] = interpreter.get(m_arguments[i]); + interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), CallType::Construct, callee, argument_values))); + return {}; +} + +ThrowCompletionOr CallDirectEval::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto callee = interpreter.get(m_callee); + + TRY(throw_if_needed_for_call(interpreter, callee, CallType::DirectEval, expression_string())); + + auto argument_values = interpreter.allocate_argument_values(m_argument_count); + for (size_t i = 0; i < m_argument_count; ++i) + argument_values[i] = interpreter.get(m_arguments[i]); + interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), CallType::DirectEval, callee, argument_values))); + return {}; +} + +ThrowCompletionOr CallBuiltin::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto callee = interpreter.get(m_callee); + + TRY(throw_if_needed_for_call(interpreter, callee, CallType::Call, expression_string())); + + if (m_argument_count == Bytecode::builtin_argument_count(m_builtin) && callee.is_object() && interpreter.realm().get_builtin_value(m_builtin) == &callee.as_object()) { + interpreter.set(dst(), TRY(dispatch_builtin_call(interpreter, m_builtin, { m_arguments, m_argument_count }))); - if (m_builtin.has_value() - && m_argument_count == Bytecode::builtin_argument_count(m_builtin.value()) - && callee.is_object() - && interpreter.realm().get_builtin_value(m_builtin.value()) == &callee.as_object()) { - interpreter.set(dst(), TRY(dispatch_builtin_call(interpreter, m_builtin.value(), { m_arguments, m_argument_count }))); return {}; } auto argument_values = interpreter.allocate_argument_values(m_argument_count); for (size_t i = 0; i < m_argument_count; ++i) argument_values[i] = interpreter.get(m_arguments[i]); - interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), call_type(), callee, argument_values))); + interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), CallType::Call, callee, argument_values))); return {}; } @@ -3289,21 +3329,67 @@ static StringView call_type_to_string(CallType type) ByteString Call::to_byte_string_impl(Bytecode::Executable const& executable) const { - auto type = call_type_to_string(m_type); - StringBuilder builder; - builder.appendff("Call{} {}, {}, {}, "sv, - type, + builder.appendff("Call {}, {}, {}, "sv, format_operand("dst"sv, m_dst, executable), format_operand("callee"sv, m_callee, executable), format_operand("this"sv, m_this_value, executable)); builder.append(format_operand_list("args"sv, { m_arguments, m_argument_count }, executable)); - if (m_builtin.has_value()) { - builder.appendff(", (builtin:{})", m_builtin.value()); + if (m_expression_string.has_value()) { + builder.appendff(", `{}`", executable.get_string(m_expression_string.value())); } + return builder.to_byte_string(); +} + +ByteString CallConstruct::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + StringBuilder builder; + builder.appendff("CallConstruct {}, {}, {}, "sv, + format_operand("dst"sv, m_dst, executable), + format_operand("callee"sv, m_callee, executable), + format_operand("this"sv, m_this_value, executable)); + + builder.append(format_operand_list("args"sv, { m_arguments, m_argument_count }, executable)); + + if (m_expression_string.has_value()) { + builder.appendff(", `{}`", executable.get_string(m_expression_string.value())); + } + + return builder.to_byte_string(); +} + +ByteString CallDirectEval::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + StringBuilder builder; + builder.appendff("CallDirectEval {}, {}, {}, "sv, + format_operand("dst"sv, m_dst, executable), + format_operand("callee"sv, m_callee, executable), + format_operand("this"sv, m_this_value, executable)); + + builder.append(format_operand_list("args"sv, { m_arguments, m_argument_count }, executable)); + + if (m_expression_string.has_value()) { + builder.appendff(", `{}`", executable.get_string(m_expression_string.value())); + } + + return builder.to_byte_string(); +} + +ByteString CallBuiltin::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + StringBuilder builder; + builder.appendff("CallBuiltin {}, {}, {}, "sv, + format_operand("dst"sv, m_dst, executable), + format_operand("callee"sv, m_callee, executable), + format_operand("this"sv, m_this_value, executable)); + + builder.append(format_operand_list("args"sv, { m_arguments, m_argument_count }, executable)); + + builder.appendff(", (builtin:{})", m_builtin); + if (m_expression_string.has_value()) { builder.appendff(", `{}`", executable.get_string(m_expression_string.value())); } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 85c08aa9143..065abb27cd8 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -1726,14 +1726,12 @@ class Call final : public Instruction { public: static constexpr bool IsVariableLength = true; - Call(CallType type, Operand dst, Operand callee, Operand this_value, ReadonlySpan arguments, Optional expression_string = {}, Optional builtin = {}) + Call(Operand dst, Operand callee, Operand this_value, ReadonlySpan arguments, Optional expression_string = {}) : Instruction(Type::Call) , m_dst(dst) , m_callee(callee) , m_this_value(this_value) , m_argument_count(arguments.size()) - , m_type(type) - , m_builtin(builtin) , m_expression_string(expression_string) { for (size_t i = 0; i < arguments.size(); ++i) @@ -1745,7 +1743,6 @@ public: return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_argument_count); } - CallType call_type() const { return m_type; } Operand dst() const { return m_dst; } Operand callee() const { return m_callee; } Operand this_value() const { return m_this_value; } @@ -1753,8 +1750,6 @@ public: u32 argument_count() const { return m_argument_count; } - Optional const& builtin() const { return m_builtin; } - ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; void visit_operands_impl(Function visitor) @@ -1771,8 +1766,154 @@ private: Operand m_callee; Operand m_this_value; u32 m_argument_count { 0 }; - CallType m_type; - Optional m_builtin; + Optional m_expression_string; + Operand m_arguments[]; +}; + +class CallBuiltin final : public Instruction { +public: + static constexpr bool IsVariableLength = true; + + CallBuiltin(Operand dst, Operand callee, Operand this_value, ReadonlySpan arguments, Builtin builtin, Optional expression_string = {}) + : Instruction(Type::CallBuiltin) + , m_dst(dst) + , m_callee(callee) + , m_this_value(this_value) + , m_argument_count(arguments.size()) + , m_builtin(builtin) + , m_expression_string(expression_string) + { + for (size_t i = 0; i < arguments.size(); ++i) + m_arguments[i] = arguments[i]; + } + + size_t length_impl() const + { + return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_argument_count); + } + + Operand dst() const { return m_dst; } + Operand callee() const { return m_callee; } + Operand this_value() const { return m_this_value; } + Optional const& expression_string() const { return m_expression_string; } + + u32 argument_count() const { return m_argument_count; } + + Builtin const& builtin() const { return m_builtin; } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + void visit_operands_impl(Function visitor) + { + visitor(m_dst); + visitor(m_callee); + visitor(m_this_value); + for (size_t i = 0; i < m_argument_count; i++) + visitor(m_arguments[i]); + } + +private: + Operand m_dst; + Operand m_callee; + Operand m_this_value; + u32 m_argument_count { 0 }; + Builtin m_builtin; + Optional m_expression_string; + Operand m_arguments[]; +}; + +class CallConstruct final : public Instruction { +public: + static constexpr bool IsVariableLength = true; + + CallConstruct(Operand dst, Operand callee, Operand this_value, ReadonlySpan arguments, Optional expression_string = {}) + : Instruction(Type::CallConstruct) + , m_dst(dst) + , m_callee(callee) + , m_this_value(this_value) + , m_argument_count(arguments.size()) + , m_expression_string(expression_string) + { + for (size_t i = 0; i < arguments.size(); ++i) + m_arguments[i] = arguments[i]; + } + + size_t length_impl() const + { + return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_argument_count); + } + + Operand dst() const { return m_dst; } + Operand callee() const { return m_callee; } + Operand this_value() const { return m_this_value; } + Optional const& expression_string() const { return m_expression_string; } + + u32 argument_count() const { return m_argument_count; } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + void visit_operands_impl(Function visitor) + { + visitor(m_dst); + visitor(m_callee); + visitor(m_this_value); + for (size_t i = 0; i < m_argument_count; i++) + visitor(m_arguments[i]); + } + +private: + Operand m_dst; + Operand m_callee; + Operand m_this_value; + u32 m_argument_count { 0 }; + Optional m_expression_string; + Operand m_arguments[]; +}; + +class CallDirectEval final : public Instruction { +public: + static constexpr bool IsVariableLength = true; + + CallDirectEval(Operand dst, Operand callee, Operand this_value, ReadonlySpan arguments, Optional expression_string = {}) + : Instruction(Type::CallDirectEval) + , m_dst(dst) + , m_callee(callee) + , m_this_value(this_value) + , m_argument_count(arguments.size()) + , m_expression_string(expression_string) + { + for (size_t i = 0; i < arguments.size(); ++i) + m_arguments[i] = arguments[i]; + } + + size_t length_impl() const + { + return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_argument_count); + } + + Operand dst() const { return m_dst; } + Operand callee() const { return m_callee; } + Operand this_value() const { return m_this_value; } + Optional const& expression_string() const { return m_expression_string; } + + u32 argument_count() const { return m_argument_count; } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + void visit_operands_impl(Function visitor) + { + visitor(m_dst); + visitor(m_callee); + visitor(m_this_value); + for (size_t i = 0; i < m_argument_count; i++) + visitor(m_arguments[i]); + } + +private: + Operand m_dst; + Operand m_callee; + Operand m_this_value; + u32 m_argument_count { 0 }; Optional m_expression_string; Operand m_arguments[]; };