summaryrefslogtreecommitdiff
path: root/Libraries/LibJS/Parser.cpp
diff options
context:
space:
mode:
authorJack Karamanian <karamanian.jack@gmail.com>2020-06-08 13:31:21 -0500
committerAndreas Kling <kling@serenityos.org>2020-06-29 17:54:54 +0200
commit7533fd8b025937b2a9ab834a6bbee2be7309de8b (patch)
treec6f2a77266260b539468a3b0f9d6d733857dc74c /Libraries/LibJS/Parser.cpp
parenta535d58cac6cfef7c85cd9ee8419189d58560afb (diff)
downloadserenity-7533fd8b025937b2a9ab834a6bbee2be7309de8b.zip
LibJS: Initial class implementation; allow super expressions in object
literal methods; add EnvrionmentRecord fields and methods to LexicalEnvironment Adding EnvrionmentRecord's fields and methods lets us throw an exception when |this| is not initialized, which occurs when the super constructor in a derived class has not yet been called, or when |this| has already been initialized (the super constructor was already called).
Diffstat (limited to 'Libraries/LibJS/Parser.cpp')
-rw-r--r--Libraries/LibJS/Parser.cpp218
1 files changed, 183 insertions, 35 deletions
diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp
index 7924c4913a..f06bab3572 100644
--- a/Libraries/LibJS/Parser.cpp
+++ b/Libraries/LibJS/Parser.cpp
@@ -244,6 +244,8 @@ NonnullRefPtr<Statement> Parser::parse_statement()
{
auto statement = [this]() -> NonnullRefPtr<Statement> {
switch (m_parser_state.m_current_token.type()) {
+ case TokenType::Class:
+ return parse_class_declaration();
case TokenType::Function: {
auto declaration = parse_function_node<FunctionDeclaration>();
m_parser_state.m_function_scopes.last().append(declaration);
@@ -421,6 +423,136 @@ RefPtr<Statement> Parser::try_parse_labelled_statement()
return statement;
}
+NonnullRefPtr<ClassDeclaration> Parser::parse_class_declaration()
+{
+ return create_ast_node<ClassDeclaration>(parse_class_expression(true));
+}
+
+NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_name)
+{
+ // Classes are always in strict mode.
+ TemporaryChange strict_mode_rollback(m_parser_state.m_strict_mode, true);
+
+ consume(TokenType::Class);
+
+ NonnullRefPtrVector<ClassMethod> methods;
+ RefPtr<Expression> super_class;
+ RefPtr<FunctionExpression> constructor;
+
+ String class_name = expect_class_name || match(TokenType::Identifier) ? consume(TokenType::Identifier).value().to_string() : "";
+
+ if (match(TokenType::Extends)) {
+ consume();
+ super_class = parse_primary_expression();
+ }
+
+ consume(TokenType::CurlyOpen);
+
+ while (!done() && !match(TokenType::CurlyClose)) {
+ RefPtr<Expression> property_key;
+ bool is_static = false;
+ bool is_constructor = false;
+ auto method_kind = ClassMethod::Kind::Method;
+
+ if (match(TokenType::Semicolon)) {
+ consume();
+ continue;
+ }
+
+ if (match_property_key()) {
+ StringView name;
+ if (match(TokenType::Identifier) && m_parser_state.m_current_token.value() == "static") {
+ consume();
+ is_static = true;
+ }
+
+ if (match(TokenType::Identifier)) {
+ auto identifier_name = m_parser_state.m_current_token.value();
+
+ if (identifier_name == "get") {
+ method_kind = ClassMethod::Kind::Getter;
+ consume();
+ } else if (identifier_name == "set") {
+ method_kind = ClassMethod::Kind::Setter;
+ consume();
+ }
+ }
+
+ if (match_property_key()) {
+ switch (m_parser_state.m_current_token.type()) {
+ case TokenType::Identifier:
+ name = consume().value();
+ property_key = create_ast_node<StringLiteral>(name);
+ break;
+ case TokenType::StringLiteral: {
+ auto string_literal = parse_string_literal(consume());
+ name = string_literal->value();
+ property_key = move(string_literal);
+ break;
+ }
+ default:
+ property_key = parse_property_key();
+ break;
+ }
+
+ } else {
+ expected("property key");
+ }
+
+ // Constructor may be a StringLiteral or an Identifier.
+ if (!is_static && name == "constructor") {
+ if (method_kind != ClassMethod::Kind::Method)
+ syntax_error("Class constructor may not be an accessor");
+ if (!constructor.is_null())
+ syntax_error("Classes may not have more than one constructor");
+
+ is_constructor = true;
+ }
+ }
+
+ if (match(TokenType::ParenOpen)) {
+ auto function = parse_function_node<FunctionExpression>(false, true, !super_class.is_null());
+ auto arg_count = function->parameters().size();
+
+ if (method_kind == ClassMethod::Kind::Getter && arg_count != 0) {
+ syntax_error("Class getter method must have no arguments");
+ } else if (method_kind == ClassMethod::Kind::Setter && arg_count != 1) {
+ syntax_error("Class setter method must have one argument");
+ }
+
+ if (is_constructor) {
+ constructor = move(function);
+ } else if (!property_key.is_null()) {
+ methods.append(create_ast_node<ClassMethod>(property_key.release_nonnull(), move(function), method_kind, is_static));
+ } else {
+ syntax_error("No key for class method");
+ }
+ } else {
+ expected("ParenOpen");
+ consume();
+ }
+ }
+
+ consume(TokenType::CurlyClose);
+
+ if (constructor.is_null()) {
+ auto constructor_body = create_ast_node<BlockStatement>();
+ if (!super_class.is_null()) {
+ // Set constructor to the result of parsing the source text
+ // constructor(... args){ super (...args);}
+ auto super_call = create_ast_node<CallExpression>(create_ast_node<SuperExpression>(), Vector { CallExpression::Argument { create_ast_node<Identifier>("args"), true } });
+ constructor_body->append(create_ast_node<ExpressionStatement>(move(super_call)));
+ constructor_body->add_variables(m_parser_state.m_var_scopes.last());
+
+ constructor = create_ast_node<FunctionExpression>(class_name, move(constructor_body), Vector { FunctionNode::Parameter { "args", nullptr, true } }, 0, NonnullRefPtrVector<VariableDeclaration>());
+ } else {
+ constructor = create_ast_node<FunctionExpression>(class_name, move(constructor_body), Vector<FunctionNode::Parameter> {}, 0, NonnullRefPtrVector<VariableDeclaration>());
+ }
+ }
+
+ return create_ast_node<ClassExpression>(move(class_name), move(constructor), move(super_class), move(methods));
+}
+
NonnullRefPtr<Expression> Parser::parse_primary_expression()
{
if (match_unary_prefixed_expression())
@@ -442,6 +574,13 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression()
case TokenType::This:
consume();
return create_ast_node<ThisExpression>();
+ case TokenType::Class:
+ return parse_class_expression(false);
+ case TokenType::Super:
+ consume();
+ if (!m_parser_state.m_allow_super_property_lookup)
+ syntax_error("'super' keyword unexpected here");
+ return create_ast_node<SuperExpression>();
case TokenType::Identifier: {
auto arrow_function_result = try_parse_arrow_function_expression(false);
if (!arrow_function_result.is_null()) {
@@ -537,6 +676,27 @@ NonnullRefPtr<Expression> Parser::parse_unary_prefixed_expression()
}
}
+NonnullRefPtr<Expression> Parser::parse_property_key()
+{
+ if (match(TokenType::StringLiteral)) {
+ return parse_string_literal(consume());
+ } else if (match(TokenType::NumericLiteral)) {
+ return create_ast_node<StringLiteral>(consume(TokenType::NumericLiteral).value());
+ } else if (match(TokenType::BigIntLiteral)) {
+ auto value = consume(TokenType::BigIntLiteral).value();
+ return create_ast_node<StringLiteral>(value.substring_view(0, value.length() - 1));
+ } else if (match(TokenType::BracketOpen)) {
+ consume(TokenType::BracketOpen);
+ auto result = parse_expression(0);
+ consume(TokenType::BracketClose);
+ return result;
+ } else {
+ if (!match_identifier_name())
+ expected("IdentifierName");
+ return create_ast_node<StringLiteral>(consume().value());
+ }
+}
+
NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
{
consume(TokenType::CurlyOpen);
@@ -544,35 +704,6 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
NonnullRefPtrVector<ObjectProperty> properties;
ObjectProperty::Type property_type;
- auto match_property_key = [&]() -> bool {
- auto type = m_parser_state.m_current_token.type();
- return match_identifier_name()
- || type == TokenType::BracketOpen
- || type == TokenType::StringLiteral
- || type == TokenType::NumericLiteral
- || type == TokenType::BigIntLiteral;
- };
-
- auto parse_property_key = [&]() -> NonnullRefPtr<Expression> {
- if (match(TokenType::StringLiteral)) {
- return parse_string_literal(consume());
- } else if (match(TokenType::NumericLiteral)) {
- return create_ast_node<StringLiteral>(consume(TokenType::NumericLiteral).value());
- } else if (match(TokenType::BigIntLiteral)) {
- auto value = consume(TokenType::BigIntLiteral).value();
- return create_ast_node<StringLiteral>(value.substring_view(0, value.length() - 1));
- } else if (match(TokenType::BracketOpen)) {
- consume(TokenType::BracketOpen);
- auto result = parse_expression(0);
- consume(TokenType::BracketClose);
- return result;
- } else {
- if (!match_identifier_name())
- expected("IdentifierName");
- return create_ast_node<StringLiteral>(consume().value());
- }
- };
-
auto skip_to_next_property = [&] {
while (!done() && !match(TokenType::Comma) && !match(TokenType::CurlyOpen))
consume();
@@ -586,7 +717,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
if (match(TokenType::TripleDot)) {
consume();
property_name = parse_expression(4);
- properties.append(create_ast_node<ObjectProperty>(*property_name, nullptr, ObjectProperty::Type::Spread));
+ properties.append(create_ast_node<ObjectProperty>(*property_name, nullptr, ObjectProperty::Type::Spread, false));
if (!match(TokenType::Comma))
break;
consume(TokenType::Comma);
@@ -622,7 +753,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
if (match(TokenType::ParenOpen)) {
ASSERT(property_name);
- auto function = parse_function_node<FunctionExpression>(false);
+ auto function = parse_function_node<FunctionExpression>(false, true);
auto arg_count = function->parameters().size();
if (property_type == ObjectProperty::Type::Getter && arg_count != 0) {
@@ -642,7 +773,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
continue;
}
- properties.append(create_ast_node<ObjectProperty>(*property_name, function, property_type));
+ properties.append(create_ast_node<ObjectProperty>(*property_name, function, property_type, true));
} else if (match(TokenType::Colon)) {
if (!property_name) {
syntax_error("Expected a property name");
@@ -650,9 +781,9 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
continue;
}
consume();
- properties.append(create_ast_node<ObjectProperty>(*property_name, parse_expression(2), property_type));
+ properties.append(create_ast_node<ObjectProperty>(*property_name, parse_expression(2), property_type, false));
} else if (property_name && property_value) {
- properties.append(create_ast_node<ObjectProperty>(*property_name, *property_value, property_type));
+ properties.append(create_ast_node<ObjectProperty>(*property_name, *property_value, property_type, false));
} else {
syntax_error("Expected a property");
skip_to_next_property();
@@ -976,6 +1107,9 @@ NonnullRefPtr<Expression> Parser::parse_secondary_expression(NonnullRefPtr<Expre
NonnullRefPtr<CallExpression> Parser::parse_call_expression(NonnullRefPtr<Expression> lhs)
{
+ if (!m_parser_state.m_allow_super_constructor_call && lhs->is_super_expression())
+ syntax_error("'super' keyword unexpected here");
+
consume(TokenType::ParenOpen);
Vector<CallExpression::Argument> arguments;
@@ -1085,8 +1219,11 @@ NonnullRefPtr<BlockStatement> Parser::parse_block_statement()
}
template<typename FunctionNodeType>
-NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool check_for_function_and_name)
+NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool check_for_function_and_name, bool allow_super_property_lookup, bool allow_super_constructor_call)
{
+ TemporaryChange super_property_access_rollback(m_parser_state.m_allow_super_property_lookup, allow_super_property_lookup);
+ TemporaryChange super_constructor_call_rollback(m_parser_state.m_allow_super_constructor_call, allow_super_constructor_call);
+
ScopePusher scope(*this, ScopePusher::Var | ScopePusher::Function);
if (check_for_function_and_name)
@@ -1465,6 +1602,7 @@ bool Parser::match_expression() const
|| type == TokenType::ParenOpen
|| type == TokenType::Function
|| type == TokenType::This
+ || type == TokenType::Super
|| type == TokenType::RegexLiteral
|| match_unary_prefixed_expression();
}
@@ -1563,6 +1701,16 @@ bool Parser::match_identifier_name() const
return m_parser_state.m_current_token.is_identifier_name();
}
+bool Parser::match_property_key() const
+{
+ auto type = m_parser_state.m_current_token.type();
+ return match_identifier_name()
+ || type == TokenType::BracketOpen
+ || type == TokenType::StringLiteral
+ || type == TokenType::NumericLiteral
+ || type == TokenType::BigIntLiteral;
+}
+
bool Parser::done() const
{
return match(TokenType::Eof);