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

This commit is contained in:
Andreas Kling 2021-07-02 19:30:38 +02:00
parent d81f4d5228
commit 71fc7ac7ac
Notes: sideshowbarker 2024-07-18 11:06:41 +09:00
3 changed files with 74 additions and 22 deletions

View File

@ -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));
}
if (!is<SuperExpression>(*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<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);

View File

@ -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,

View File

@ -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));
}