diff options
author | Hendiadyoin1 <leon.a@serenityos.org> | 2022-10-01 01:36:06 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-10-17 01:36:41 +0200 |
commit | 490c097bc4b7356127d603f99566039ca453a6a6 (patch) | |
tree | 95e3843989107d07464e4fe49f6a64b1c04bf2c0 /Userland | |
parent | a2ccf31a629ee0bbc51710612b427463aaf44f05 (diff) | |
download | serenity-490c097bc4b7356127d603f99566039ca453a6a6.zip |
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.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 26 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp | 7 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Op.cpp | 19 | ||||
-rw-r--r-- | 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 cda5af2b97..80b9c82b2b 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<String> CallExpression::expression_string() const +{ + if (is<Identifier>(*m_callee)) + return static_cast<Identifier const&>(*m_callee).string(); + + if (is<MemberExpression>(*m_callee)) + return static_cast<MemberExpression const&>(*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<Identifier>(*m_callee) || is<MemberExpression>(*m_callee)) { - String expression_string; - if (is<Identifier>(*m_callee)) { - expression_string = static_cast<Identifier const&>(*m_callee).string(); - } else { - expression_string = static_cast<MemberExpression const&>(*m_callee).to_string_approximation(); - } - return vm.throw_completion<TypeError>(ErrorType::IsNotAEvaluatedFrom, callee_value.to_string_without_side_effects(), call_type, expression_string); - } else { - return vm.throw_completion<TypeError>(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<TypeError>(ErrorType::IsNotAEvaluatedFrom, callee_value.to_string_without_side_effects(), call_type, expression_string.release_value()); + + return vm.throw_completion<TypeError>(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 c8b52f24e0..d06496c16a 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<String> expression_string() const; NonnullRefPtr<Expression> m_callee; Vector<Argument> const m_arguments; diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 3cc90852d2..8f022cf20f 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1548,7 +1548,12 @@ Bytecode::CodeGenerationErrorOr<void> CallExpression::generate_bytecode(Bytecode call_type = Bytecode::Op::Call::CallType::Call; } - generator.emit<Bytecode::Op::Call>(call_type, callee_reg, this_reg); + Optional<Bytecode::StringTableIndex> 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<Bytecode::Op::Call>(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 07adf1fe6c..7efde7e84c 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -584,6 +584,15 @@ static MarkedVector<Value> 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<TypeError>(ErrorType::IsNotAEvaluatedFrom, callee.to_string_without_side_effects(), callee_type, interpreter.current_executable().get_string(m_expression_string->value())); + + return interpreter.vm().throw_completion<TypeError>(ErrorType::IsNotA, callee.to_string_without_side_effects(), callee_type); +} + ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); @@ -591,10 +600,9 @@ ThrowCompletionOr<void> 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<TypeError>(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<TypeError>(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 db951a337e..b4cf413e8a 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<StringTableIndex> 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<StringTableIndex> m_expression_string; }; // NOTE: This instruction is variable-width depending on the number of arguments! |