Browse Source

LibJS: Make SuperCall a proper AST node and clean up evaluation

Andreas Kling 4 năm trước cách đây
mục cha
commit
71fc7ac7ac

+ 55 - 20
Userland/Libraries/LibJS/AST.cpp

@@ -115,13 +115,6 @@ CallExpression::ThisAndCallee CallExpression::compute_this_and_callee(Interprete
 {
     auto& vm = interpreter.vm();
 
-    if (is<SuperExpression>(*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<MemberExpression>(*m_callee)) {
         auto& member_expression = static_cast<MemberExpression const&>(*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));
+}
+
+// 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();
+
+    // 1. Let newTarget be GetNewTarget().
+    auto new_target = vm.get_new_target();
+    if (vm.exception())
+        return {};
 
-    if (!is<SuperExpression>(*m_callee))
-        return vm.call(function, this_value, move(arg_list));
+    // 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 {};
 
-    auto* super_constructor = get_super_constructor(interpreter.vm());
-    // FIXME: Functions should track their constructor kind.
-    if (!super_constructor || !super_constructor->is_function()) {
+    // 5. If IsConstructor(func) is false, throw a TypeError exception.
+    // FIXME: This check is non-conforming.
+    if (!func || !func->is_function()) {
         vm.throw_exception<TypeError>(global_object, ErrorType::NotAConstructor, "Super constructor");
         return {};
     }
-    result = vm.construct(static_cast<FunctionObject&>(*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<FunctionObject&>(*func), function, move(arg_list));
     if (vm.exception())
         return {};
 
-    auto& this_er = get_this_environment(interpreter.vm());
-    verify_cast<FunctionEnvironment>(this_er).bind_this_value(global_object, result);
+    // 7. Let thisER be GetThisEnvironment().
+    auto& this_er = verify_cast<FunctionEnvironment>(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);

+ 15 - 0
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<CallExpression::Argument> arguments)
+        : Expression(source_range)
+        , m_arguments(move(arguments))
+    {
+    }
+
+    virtual Value execute(Interpreter&, GlobalObject&) const override;
+    virtual void dump(int indent) const override;
+
+private:
+    Vector<CallExpression::Argument> const m_arguments;
+};
+
 enum class AssignmentOp {
     Assignment,
     AdditionAssignment,

+ 4 - 2
Userland/Libraries/LibJS/Parser.cpp

@@ -615,9 +615,8 @@ NonnullRefPtr<ClassExpression> 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<CallExpression>(
+            auto super_call = create_ast_node<SuperCall>(
                 { m_state.current_token.filename(), rule_start.position(), position() },
-                create_ast_node<SuperExpression>({ m_state.current_token.filename(), rule_start.position(), position() }),
                 Vector { CallExpression::Argument { create_ast_node<Identifier>({ m_state.current_token.filename(), rule_start.position(), position() }, "args"), true } });
             constructor_body->append(create_ast_node<ExpressionStatement>({ 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<CallExpression> Parser::parse_call_expression(NonnullRefPtr<Expres
 
     consume(TokenType::ParenClose);
 
+    if (is<SuperExpression>(*lhs))
+        return create_ast_node<SuperCall>({ m_state.current_token.filename(), rule_start.position(), position() }, move(arguments));
+
     return create_ast_node<CallExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), move(arguments));
 }