diff options
-rw-r--r-- | Libraries/LibJS/AST.cpp | 28 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 15 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.cpp | 25 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/constructor-basic.js | 7 |
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"); |