in lib/Parser/JSParserImpl.cpp [4575:4935]
Optional<ESTree::Node *> JSParserImpl::parseClassElement(
bool isStatic,
SMRange startRange,
bool declare,
bool isPrivate,
bool eagerly) {
SMLoc startLoc = tok_->getStartLoc();
bool optional = false;
enum class SpecialKind {
None,
Get,
Set,
Generator,
Async,
AsyncGenerator,
ClassProperty,
};
// Indicates if this method is out of the ordinary.
// In particular, indicates getters and setters.
SpecialKind special = SpecialKind::None;
// When true, call parsePropertyName.
// Set to false if the identifiers 'get' or 'set' were already parsed as
// function names instead of as getter/setter specifiers.
bool doParsePropertyName = true;
ESTree::Node *prop = nullptr;
if (check(getIdent_)) {
SMRange range = advance();
if (!checkN(
TokenKind::less,
TokenKind::l_paren,
TokenKind::r_brace,
TokenKind::equal,
TokenKind::colon,
TokenKind::semi)) {
// This was actually a getter.
special = SpecialKind::Get;
} else {
prop = setLocation(
range,
range,
new (context_) ESTree::IdentifierNode(getIdent_, nullptr, false));
doParsePropertyName = false;
}
} else if (check(setIdent_)) {
SMRange range = advance();
if (!checkN(
TokenKind::less,
TokenKind::l_paren,
TokenKind::r_brace,
TokenKind::equal,
TokenKind::colon,
TokenKind::semi)) {
// If we don't see '(' then this was actually a setter.
special = SpecialKind::Set;
} else {
prop = setLocation(
range,
range,
new (context_) ESTree::IdentifierNode(setIdent_, nullptr, false));
doParsePropertyName = false;
}
} else if (check(asyncIdent_)) {
SMRange range = advance();
if (!checkN(
TokenKind::less,
TokenKind::l_paren,
TokenKind::r_brace,
TokenKind::equal,
TokenKind::colon,
TokenKind::semi) &&
!lexer_.isNewLineBeforeCurrentToken()) {
// If we don't see '(' then this was actually an async method.
// Async methods cannot have a newline between 'async' and the name.
// These can be either Async or AsyncGenerator, so check for that.
special = checkAndEat(TokenKind::star) ? SpecialKind::AsyncGenerator
: SpecialKind::Async;
} else {
prop = setLocation(
range,
range,
new (context_) ESTree::IdentifierNode(asyncIdent_, nullptr, false));
doParsePropertyName = false;
}
} else if (checkAndEat(TokenKind::star)) {
special = SpecialKind::Generator;
} else if (check(TokenKind::l_paren, TokenKind::less) && isStatic) {
// We've already parsed 'static', but there is nothing between 'static'
// and the '(', so it must be used as the PropertyName and not as an
// indicator for a static function.
prop = setLocation(
startRange,
startRange,
new (context_) ESTree::IdentifierNode(staticIdent_, nullptr, false));
isStatic = false;
doParsePropertyName = false;
}
ESTree::Node *variance = nullptr;
#if HERMES_PARSE_FLOW
if (context_.getParseFlow() && check(TokenKind::plus, TokenKind::minus)) {
variance = setLocation(
tok_,
tok_,
new (context_) ESTree::VarianceNode(
check(TokenKind::plus) ? plusIdent_ : minusIdent_));
advance(JSLexer::GrammarContext::Type);
}
#endif
bool computed = false;
if (doParsePropertyName) {
if (check(TokenKind::private_identifier)) {
isPrivate = true;
prop = setLocation(
tok_,
tok_,
new (context_) ESTree::IdentifierNode(
tok_->getPrivateIdentifier(), nullptr, false));
advance();
} else {
computed = check(TokenKind::l_square);
auto optProp = parsePropertyName();
if (!optProp)
return None;
prop = *optProp;
}
}
// Store the propName for comparisons, used for SyntaxErrors.
UniqueString *propName = nullptr;
if (auto *id = dyn_cast<ESTree::IdentifierNode>(prop)) {
propName = id->_name;
} else if (auto *str = dyn_cast<ESTree::StringLiteralNode>(prop)) {
propName = str->_value;
}
bool isConstructor =
!isStatic && !computed && propName && propName->str() == "constructor";
if (special == SpecialKind::None &&
!check(TokenKind::less, TokenKind::l_paren)) {
// Parse a class property, because this can't be a method definition.
// Attempt ASI after the fact, and continue on, letting the next iteration
// error if it wasn't actually a class property.
// FieldDefinition ;
// ^
ESTree::Node *typeAnnotation = nullptr;
#if HERMES_PARSE_TS
if (context_.getParseTS() && checkAndEat(TokenKind::question)) {
optional = true;
}
#endif
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
if (context_.getParseTypes() && check(TokenKind::colon)) {
SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start;
auto optType = parseTypeAnnotation(annotStart);
if (!optType)
return None;
typeAnnotation = *optType;
}
#endif
ESTree::Node *value = nullptr;
if (checkAndEat(TokenKind::equal)) {
// ClassElementName Initializer[opt]
// ^
auto optValue = parseAssignmentExpression();
if (!optValue)
return None;
value = *optValue;
if (declare) {
error(startRange, "Invalid 'declare' with initializer");
}
}
// ASI is allowed for separating class elements.
if (!eatSemi(true) && !typeAnnotation) {
errorExpected(
TokenKind::semi,
"after class property",
"start of class property",
startRange.Start);
return None;
}
if (isPrivate) {
return setLocation(
prop,
getPrevTokenEndLoc(),
new (context_) ESTree::ClassPrivatePropertyNode(
prop,
value,
isStatic,
declare,
optional,
variance,
typeAnnotation));
}
return setLocation(
startRange,
getPrevTokenEndLoc(),
new (context_) ESTree::ClassPropertyNode(
prop,
value,
computed,
isStatic,
declare,
optional,
variance,
typeAnnotation));
}
if (declare) {
error(startRange, "Invalid 'declare' in class method");
}
SMLoc funcExprStartLoc = tok_->getStartLoc();
ESTree::Node *typeParams = nullptr;
#if HERMES_PARSE_FLOW
if (context_.getParseFlow() && check(TokenKind::less)) {
auto optTypeParams = parseTypeParamsFlow();
if (!optTypeParams)
return None;
typeParams = *optTypeParams;
}
#endif
// (
if (!need(
TokenKind::l_paren,
"in method definition",
"start of method definition",
startLoc))
return None;
ESTree::NodeList args{};
llvh::SaveAndRestore<bool> saveArgsAndBodyParamYield(
paramYield_,
special == SpecialKind::Generator ||
special == SpecialKind::AsyncGenerator);
llvh::SaveAndRestore<bool> saveArgsAndBodyParamAwait(
paramAwait_,
special == SpecialKind::Async || special == SpecialKind::AsyncGenerator);
if (!parseFormalParameters(Param{}, args))
return None;
ESTree::Node *returnType = nullptr;
#if HERMES_PARSE_FLOW
if (context_.getParseFlow() && check(TokenKind::colon)) {
SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start;
auto optRet = parseTypeAnnotationFlow(annotStart);
if (!optRet)
return None;
returnType = *optRet;
}
#endif
if (!need(
TokenKind::l_brace,
"in method definition",
"start of method definition",
startLoc))
return None;
auto optBody = parseFunctionBody(
ParamReturn,
eagerly,
saveArgsAndBodyParamYield.get(),
saveArgsAndBodyParamAwait.get(),
JSLexer::AllowRegExp,
true);
if (!optBody)
return None;
auto *funcExpr = setLocation(
funcExprStartLoc,
optBody.getValue(),
new (context_) ESTree::FunctionExpressionNode(
nullptr,
std::move(args),
optBody.getValue(),
typeParams,
returnType,
/* predicate */ nullptr,
special == SpecialKind::Generator ||
special == SpecialKind::AsyncGenerator,
special == SpecialKind::Async ||
special == SpecialKind::AsyncGenerator));
assert(isStrictMode() && "parseClassElement should only be used for classes");
funcExpr->isMethodDefinition = true;
if (special == SpecialKind::Get && funcExpr->_params.size() != 0) {
error(
{startLoc, funcExpr->getEndLoc()},
Twine("getter method must no one formal arguments, found ") +
Twine(funcExpr->_params.size()));
}
if (special == SpecialKind::Set && funcExpr->_params.size() != 1) {
error(
{startLoc, funcExpr->getEndLoc()},
Twine("setter method must have exactly one formal argument, found ") +
Twine(funcExpr->_params.size()));
}
#if HERMES_PARSE_FLOW
if ((special == SpecialKind::Get || special == SpecialKind::Set) &&
typeParams != nullptr) {
error(
{startLoc, funcExpr->getEndLoc()},
"accessor method may not have type parameters");
}
#endif
if (isStatic && propName && propName->str() == "prototype") {
// ClassElement : static MethodDefinition
// It is a Syntax Error if PropName of MethodDefinition is "prototype".
error(
{startLoc, funcExpr->getEndLoc()},
"prototype method must not be static");
return None;
}
UniqueString *kind = methodIdent_;
if (isConstructor) {
if (special != SpecialKind::None) {
// It is a Syntax Error if PropName of MethodDefinition is "constructor"
// and SpecialMethod of MethodDefinition is true.
// TODO: Account for generator methods in SpecialMethod here.
error(
{startLoc, funcExpr->getEndLoc()},
"constructor method must not be a getter or setter");
return None;
}
kind = constructorIdent_;
} else if (special == SpecialKind::Get) {
kind = getIdent_;
} else if (special == SpecialKind::Set) {
kind = setIdent_;
}
if (isPrivate) {
prop = setLocation(
startLoc, prop, new (context_) ESTree::PrivateNameNode(prop));
}
if (variance) {
error(variance->getSourceRange(), "Unexpected variance sigil");
}
return setLocation(
startRange,
optBody.getValue(),
new (context_) ESTree::MethodDefinitionNode(
prop, funcExpr, kind, computed, isStatic));
}