1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-29 14:25:07 +00:00

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).
This commit is contained in:
Jack Karamanian 2020-06-08 13:31:21 -05:00 committed by Andreas Kling
parent a535d58cac
commit 7533fd8b02
18 changed files with 967 additions and 92 deletions

View file

@ -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);