summaryrefslogtreecommitdiff
path: root/Libraries/LibJS
diff options
context:
space:
mode:
Diffstat (limited to 'Libraries/LibJS')
-rw-r--r--Libraries/LibJS/AST.cpp28
-rw-r--r--Libraries/LibJS/AST.h15
-rw-r--r--Libraries/LibJS/Parser.cpp25
-rw-r--r--Libraries/LibJS/Parser.h1
-rw-r--r--Libraries/LibJS/Tests/constructor-basic.js7
5 files changed, 67 insertions, 9 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp
index 638ed6ec7a..d5f8da6323 100644
--- a/Libraries/LibJS/AST.cpp
+++ b/Libraries/LibJS/AST.cpp
@@ -80,18 +80,30 @@ Value CallExpression::execute(Interpreter& interpreter) const
return {};
}
- if (m_callee->is_member_expression()) {
- auto object_value = static_cast<const MemberExpression&>(*m_callee).object().execute(interpreter);
- if (interpreter.exception())
- return {};
- auto this_value = object_value.to_object(interpreter.heap());
- if (interpreter.exception())
- return {};
- call_frame.this_value = this_value;
+ Object* new_object = nullptr;
+ if (is_new_expression()) {
+ new_object = interpreter.heap().allocate<Object>();
+ call_frame.this_value = new_object;
+ } else {
+ if (m_callee->is_member_expression()) {
+ auto object_value = static_cast<const MemberExpression&>(*m_callee).object().execute(interpreter);
+ 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);
interpreter.pop_call_frame();
+
+ if (is_new_expression()) {
+ if (result.is_object())
+ return result;
+ return new_object;
+ }
return result;
}
diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h
index 34a3fd6b4c..aef19889aa 100644
--- a/Libraries/LibJS/AST.h
+++ b/Libraries/LibJS/AST.h
@@ -54,6 +54,7 @@ public:
virtual bool is_member_expression() const { return false; }
virtual bool is_scope_node() const { return false; }
virtual bool is_variable_declaration() const { return false; }
+ virtual bool is_new_expression() const { return false; }
protected:
ASTNode() {}
@@ -470,7 +471,7 @@ private:
class CallExpression : public Expression {
public:
- explicit CallExpression(NonnullRefPtr<Expression> callee, NonnullRefPtrVector<Expression> arguments = {})
+ CallExpression(NonnullRefPtr<Expression> callee, NonnullRefPtrVector<Expression> arguments = {})
: m_callee(move(callee))
, m_arguments(move(arguments))
{
@@ -486,6 +487,18 @@ private:
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 {
Assignment,
AdditionAssignment,
diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp
index c4f2a3966f..4552e22cdb 100644
--- a/Libraries/LibJS/Parser.cpp
+++ b/Libraries/LibJS/Parser.cpp
@@ -247,6 +247,8 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression()
return parse_function_node<FunctionExpression>();
case TokenType::BracketOpen:
return parse_array_expression();
+ case TokenType::New:
+ return parse_new_expression();
default:
m_has_errors = true;
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));
}
+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()
{
consume(TokenType::Return);
diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h
index 7cbd614b32..25bc408da4 100644
--- a/Libraries/LibJS/Parser.h
+++ b/Libraries/LibJS/Parser.h
@@ -63,6 +63,7 @@ public:
NonnullRefPtr<ArrayExpression> parse_array_expression();
NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression>, int min_precedence, Associativity associate = Associativity::Right);
NonnullRefPtr<CallExpression> parse_call_expression(NonnullRefPtr<Expression>);
+ NonnullRefPtr<NewExpression> parse_new_expression();
bool has_errors() const { return m_has_errors; }
diff --git a/Libraries/LibJS/Tests/constructor-basic.js b/Libraries/LibJS/Tests/constructor-basic.js
new file mode 100644
index 0000000000..9e2e1233ea
--- /dev/null
+++ b/Libraries/LibJS/Tests/constructor-basic.js
@@ -0,0 +1,7 @@
+function Foo() {
+ this.x = 123;
+}
+
+var foo = new Foo();
+if (foo.x === 123)
+ console.log("PASS");