From 71fc7ac7ac28156edc7609b8c4bb39198e27202d Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 2 Jul 2021 19:30:38 +0200 Subject: [PATCH] LibJS: Make SuperCall a proper AST node and clean up evaluation --- Userland/Libraries/LibJS/AST.cpp | 75 +++++++++++++++++++++-------- Userland/Libraries/LibJS/AST.h | 15 ++++++ Userland/Libraries/LibJS/Parser.cpp | 6 ++- 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index dff0d4da50a..d054b2d24a8 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -115,13 +115,6 @@ CallExpression::ThisAndCallee CallExpression::compute_this_and_callee(Interprete { auto& vm = interpreter.vm(); - if (is(*m_callee)) { - // If we are calling super, |this| has not been initialized yet, and would not be meaningful to provide. - auto new_target = vm.get_new_target(); - VERIFY(new_target.is_function()); - return { js_undefined(), new_target }; - } - if (is(*m_callee)) { auto& member_expression = static_cast(*m_callee); Value callee; @@ -253,27 +246,63 @@ Value CallExpression::execute(Interpreter& interpreter, GlobalObject& global_obj return perform_eval(script_value, global_object, vm.in_strict_mode() ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct); } - Value result; + return vm.call(function, this_value, move(arg_list)); +} - if (!is(*m_callee)) - return vm.call(function, this_value, move(arg_list)); +// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation +// SuperCall : super Arguments +Value SuperCall::execute(Interpreter& interpreter, GlobalObject& global_object) const +{ + InterpreterNodeScope node_scope { interpreter, *this }; + auto& vm = interpreter.vm(); - auto* super_constructor = get_super_constructor(interpreter.vm()); - // FIXME: Functions should track their constructor kind. - if (!super_constructor || !super_constructor->is_function()) { + // 1. Let newTarget be GetNewTarget(). + auto new_target = vm.get_new_target(); + if (vm.exception()) + return {}; + + // 2. Assert: Type(newTarget) is Object. + VERIFY(new_target.is_function()); + + // 3. Let func be ! GetSuperConstructor(). + auto* func = get_super_constructor(interpreter.vm()); + VERIFY(!vm.exception()); + + // 4. Let argList be ? ArgumentListEvaluation of Arguments. + MarkedValueList arg_list(vm.heap()); + argument_list_evaluation(interpreter, global_object, m_arguments, arg_list); + if (interpreter.exception()) + return {}; + + // 5. If IsConstructor(func) is false, throw a TypeError exception. + // FIXME: This check is non-conforming. + if (!func || !func->is_function()) { vm.throw_exception(global_object, ErrorType::NotAConstructor, "Super constructor"); return {}; } - result = vm.construct(static_cast(*super_constructor), function, move(arg_list)); + + // 6. Let result be ? Construct(func, argList, newTarget). + auto& function = new_target.as_function(); + auto result = vm.construct(static_cast(*func), function, move(arg_list)); if (vm.exception()) return {}; - auto& this_er = get_this_environment(interpreter.vm()); - verify_cast(this_er).bind_this_value(global_object, result); + // 7. Let thisER be GetThisEnvironment(). + auto& this_er = verify_cast(get_this_environment(interpreter.vm())); + // 8. Perform ? thisER.BindThisValue(result). + this_er.bind_this_value(global_object, result); if (vm.exception()) return {}; + // 9. Let F be thisER.[[FunctionObject]]. + // 10. Assert: F is an ECMAScript function object. (NOTE: This is implied by the strong C++ type.) + [[maybe_unused]] auto& f = this_er.function_object(); + + // 11. Perform ? InitializeInstanceElements(result, F). + // FIXME: This is missing here. + + // 12. Return result. return result; } @@ -755,11 +784,9 @@ Value UnaryExpression::execute(Interpreter& interpreter, GlobalObject& global_ob VERIFY_NOT_REACHED(); } -Value SuperExpression::execute(Interpreter& interpreter, GlobalObject&) const +Value SuperExpression::execute(Interpreter&, GlobalObject&) const { - InterpreterNodeScope node_scope { interpreter, *this }; - - // The semantics for SuperExpressions are handled in CallExpression::compute_this_and_callee() + // The semantics for SuperExpression are handled in CallExpression and SuperCall. VERIFY_NOT_REACHED(); } @@ -1055,6 +1082,14 @@ void CallExpression::dump(int indent) const argument.value->dump(indent + 1); } +void SuperCall::dump(int indent) const +{ + print_indent(indent); + outln("SuperCall"); + for (auto& argument : m_arguments) + argument.value->dump(indent + 1); +} + void ClassDeclaration::dump(int indent) const { ASTNode::dump(indent); diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 3c7c3efe19c..e229900e035 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -951,6 +951,21 @@ public: virtual bool is_new_expression() const override { return true; } }; +class SuperCall final : public Expression { +public: + SuperCall(SourceRange source_range, Vector arguments) + : Expression(source_range) + , m_arguments(move(arguments)) + { + } + + virtual Value execute(Interpreter&, GlobalObject&) const override; + virtual void dump(int indent) const override; + +private: + Vector const m_arguments; +}; + enum class AssignmentOp { Assignment, AdditionAssignment, diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 63c86d214fd..24c8d30c5d0 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -615,9 +615,8 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ if (!super_class.is_null()) { // Set constructor to the result of parsing the source text // constructor(... args){ super (...args);} - auto super_call = create_ast_node( + auto super_call = create_ast_node( { m_state.current_token.filename(), rule_start.position(), position() }, - create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }), Vector { CallExpression::Argument { create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, "args"), true } }); constructor_body->append(create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(super_call))); constructor_body->add_variables(m_state.var_scopes.last()); @@ -1288,6 +1287,9 @@ NonnullRefPtr Parser::parse_call_expression(NonnullRefPtr(*lhs)) + return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(arguments)); + return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), move(arguments)); }