mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:07:34 +00:00
LibJS: Make SuperCall a proper AST node and clean up evaluation
This commit is contained in:
parent
d81f4d5228
commit
71fc7ac7ac
3 changed files with 74 additions and 22 deletions
|
@ -115,13 +115,6 @@ CallExpression::ThisAndCallee CallExpression::compute_this_and_callee(Interprete
|
||||||
{
|
{
|
||||||
auto& vm = interpreter.vm();
|
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)) {
|
if (is<MemberExpression>(*m_callee)) {
|
||||||
auto& member_expression = static_cast<MemberExpression const&>(*m_callee);
|
auto& member_expression = static_cast<MemberExpression const&>(*m_callee);
|
||||||
Value 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);
|
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))
|
// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
|
||||||
return vm.call(function, this_value, move(arg_list));
|
// 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());
|
// 1. Let newTarget be GetNewTarget().
|
||||||
// FIXME: Functions should track their constructor kind.
|
auto new_target = vm.get_new_target();
|
||||||
if (!super_constructor || !super_constructor->is_function()) {
|
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");
|
vm.throw_exception<TypeError>(global_object, ErrorType::NotAConstructor, "Super constructor");
|
||||||
return {};
|
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())
|
if (vm.exception())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto& this_er = get_this_environment(interpreter.vm());
|
// 7. Let thisER be GetThisEnvironment().
|
||||||
verify_cast<FunctionEnvironment>(this_er).bind_this_value(global_object, result);
|
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())
|
if (vm.exception())
|
||||||
return {};
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,11 +784,9 @@ Value UnaryExpression::execute(Interpreter& interpreter, GlobalObject& global_ob
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Value SuperExpression::execute(Interpreter& interpreter, GlobalObject&) const
|
Value SuperExpression::execute(Interpreter&, GlobalObject&) const
|
||||||
{
|
{
|
||||||
InterpreterNodeScope node_scope { interpreter, *this };
|
// The semantics for SuperExpression are handled in CallExpression and SuperCall.
|
||||||
|
|
||||||
// The semantics for SuperExpressions are handled in CallExpression::compute_this_and_callee()
|
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1055,6 +1082,14 @@ void CallExpression::dump(int indent) const
|
||||||
argument.value->dump(indent + 1);
|
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
|
void ClassDeclaration::dump(int indent) const
|
||||||
{
|
{
|
||||||
ASTNode::dump(indent);
|
ASTNode::dump(indent);
|
||||||
|
|
|
@ -951,6 +951,21 @@ public:
|
||||||
virtual bool is_new_expression() const override { return true; }
|
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 {
|
enum class AssignmentOp {
|
||||||
Assignment,
|
Assignment,
|
||||||
AdditionAssignment,
|
AdditionAssignment,
|
||||||
|
|
|
@ -615,9 +615,8 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
|
||||||
if (!super_class.is_null()) {
|
if (!super_class.is_null()) {
|
||||||
// Set constructor to the result of parsing the source text
|
// Set constructor to the result of parsing the source text
|
||||||
// constructor(... args){ super (...args);}
|
// 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() },
|
{ 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 } });
|
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->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());
|
constructor_body->add_variables(m_state.var_scopes.last());
|
||||||
|
@ -1288,6 +1287,9 @@ NonnullRefPtr<CallExpression> Parser::parse_call_expression(NonnullRefPtr<Expres
|
||||||
|
|
||||||
consume(TokenType::ParenClose);
|
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));
|
return create_ast_node<CallExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(lhs), move(arguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue