mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-30 00:31:14 +00:00
LibJS: Make SuperCall a proper AST node and clean up evaluation
This commit is contained in:
parent
d81f4d5228
commit
71fc7ac7ac
Notes:
sideshowbarker
2024-07-18 11:06:41 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/71fc7ac7ac2
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue