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:
parent
fecbef4ffe
commit
0593ce406b
5 changed files with 67 additions and 9 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
7
Libraries/LibJS/Tests/constructor-basic.js
Normal file
7
Libraries/LibJS/Tests/constructor-basic.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
function Foo() {
|
||||||
|
this.x = 123;
|
||||||
|
}
|
||||||
|
|
||||||
|
var foo = new Foo();
|
||||||
|
if (foo.x === 123)
|
||||||
|
console.log("PASS");
|
Loading…
Add table
Add a link
Reference in a new issue