mirror of
https://github.com/RGBCube/serenity
synced 2025-05-29 20:35:13 +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:
parent
a535d58cac
commit
7533fd8b02
18 changed files with 967 additions and 92 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue