1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 03:48:13 +00:00

LibJS: Implement basic support for the "new" keyword

NewExpression mostly piggybacks on the existing CallExpression. The big
difference is that "new" creates a new Object and passes it as |this|
to the callee.
This commit is contained in:
Andreas Kling 2020-03-28 16:33:52 +01:00
parent fecbef4ffe
commit 0593ce406b
5 changed files with 67 additions and 9 deletions

View file

@ -80,18 +80,30 @@ Value CallExpression::execute(Interpreter& interpreter) const
return {}; return {};
} }
if (m_callee->is_member_expression()) { Object* new_object = nullptr;
auto object_value = static_cast<const MemberExpression&>(*m_callee).object().execute(interpreter); if (is_new_expression()) {
if (interpreter.exception()) new_object = interpreter.heap().allocate<Object>();
return {}; call_frame.this_value = new_object;
auto this_value = object_value.to_object(interpreter.heap()); } else {
if (interpreter.exception()) if (m_callee->is_member_expression()) {
return {}; auto object_value = static_cast<const MemberExpression&>(*m_callee).object().execute(interpreter);
call_frame.this_value = this_value; if (interpreter.exception())
return {};
auto this_value = object_value.to_object(interpreter.heap());
if (interpreter.exception())
return {};
call_frame.this_value = this_value;
}
} }
auto result = function->call(interpreter, call_frame.arguments); auto result = function->call(interpreter, call_frame.arguments);
interpreter.pop_call_frame(); interpreter.pop_call_frame();
if (is_new_expression()) {
if (result.is_object())
return result;
return new_object;
}
return result; return result;
} }

View file

@ -54,6 +54,7 @@ public:
virtual bool is_member_expression() const { return false; } virtual bool is_member_expression() const { return false; }
virtual bool is_scope_node() const { return false; } virtual bool is_scope_node() const { return false; }
virtual bool is_variable_declaration() const { return false; } virtual bool is_variable_declaration() const { return false; }
virtual bool is_new_expression() const { return false; }
protected: protected:
ASTNode() {} ASTNode() {}
@ -470,7 +471,7 @@ private:
class CallExpression : public Expression { class CallExpression : public Expression {
public: public:
explicit CallExpression(NonnullRefPtr<Expression> callee, NonnullRefPtrVector<Expression> arguments = {}) CallExpression(NonnullRefPtr<Expression> callee, NonnullRefPtrVector<Expression> arguments = {})
: m_callee(move(callee)) : m_callee(move(callee))
, m_arguments(move(arguments)) , m_arguments(move(arguments))
{ {
@ -486,6 +487,18 @@ private:
const NonnullRefPtrVector<Expression> m_arguments; const NonnullRefPtrVector<Expression> m_arguments;
}; };
class NewExpression final : public CallExpression {
public:
NewExpression(NonnullRefPtr<Expression> callee, NonnullRefPtrVector<Expression> arguments = {})
: CallExpression(move(callee), move(arguments))
{
}
private:
virtual const char * class_name() const override { return "NewExpression"; }
virtual bool is_new_expression() const override { return true; }
};
enum class AssignmentOp { enum class AssignmentOp {
Assignment, Assignment,
AdditionAssignment, AdditionAssignment,

View file

@ -247,6 +247,8 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression()
return parse_function_node<FunctionExpression>(); return parse_function_node<FunctionExpression>();
case TokenType::BracketOpen: case TokenType::BracketOpen:
return parse_array_expression(); return parse_array_expression();
case TokenType::New:
return parse_new_expression();
default: default:
m_has_errors = true; m_has_errors = true;
expected("primary expression (missing switch case)"); expected("primary expression (missing switch case)");
@ -443,6 +445,29 @@ NonnullRefPtr<CallExpression> Parser::parse_call_expression(NonnullRefPtr<Expres
return create_ast_node<CallExpression>(move(lhs), move(arguments)); return create_ast_node<CallExpression>(move(lhs), move(arguments));
} }
NonnullRefPtr<NewExpression> Parser::parse_new_expression()
{
consume(TokenType::New);
// FIXME: Support full expressions as the callee as well.
auto callee = create_ast_node<Identifier>(consume(TokenType::Identifier).value());
NonnullRefPtrVector<Expression> arguments;
if (match(TokenType::ParenOpen)) {
consume(TokenType::ParenOpen);
while (match_expression()) {
arguments.append(parse_expression(0));
if (!match(TokenType::Comma))
break;
consume();
}
consume(TokenType::ParenClose);
}
return create_ast_node<NewExpression>(move(callee), move(arguments));
}
NonnullRefPtr<ReturnStatement> Parser::parse_return_statement() NonnullRefPtr<ReturnStatement> Parser::parse_return_statement()
{ {
consume(TokenType::Return); consume(TokenType::Return);

View file

@ -63,6 +63,7 @@ public:
NonnullRefPtr<ArrayExpression> parse_array_expression(); NonnullRefPtr<ArrayExpression> parse_array_expression();
NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression>, int min_precedence, Associativity associate = Associativity::Right); NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression>, int min_precedence, Associativity associate = Associativity::Right);
NonnullRefPtr<CallExpression> parse_call_expression(NonnullRefPtr<Expression>); NonnullRefPtr<CallExpression> parse_call_expression(NonnullRefPtr<Expression>);
NonnullRefPtr<NewExpression> parse_new_expression();
bool has_errors() const { return m_has_errors; } bool has_errors() const { return m_has_errors; }

View file

@ -0,0 +1,7 @@
function Foo() {
this.x = 123;
}
var foo = new Foo();
if (foo.x === 123)
console.log("PASS");