From 490c097bc4b7356127d603f99566039ca453a6a6 Mon Sep 17 00:00:00 2001 From: Hendiadyoin1 Date: Sat, 1 Oct 2022 01:36:06 +0200 Subject: [PATCH] LibJS: Forward a string aproximation of the CallExpression to Call Ops This gives us better debug output when analysing calls to `undefined` and also fixes multiple test-js cases expecting an `(evaluated from $Expression)` in the error message. This also refactors out the generation of that string, to avoid code duplication with the AST interpreter. --- Userland/Libraries/LibJS/AST.cpp | 26 +++++++++++-------- Userland/Libraries/LibJS/AST.h | 1 + .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 7 ++++- Userland/Libraries/LibJS/Bytecode/Op.cpp | 19 +++++++++++--- Userland/Libraries/LibJS/Bytecode/Op.h | 6 ++++- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index cda5af2b972..80b9c82b2be 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -404,21 +404,25 @@ Completion NewExpression::execute(Interpreter& interpreter) const return Value { TRY(construct(vm, constructor.as_function(), move(arg_list))) }; } +Optional CallExpression::expression_string() const +{ + if (is(*m_callee)) + return static_cast(*m_callee).string(); + + if (is(*m_callee)) + return static_cast(*m_callee).to_string_approximation(); + + return {}; +} + Completion CallExpression::throw_type_error_for_callee(Interpreter& interpreter, Value callee_value, StringView call_type) const { auto& vm = interpreter.vm(); - if (is(*m_callee) || is(*m_callee)) { - String expression_string; - if (is(*m_callee)) { - expression_string = static_cast(*m_callee).string(); - } else { - expression_string = static_cast(*m_callee).to_string_approximation(); - } - return vm.throw_completion(ErrorType::IsNotAEvaluatedFrom, callee_value.to_string_without_side_effects(), call_type, expression_string); - } else { - return vm.throw_completion(ErrorType::IsNotA, callee_value.to_string_without_side_effects(), call_type); - } + if (auto expression_string = this->expression_string(); expression_string.has_value()) + return vm.throw_completion(ErrorType::IsNotAEvaluatedFrom, callee_value.to_string_without_side_effects(), call_type, expression_string.release_value()); + + return vm.throw_completion(ErrorType::IsNotA, callee_value.to_string_without_side_effects(), call_type); } // 13.3.6.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index c8b52f24e0f..d06496c16af 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -1477,6 +1477,7 @@ protected: virtual bool is_call_expression() const override { return true; } Completion throw_type_error_for_callee(Interpreter&, Value callee_value, StringView call_type) const; + Optional expression_string() const; NonnullRefPtr m_callee; Vector const m_arguments; diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 3cc90852d24..8f022cf20f4 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1548,7 +1548,12 @@ Bytecode::CodeGenerationErrorOr CallExpression::generate_bytecode(Bytecode call_type = Bytecode::Op::Call::CallType::Call; } - generator.emit(call_type, callee_reg, this_reg); + 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()); + + generator.emit(call_type, callee_reg, this_reg, expression_string_index); + return {}; } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 07adf1fe6cb..7efde7e84c0 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -584,6 +584,15 @@ static MarkedVector argument_list_evaluation(Bytecode::Interpreter& inter return argument_values; } +Completion Call::throw_type_error_for_callee(Bytecode::Interpreter& interpreter, StringView callee_type) const +{ + auto callee = interpreter.reg(m_callee); + if (m_expression_string.has_value()) + return interpreter.vm().throw_completion(ErrorType::IsNotAEvaluatedFrom, callee.to_string_without_side_effects(), callee_type, interpreter.current_executable().get_string(m_expression_string->value())); + + return interpreter.vm().throw_completion(ErrorType::IsNotA, callee.to_string_without_side_effects(), callee_type); +} + ThrowCompletionOr Call::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); @@ -591,10 +600,9 @@ ThrowCompletionOr Call::execute_impl(Bytecode::Interpreter& interpreter) c auto callee = interpreter.reg(m_callee); if (m_type == CallType::Call && !callee.is_function()) - return vm.throw_completion(ErrorType::IsNotA, callee.to_string_without_side_effects(), "function"sv); - + return throw_type_error_for_callee(interpreter, "function"sv); if (m_type == CallType::Construct && !callee.is_constructor()) - return vm.throw_completion(ErrorType::IsNotA, callee.to_string_without_side_effects(), "constructor"sv); + return throw_type_error_for_callee(interpreter, "constructor"sv); auto& function = callee.as_function(); @@ -1138,8 +1146,11 @@ String JumpUndefined::to_string_impl(Bytecode::Executable const&) const return String::formatted("JumpUndefined undefined:{} not undefined:{}", true_string, false_string); } -String Call::to_string_impl(Bytecode::Executable const&) const +String Call::to_string_impl(Bytecode::Executable const& executable) const { + if (m_expression_string.has_value()) + return String::formatted("Call callee:{}, this:{}, arguments:[...acc] ({})", m_callee, m_this_value, executable.get_string(m_expression_string.value())); + return String::formatted("Call callee:{}, this:{}, arguments:[...acc]", m_callee, m_this_value); } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index db951a337e8..b4cf413e8aa 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -604,11 +604,12 @@ public: Construct, }; - Call(CallType type, Register callee, Register this_value) + Call(CallType type, Register callee, Register this_value, Optional expression_string = {}) : Instruction(Type::Call) , m_callee(callee) , m_this_value(this_value) , m_type(type) + , m_expression_string(expression_string) { } @@ -616,10 +617,13 @@ public: String to_string_impl(Bytecode::Executable const&) const; void replace_references_impl(BasicBlock const&, BasicBlock const&) { } + Completion throw_type_error_for_callee(Bytecode::Interpreter&, StringView callee_type) const; + private: Register m_callee; Register m_this_value; CallType m_type; + Optional m_expression_string; }; // NOTE: This instruction is variable-width depending on the number of arguments!