lib/Parser/JSParserImpl.h (809 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef HERMES_PARSER_JSPARSERIMPL_H
#define HERMES_PARSER_JSPARSERIMPL_H
#include "hermes/AST/Config.h"
#include "hermes/AST/Context.h"
#include "hermes/AST/ESTree.h"
#include "hermes/Parser/JSLexer.h"
#include "hermes/Parser/JSParser.h"
#include "hermes/Parser/PreParser.h"
#include "hermes/Support/Compiler.h"
#include "llvh/ADT/ArrayRef.h"
#include "llvh/ADT/Optional.h"
#include "llvh/ADT/SmallVector.h"
#include "llvh/Support/DataTypes.h"
#include <utility>
namespace hermes {
namespace parser {
namespace detail {
using llvh::ArrayRef;
using llvh::None;
using llvh::Optional;
/// A convenience class to encapsulate passing multiple boolean parameters
/// between parser functions.
class Param {
unsigned flags_;
public:
constexpr Param() : flags_(0) {}
constexpr explicit Param(unsigned f) : flags_(f) {}
constexpr Param operator+(Param b) const {
return Param{flags_ | b.flags_};
}
constexpr Param operator-(Param b) const {
return Param{flags_ & ~(b.flags_)};
}
/// \return true if any of the flags in \p p are set in this instance.
bool has(Param p) const {
return flags_ & p.flags_;
}
/// \return true if all of the flags in \p p are set in this instance.
bool hasAll(Param p) const {
return (flags_ & p.flags_) == p.flags_;
}
/// \return \p p if at least on of its bits is set in this instance,
/// otherwise returns an empty param.
constexpr Param get(Param p) const {
return Param{flags_ & p.flags_};
}
template <typename... T>
constexpr Param get(Param p, T... tail) const {
return Param{get(p).flags_ | get(tail...).flags_};
}
};
/// If set, "in" is recognized as a binary operator in RelationalExpression.
static constexpr Param ParamIn{1 << 0};
static constexpr Param ParamReturn{1 << 1};
static constexpr Param ParamDefault{1 << 2};
static constexpr Param ParamTagged{1 << 3};
/// An EcmaScript 5.1 parser.
/// It is a standard recursive descent LL(1) parser with no tricks. The only
/// complication, is the need to communicate information to the lexer whether
/// a regexp is allowed or not.
/// We go to some effort to avoid the need for more than one token lookahead
/// for performance. Some things (like recognizing a label) would have been
/// simplified with larger lookahead.
class JSParserImpl {
public:
explicit JSParserImpl(
Context &context,
std::unique_ptr<llvh::MemoryBuffer> input);
explicit JSParserImpl(Context &context, uint32_t bufferId, ParserPass pass);
JSParserImpl(Context &context, StringRef input)
: JSParserImpl(
context,
llvh::MemoryBuffer::getMemBuffer(input, "JavaScript")) {}
JSParserImpl(Context &context, llvh::MemoryBufferRef input)
: JSParserImpl(context, llvh::MemoryBuffer::getMemBuffer(input)) {}
Context &getContext() {
return context_;
}
bool isStrictMode() const {
return lexer_.isStrictMode();
}
void setStrictMode(bool mode) {
lexer_.setStrictMode(mode);
}
llvh::SmallVector<UniqueString *, 1> &getSeenDirectives() {
return seenDirectives_;
}
/// Copy the seen directives from a vector of \c UniqueString* into a vector
/// of \c SmallString to be stored in \c PreParseFunctionInfo safely.
llvh::SmallVector<llvh::SmallString<24>, 1> copySeenDirectives() const;
llvh::ArrayRef<StoredComment> getStoredComments() const {
return lexer_.getStoredComments();
}
llvh::ArrayRef<StoredToken> getStoredTokens() const {
return lexer_.getStoredTokens();
}
void setStoreComments(bool storeComments) {
lexer_.setStoreComments(storeComments);
}
void setStoreTokens(bool storeTokens) {
lexer_.setStoreTokens(storeTokens);
}
Optional<ESTree::ProgramNode *> parse();
void seek(SMLoc startPos) {
lexer_.seek(startPos);
tok_ = lexer_.advance();
}
/// Parse the given buffer id, indexing all functions and storing them in the
/// \p Context. Returns true on success, at which point the file can be
/// processed on demand in \p LazyParse mode. \p useStaticBuiltinDetected will
/// be set to true if 'use static builtin' directive is detected in the
/// source.
static bool preParseBuffer(
Context &context,
uint32_t bufferId,
bool &useStaticBuiltinDetected);
/// Parse the AST of a specified function type at a given starting point.
/// This is used for lazy compilation to parse and compile the function on
/// the first call.
Optional<ESTree::NodePtr> parseLazyFunction(
ESTree::NodeKind kind,
bool paramYield,
bool paramAwait,
SMLoc start);
/// Return true if the parser detected 'use static builtin' directive from the
/// source.
bool getUseStaticBuiltin() const {
return useStaticBuiltin_;
}
private:
/// Called when the parser detects 'use static builtin' directive from the
/// source.
void setUseStaticBuiltin() {
useStaticBuiltin_ = true;
}
/// Called during construction to initialize Identifiers used for parsing,
/// such as "var". The lexer and parser uses these to avoid passing strings
/// around.
void initializeIdentifiers();
/// Current compilation context.
Context &context_;
/// Source error and buffer manager.
SourceErrorManager &sm_;
/// Source code lexer.
JSLexer lexer_;
/// Current token.
const Token *tok_{};
/// The current parser mode (see \p ParserPass).
ParserPass pass_{FullParse};
/// Function offsets. PreParse mode fills it in, LazyParse mode uses it
/// to skip spans while parsing.
PreParsedBufferInfo *preParsed_{nullptr};
/// Track the parser recursion depth to avoid stack overflow.
/// We don't have to track it precisely as long as we increment it once in
/// every possible recursion cycle.
unsigned recursionDepth_{0};
/// Self-explanatory: the maximum depth of parser recursion.
static constexpr unsigned MAX_RECURSION_DEPTH =
#ifdef HERMES_LIMIT_STACK_DEPTH
128
#elif defined(_MSC_VER) && defined(HERMES_SLOW_DEBUG)
128
#elif defined(_MSC_VER)
512
#else
1024
#endif
;
/// Set when the parser sees the 'use static builtin' directive in any scope.
bool useStaticBuiltin_{false};
/// Set when the parser is inside a generator function.
/// This is used when checking if `yield` is a valid Identifier name.
bool paramYield_{false};
/// Set when the parser is inside an async function.
/// This is used when checking if `await` is a valid Identifier name.
bool paramAwait_{false};
/// Appended when the parser has seen an directive being visited in the
/// current function scope (It's intended to be used with
/// `SaveStrictModeAndSeenDirectives`).
/// This is used to store directives for lazy functions in the preParse pass,
/// so we can recover directive nodes back in the lazyParse pass.
llvh::SmallVector<UniqueString *, 1> seenDirectives_{};
#if HERMES_PARSE_JSX
/// Incremented when inside a JSX tag and decremented when leaving it.
/// Used to know whether to lex JS values or JSX text.
uint32_t jsxDepth_{0};
#endif
#if HERMES_PARSE_FLOW
bool allowAnonFunctionType_{false};
#endif
// Certain known identifiers which we need to use when constructing the
// ESTree or when parsing;
UniqueString *getIdent_;
UniqueString *setIdent_;
UniqueString *initIdent_;
UniqueString *useStrictIdent_;
UniqueString *showSourceIdent_;
UniqueString *hideSourceIdent_;
UniqueString *sensitiveIdent_;
UniqueString *useStaticBuiltinIdent_;
UniqueString *letIdent_;
UniqueString *ofIdent_;
UniqueString *fromIdent_;
UniqueString *asIdent_;
UniqueString *implementsIdent_;
UniqueString *interfaceIdent_;
UniqueString *packageIdent_;
UniqueString *privateIdent_;
UniqueString *protectedIdent_;
UniqueString *publicIdent_;
UniqueString *staticIdent_;
UniqueString *methodIdent_;
UniqueString *constructorIdent_;
UniqueString *yieldIdent_;
UniqueString *newIdent_;
UniqueString *targetIdent_;
UniqueString *valueIdent_;
UniqueString *typeIdent_;
UniqueString *asyncIdent_;
UniqueString *awaitIdent_;
UniqueString *assertIdent_;
#if HERMES_PARSE_FLOW
UniqueString *typeofIdent_;
UniqueString *declareIdent_;
UniqueString *protoIdent_;
UniqueString *opaqueIdent_;
UniqueString *plusIdent_;
UniqueString *minusIdent_;
UniqueString *moduleIdent_;
UniqueString *exportsIdent_;
UniqueString *esIdent_;
UniqueString *commonJSIdent_;
UniqueString *mixinsIdent_;
UniqueString *thisIdent_;
UniqueString *anyIdent_;
UniqueString *mixedIdent_;
UniqueString *emptyIdent_;
UniqueString *booleanIdent_;
UniqueString *boolIdent_;
UniqueString *numberIdent_;
UniqueString *stringIdent_;
UniqueString *voidIdent_;
UniqueString *nullIdent_;
UniqueString *symbolIdent_;
UniqueString *checksIdent_;
#endif
#if HERMES_PARSE_TS
UniqueString *namespaceIdent_;
UniqueString *readonlyIdent_;
UniqueString *isIdent_;
#endif
/// String representation of all tokens.
UniqueString *tokenIdent_[NUM_JS_TOKENS];
UniqueString *getTokenIdent(TokenKind kind) const {
return tokenIdent_[(unsigned)kind];
}
/// Allocate an ESTree node of a certain type with supplied location and
/// construction arguments. All nodes are allocated using the supplied
/// allocator.
template <class Node, class StartLoc, class EndLoc>
Node *setLocation(StartLoc start, EndLoc end, Node *node) {
node->setStartLoc(getStartLoc(start));
node->setEndLoc(getEndLoc(end));
node->setDebugLoc(getStartLoc(start));
return node;
}
/// Sets staart, end and debug lcoations of an ast node.
template <class Node, class StartLoc, class EndLoc, class DebugLoc>
Node *setLocation(StartLoc start, EndLoc end, DebugLoc debugLoc, Node *node) {
node->setStartLoc(getStartLoc(start));
node->setEndLoc(getEndLoc(end));
node->setDebugLoc(getStartLoc(debugLoc));
return node;
}
// A group of overrides to extract the start and end location of various
// objects. The purpose is to allow flexibility when passing the location
// information to setLocation(). We could pass existing nodes, locations,
// tokens, or a combination of them.
static SMLoc getStartLoc(const Token *tok) {
return tok->getStartLoc();
}
static SMLoc getStartLoc(const ESTree::Node *from) {
return from->getStartLoc();
}
static SMLoc getStartLoc(SMLoc loc) {
return loc;
}
static SMLoc getStartLoc(const SMRange &rng) {
return rng.Start;
}
static SMLoc getEndLoc(const Token *tok) {
return tok->getEndLoc();
}
static SMLoc getEndLoc(const ESTree::Node *from) {
return from->getEndLoc();
}
static SMLoc getEndLoc(SMLoc loc) {
return loc;
}
static SMLoc getEndLoc(const SMRange &rng) {
return rng.End;
}
SMLoc getPrevTokenEndLoc() const {
return lexer_.getPrevTokenEndLoc();
}
/// Obtain the next token from the lexer and store it in tok_.
/// \param grammarContext enable recognizing either "/" and "/=", or a regexp.
/// \return the source location of the just consumed (previous) token.
SMRange advance(
JSLexer::GrammarContext grammarContext = JSLexer::AllowRegExp) {
SMRange loc = tok_->getSourceRange();
tok_ = lexer_.advance(grammarContext);
return loc;
}
/// Report an error that one of the specified tokens was expected at the
/// location of the current token.
/// \param where (optional) If non-null, it is appended to the error message,
/// and is intended to explain the context in which these tokens
/// were expected (e.g. "after 'if'")
/// \param what (optional) If not null, showen as an additional hint about the
/// error at the location specified by whatLoc. If whatLoc and the
/// current token are on the same line, `what` is not displayed but
/// the entire region between the location is emphasized.
void errorExpected(
ArrayRef<TokenKind> toks,
const char *where,
const char *what,
SMLoc whatLoc);
/// Convenience wrapper around errorExpected().
void errorExpected(
TokenKind k1,
const char *where,
const char *what,
SMLoc whatLoc) {
errorExpected(ArrayRef<TokenKind>(k1), where, what, whatLoc);
}
/// Convenience wrapper around errorExpected().
void errorExpected(
TokenKind k1,
TokenKind k2,
const char *where,
const char *what,
SMLoc whatLoc) {
TokenKind toks[] = {k1, k2};
errorExpected(ArrayRef<TokenKind>(toks, 2), where, what, whatLoc);
};
/// Check whether the current token is the specified one and report an error
/// if it isn't. \returns false if it reported an error.
/// The extra params \p where, \p what and \p whatLoc are optional and are
/// documented in errorExpected().
bool need(TokenKind kind, const char *where, const char *what, SMLoc whatLoc);
/// Report an error to the SourceErrorManager.
void error(SMLoc loc, const llvh::Twine &message) {
sm_.error(loc, message, Subsystem::Parser);
}
/// Report an error to the SourceErrorManager.
void error(SMRange range, const llvh::Twine &message) {
sm_.error(range, message, Subsystem::Parser);
}
/// Report an error using the current token's location.
void error(const llvh::Twine &msg) {
error(tok_->getSourceRange(), msg);
}
/// Emit an error at the specified source location and range. If the maximum
/// number of errors has been reached, return false and move the scanning
/// pointer to EOF.
/// \return false if too many errors have been emitted and we need to abort.
bool error(SMLoc loc, SMRange range, const llvh::Twine &msg);
/// Check whether the current token is the specified one and if it is, consume
/// it, otherwise an report an error. \returns false if it reported an error.
/// \param grammarContext enable recognizing either "/" and "/=", or a regexp
/// when consuming the next token.
/// The extra params \p where, \p what and \p whatLoc are optional and are
/// documented in errorExpected().
bool eat(
TokenKind kind,
JSLexer::GrammarContext grammarContext,
const char *where,
const char *what,
SMLoc whatLoc);
/// Check whether the current token is the specified one and consume it if so.
/// \returns true if the token matched.
bool checkAndEat(
TokenKind kind,
JSLexer::GrammarContext grammarContext =
JSLexer::GrammarContext::AllowRegExp);
/// Check whether the current token is the specified identifier and consume it
/// if so. \returns true if the token matched.
bool checkAndEat(
UniqueString *ident,
JSLexer::GrammarContext grammarContext =
JSLexer::GrammarContext::AllowRegExp);
/// Check whether the current token is the specified one. \returns true if it
/// is.
bool check(TokenKind kind) const {
return tok_->getKind() == kind;
}
/// \return true if the current token is the specified identifier.
bool check(UniqueString *ident) const {
return tok_->getKind() == TokenKind::identifier &&
tok_->getIdentifier() == ident;
}
/// Check whether the current token is one of the specified ones. \returns
/// true if it is.
bool check(TokenKind kind1, TokenKind kind2) const {
return tok_->getKind() == kind1 || tok_->getKind() == kind2;
}
template <typename T>
inline bool checkN(T t) const {
return check(t);
}
/// Convenience function to compare against more than 2 token kinds. We still
/// use check() for 2 or 1 kinds because it is more typesafe.
template <typename Head, typename... Tail>
inline bool checkN(Head h, Tail... tail) const {
return check(h) || checkN(tail...);
}
/// Check whether the current token is an assignment operator.
bool checkAssign() const;
/// Check whether the current token begins a Declaration.
bool checkDeclaration() {
if (checkN(
TokenKind::rw_function,
letIdent_,
TokenKind::rw_const,
TokenKind::rw_class) ||
(check(asyncIdent_) && checkAsyncFunction())) {
return true;
}
#if HERMES_PARSE_FLOW
if (context_.getParseFlow()) {
if (check(opaqueIdent_)) {
auto optNext = lexer_.lookahead1(llvh::None);
return optNext.hasValue() && (*optNext == TokenKind::identifier);
}
if (checkN(typeIdent_, interfaceIdent_)) {
auto optNext = lexer_.lookahead1(llvh::None);
return optNext.hasValue() && *optNext == TokenKind::identifier;
}
if (check(TokenKind::rw_interface)) {
return true;
}
if (check(TokenKind::rw_enum)) {
return true;
}
}
#endif
#if HERMES_PARSE_TS
if (context_.getParseTS()) {
if (checkN(typeIdent_, interfaceIdent_, namespaceIdent_)) {
auto optNext = lexer_.lookahead1(llvh::None);
return optNext.hasValue() && *optNext == TokenKind::identifier;
}
if (check(TokenKind::rw_interface)) {
return true;
}
if (check(TokenKind::rw_enum)) {
return true;
}
}
#endif
return false;
}
bool checkDeclareType() {
#if HERMES_PARSE_FLOW
if (check(declareIdent_)) {
auto optNext = lexer_.lookahead1(llvh::None);
if (!optNext)
return false;
TokenKind next = *optNext;
return next == TokenKind::identifier || next == TokenKind::rw_interface ||
next == TokenKind::rw_var || next == TokenKind::rw_function ||
next == TokenKind::rw_class || next == TokenKind::rw_export;
}
#endif
return false;
}
/// Check whether the current token begins a template literal.
bool checkTemplateLiteral() const {
return check(TokenKind::no_substitution_template, TokenKind::template_head);
}
/// Check whether the current token can be the token after the end of an
/// AssignmentExpression.
bool checkEndAssignmentExpression() const;
/// Check whether we match 'async [no LineTerminator here] function'.
/// \pre the current token is 'async'.
bool checkAsyncFunction();
/// Performs automatic semicolon insertion and optionally reports an error
/// if a semicolon is missing and cannot be inserted.
/// \param optional if set to true, an error will not be reported.
/// \return false if a semi was not found and it could not be inserted.
bool eatSemi(bool optional = false);
/// Process a directive by updating internal flags appropriately. Currently
/// we only care about "use strict".
void processDirective(UniqueString *directive);
/// Check whether the recursion depth has been exceeded, and if so generate
/// and error and return true.
/// If the depth has not been exceeded return false.
/// NOTE: This is intended to stay inline to avoid a function call unless the
/// depth was actually exceeded.
inline bool recursionDepthCheck() {
if (LLVM_LIKELY(recursionDepth_ < MAX_RECURSION_DEPTH)) {
return false;
}
return recursionDepthExceeded();
}
/// Generate an error and return true.
bool recursionDepthExceeded();
// Parser functions. All of these correspond more or less directly to grammar
// productions, except in cases where the grammar is ambiguous, but even then
// the name should be self-explanatory.
Optional<ESTree::ProgramNode *> parseProgram();
/// Parse a function declaration, and optionally force an eager parse.
/// Otherwise, the function will be skipped in lazy mode and a dummy returned.
/// \param param [Yield]
Optional<ESTree::FunctionDeclarationNode *> parseFunctionDeclaration(
Param param,
bool forceEagerly = false);
/// Parse a function declaration or expression, and optionally force an eager
/// parse. Otherwise, the function will be skipped in lazy mode and a dummy
/// returned.
/// \param param [Yield, Default]
Optional<ESTree::FunctionLikeNode *> parseFunctionHelper(
Param param,
bool isDeclaration,
bool forceEagerly = false);
/// Parse FormalParameters or UniqueFormalParameters with the leading '(' and
/// the trailing ')'.
/// \pre the current token must be '('.
/// \param[out] paramList populated with the FormalParameters.
/// \return true on success, false on failure.
bool parseFormalParameters(Param param, ESTree::NodeList ¶mList);
/// \param param [Yield, Return]
Optional<ESTree::Node *> parseStatement(Param param);
enum class AllowImportExport {
No,
Yes,
};
/// Parse a statement list.
/// \param param [Yield]
/// \param until stop parsing when this token is enountered
/// \param parseDirectives if true, recognize directives in the beginning of
/// the block. Specifically, it will recognize "use strict" and enable
/// strict mode.
/// \return a dummy value for consistency.
template <typename... Tail>
Optional<bool> parseStatementList(
Param param,
TokenKind until,
bool parseDirectives,
AllowImportExport allowImportExport,
ESTree::NodeList &stmtList,
Tail... otherUntil);
bool parseStatementListItem(
Param param,
AllowImportExport allowImportExport,
ESTree::NodeList &stmtList);
/// Parse a statement block.
/// \param param [Yield, Return]
/// \param grammarContext context to be used when consuming the closing brace.
/// \param parseDirectives if true, recognize directives in the beginning of
/// the block. Specifically, it will recognize "use strict" and enable
/// strict mode.
Optional<ESTree::BlockStatementNode *> parseBlock(
Param param,
JSLexer::GrammarContext grammarContext = JSLexer::AllowRegExp,
bool parseDirectives = false);
/// Parse a function body. This is a wrapper around parseBlock for the
/// purposes of lazy parsing.
/// \param paramYield the value of paramYield at the start of the function,
/// used for lazy compilation.
/// \param paramAwait the value of paramAwait at the start of the function,
/// used for lazy compilation.
/// \param param [Yield]
Optional<ESTree::BlockStatementNode *> parseFunctionBody(
Param param,
bool eagerly,
bool paramYield,
bool paramAwait,
JSLexer::GrammarContext grammarContext = JSLexer::AllowRegExp,
bool parseDirectives = false);
/// Parse a declaration.
/// \param param [Yield]
Optional<ESTree::Node *> parseDeclaration(Param param);
/// Check if the provided string is a valid binding identifier.
/// Can be used to validate identifiers after we've passed lexing them.
/// The caller must report any errors if this function returns false.
/// \param param [Yield]
/// \param range the source range of the identifier to validate.
/// \param id the string to be validated.
/// \param kind the TokenKind provided when the string was lexed.
/// \return true if \p id is a valid binding identifier.
bool validateBindingIdentifier(
Param param,
SMRange range,
UniqueString *id,
TokenKind kind);
/// ES 2015 12.1
/// Does not generate an error. It is expected that the caller will do it.
/// \param param [Yield]
Optional<ESTree::IdentifierNode *> parseBindingIdentifier(Param param);
/// Parse a VariableStatement or LexicalDeclaration.
/// \param param [In, Yield]
Optional<ESTree::VariableDeclarationNode *> parseLexicalDeclaration(
Param param);
/// Parse a VariableStatement or LexicalDeclaration.
/// \param param [Yield]
Optional<ESTree::VariableDeclarationNode *> parseVariableStatement(
Param param);
/// Parse a PrivateName starting with the '#'.
Optional<ESTree::PrivateNameNode *> parsePrivateName();
/// Parse a list of variable declarations. \returns a dummy value but the
/// optionality still encodes the error condition.
/// \param param [In, Yield]
/// \param declLoc is the location of the `rw_var` token and is used for error
/// display.
Optional<const char *> parseVariableDeclarationList(
Param param,
ESTree::NodeList &declList,
SMLoc declLoc);
/// \param param [In, Yield]
/// \param declLoc is the location of the let/var/const token and is used for
/// error display.
Optional<ESTree::VariableDeclaratorNode *> parseVariableDeclaration(
Param param,
SMLoc declLoc);
/// Ensure that all destructuring declarations in the specified declaration
/// node are initialized and report errors if they are not.
void ensureDestructuringInitialized(
ESTree::VariableDeclarationNode *declNode);
Optional<ESTree::Node *> parseBindingPattern(Param param);
Optional<ESTree::ArrayPatternNode *> parseArrayBindingPattern(Param param);
Optional<ESTree::Node *> parseBindingElement(Param param);
Optional<ESTree::Node *> parseBindingRestElement(Param param);
/// Parse "'=' Initializer" in a binding pattern.
Optional<ESTree::AssignmentPatternNode *> parseBindingInitializer(
Param param,
ESTree::Node *left);
Optional<ESTree::ObjectPatternNode *> parseObjectBindingPattern(Param param);
Optional<ESTree::PropertyNode *> parseBindingProperty(Param param);
Optional<ESTree::Node *> parseBindingRestProperty(Param param);
Optional<ESTree::EmptyStatementNode *> parseEmptyStatement();
/// \param param [Yield, Return]
Optional<ESTree::Node *> parseExpressionOrLabelledStatement(Param param);
/// \param param [Yield, Return]
Optional<ESTree::IfStatementNode *> parseIfStatement(Param param);
/// \param param [Yield, Return]
Optional<ESTree::WhileStatementNode *> parseWhileStatement(Param param);
/// \param param [Yield, Return]
Optional<ESTree::DoWhileStatementNode *> parseDoWhileStatement(Param param);
/// \param param [Yield, Return]
Optional<ESTree::Node *> parseForStatement(Param param);
Optional<ESTree::ContinueStatementNode *> parseContinueStatement();
Optional<ESTree::BreakStatementNode *> parseBreakStatement();
Optional<ESTree::ReturnStatementNode *> parseReturnStatement();
/// \param param [Yield, Return]
Optional<ESTree::WithStatementNode *> parseWithStatement(Param param);
/// \param param [Yield, Return]
Optional<ESTree::SwitchStatementNode *> parseSwitchStatement(Param param);
/// \param param [Yield]
Optional<ESTree::ThrowStatementNode *> parseThrowStatement(Param param);
/// \param param [Yield, Return]
Optional<ESTree::TryStatementNode *> parseTryStatement(Param param);
Optional<ESTree::DebuggerStatementNode *> parseDebuggerStatement();
Optional<ESTree::Node *> parsePrimaryExpression();
Optional<ESTree::ArrayExpressionNode *> parseArrayLiteral();
Optional<ESTree::ObjectExpressionNode *> parseObjectLiteral();
Optional<ESTree::Node *> parseSpreadElement();
Optional<ESTree::Node *> parsePropertyAssignment(bool eagerly);
/// Parse a property key which is a string, number or identifier. If it is
/// neither, reports an error.
Optional<ESTree::Node *> parsePropertyName();
/// Parse a template literal starting at either TemplateHead or
/// NoSubstitutionTemplate.
/// \param param [Yield, Tagged]
Optional<ESTree::Node *> parseTemplateLiteral(Param param);
Optional<ESTree::FunctionExpressionNode *> parseFunctionExpression(
bool forceEagerly = false);
/// Indicates whether certain functions should recognize `?.` as a chaining
/// operator. `?.` is not allowed in a NewExpression, for example.
enum class IsConstructorCall { No, Yes };
/// Parse OptionalExpression except the MemberExpression production starting
/// with "new".
Optional<ESTree::Node *> parseOptionalExpressionExceptNew(
IsConstructorCall isConstructorCall);
/// The "tail" of \c parseOptionalExpressionExceptNew(). It parses the
/// optional MemberExpression following the base PrimaryExpression. It is
/// ordinarily called by \c parseOptionalExpressionExceptNew(), but we need
/// to call it explicitly after parsing "new.target".
Optional<ESTree::Node *> parseOptionalExpressionExceptNew_tail(
IsConstructorCall isConstructorCall,
SMLoc startLoc,
ESTree::Node *expr);
/// Returns a dummy Optional<> just to indicate success or failure like all
/// other functions.
Optional<const char *> parseArguments(
ESTree::NodeList &argList,
SMLoc &endLoc);
/// \param startLoc the start location of the expression
/// \param objectLoc the location of the object part of the expression and is
/// used for error display.
/// \param seenOptionalChain true if there was a ?. leading up to the
/// member select (set by parseOptionalExpressionExceptNew)
Optional<ESTree::Node *> parseMemberSelect(
SMLoc startLoc,
SMLoc objectLoc,
ESTree::NodePtr expr,
bool seenOptionalChain);
/// \param startLoc the start location of the expression, used for error
/// display.
/// \param typeArgs the optional type arguments parsed before the '('.
/// \param seenOptionalChain true when `?.` is used in the chain leading
/// to this call expression
/// \param optional true when `?.` is used immediately prior to the Arguments.
Optional<ESTree::Node *> parseCallExpression(
SMLoc startLoc,
ESTree::NodePtr expr,
ESTree::NodePtr typeArgs,
bool seenOptionalChain,
bool optional);
/// Parse a \c NewExpression or a \c OptionalExpression.
/// After we have recognized "new", there is an apparent ambiguity in the
/// grammar between \c NewExpression and \c MemberExpression:
///
/// \code
/// NewExpression:
/// MemberExpression
/// new NewExpression
///
/// MemberExpression:
/// new MemberExpression Arguments
/// \endcode
///
/// The difference is that in the first case there are no arguments to the
/// constructor.
/// \param isConstructorCall is Yes when we have already recognized a "new".
/// This is used because we must disallow the ?. token before the
/// arguments to a constructor call only when "new" is used. For example,
/// the code `new a?.b()` is not valid.
Optional<ESTree::Node *> parseNewExpressionOrOptionalExpression(
IsConstructorCall isConstructorCall);
Optional<ESTree::Node *> parseLeftHandSideExpression();
Optional<ESTree::Node *> parsePostfixExpression();
Optional<ESTree::Node *> parseUnaryExpression();
/// Parse a binary expression using a precedence table, in order to decrease
/// recursion depth.
Optional<ESTree::Node *> parseBinaryExpression(Param param);
/// Whether to allow a typed arrow function in the assignment expression.
enum class AllowTypedArrowFunction { No, Yes };
/// Whether to parse CoverTypedIdentifier nodes when seeing a `:`.
/// These can only be used as typed parameters in certain contexts.
enum class CoverTypedParameters { No, Yes };
Optional<ESTree::Node *> parseConditionalExpression(
Param param = ParamIn,
CoverTypedParameters coverTypedParameters = CoverTypedParameters::Yes);
Optional<ESTree::YieldExpressionNode *> parseYieldExpression(
Param param = ParamIn);
Optional<ESTree::ClassDeclarationNode *> parseClassDeclaration(Param param);
Optional<ESTree::ClassExpressionNode *> parseClassExpression();
enum class ClassParseKind { Declaration, Expression };
/// Parse the class starting after the name (which is optional).
/// \param name the name if provided, nullptr if otherwise.
/// \param if the name is provided, the type params if provided, nullptr
/// otherwise.
/// \param kind whether the class is a declaration or expression.
Optional<ESTree::Node *> parseClassTail(
SMLoc startLoc,
ESTree::Node *name,
ESTree::Node *typeParams,
ClassParseKind kind);
Optional<ESTree::ClassBodyNode *> parseClassBody(SMLoc startLoc);
Optional<ESTree::Node *> parseClassElement(
bool isStatic,
SMRange startRange,
bool declare,
bool isPrivate,
bool eagerly = false);
/// Reparse the specified node as arrow function parameter list and store the
/// parameter list in \p paramList. Print an error and return false on error,
/// otherwise return true.
/// \param[in/out] isAsync the arrow function is async. The caller may already
/// know this prior to calling this function, in which case `true` should be
/// passed. Otherwise, this function will try to reparse a call expression
/// into an async arrow function.
bool reparseArrowParameters(
ESTree::Node *node,
ESTree::NodeList ¶mList,
bool &isAsync);
/// \param forceAsync set to true when it is already known that the arrow
/// function expression is 'async'. This occurs when there are no parens
/// around the argument list.
Optional<ESTree::Node *> parseArrowFunctionExpression(
Param param,
ESTree::Node *leftExpr,
ESTree::Node *typeParams,
ESTree::Node *returnType,
ESTree::Node *predicate,
SMLoc startLoc,
AllowTypedArrowFunction allowTypedArrowFunction,
bool forceAsync);
#if HERMES_PARSE_FLOW
Optional<ESTree::Node *> tryParseTypedAsyncArrowFunction(Param param);
/// Attempt to parse a CoverTypedIdentifierNode which consists of a
/// node which may be an arrow parameter, a colon, and a type.
/// \param test the LHS of the potential CoverTypedIdentifierNode.
/// \param optional whether the potential CoverTypedIdentifierNode is
/// optional, meaning there was a question mark preceding the type annotation
/// \return nullptr if there was no error but attempting to parse the
/// node is not possible because \p test can't be a formal parameter,
/// or there wasn't a colon in the first place, None on error.
Optional<ESTree::Node *> tryParseCoverTypedIdentifierNode(
ESTree::Node *test,
bool optional);
#endif
/// Reparse an ArrayExpression into an ArrayPattern.
/// \param inDecl whether this is a declaration context or assignment.
Optional<ESTree::Node *> reparseArrayAsignmentPattern(
ESTree::ArrayExpressionNode *AEN,
bool inDecl);
/// Reparse an ObjectExpression into an ObjectPattern.
/// \param inDecl whether this is a declaration context or assignment.
Optional<ESTree::Node *> reparseObjectAssignmentPattern(
ESTree::ObjectExpressionNode *OEN,
bool inDecl);
/// Reparse an ArrayExpression or ObjectExpression into ArrayPattern or
/// ObjectPattern.
/// \param inDecl whether this is a declaration context or assignment.
Optional<ESTree::Node *> reparseAssignmentPattern(
ESTree::Node *node,
bool inDecl);
Optional<ESTree::Node *> parseAssignmentExpression(
Param param = ParamIn,
AllowTypedArrowFunction allowTypedArrowFunction =
AllowTypedArrowFunction::Yes,
CoverTypedParameters coverTypedParameters = CoverTypedParameters::Yes,
ESTree::Node *typeParams = nullptr);
Optional<ESTree::Node *> parseExpression(
Param param = ParamIn,
CoverTypedParameters coverTypedParameters = CoverTypedParameters::Yes);
/// Parse a FromClause and return the string literal representing the source.
Optional<ESTree::StringLiteralNode *> parseFromClause();
bool parseAssertClause(ESTree::NodeList &attributes);
Optional<ESTree::ImportDeclarationNode *> parseImportDeclaration();
/// \return the kind of the import.
Optional<UniqueString *> parseImportClause(ESTree::NodeList &specifiers);
Optional<ESTree::Node *> parseNameSpaceImport();
bool parseNamedImports(ESTree::NodeList &specifiers);
Optional<ESTree::ImportSpecifierNode *> parseImportSpecifier(SMLoc importLoc);
Optional<ESTree::Node *> parseExportDeclaration();
/// \param[out] specifiers the list of parsed specifiers.
/// \param[out] invalids ranges of potentially invalid exported symbols,
/// only if the clause is eventually followed by a FromClause.
bool parseExportClause(
ESTree::NodeList &specifiers,
llvh::SmallVectorImpl<SMRange> &invalids);
/// \param[out] invalids ranges of potentially invalid exported symbols,
/// only if the clause is eventually followed by a FromClause.
/// Appended to if an exported name may be invalid.
Optional<ESTree::Node *> parseExportSpecifier(
SMLoc exportLoc,
llvh::SmallVectorImpl<SMRange> &invalids);
/// If the current token can be recognised as a directive (ES5.1 14.1),
/// process the directive and return the allocated directive statement.
/// Note that this function never needs to returns an error.
/// \return the allocated directive statement if this is a directive, or
/// null if it isn't.
ESTree::ExpressionStatementNode *parseDirective();
#if HERMES_PARSE_JSX
Optional<ESTree::Node *> parseJSX();
Optional<ESTree::Node *> parseJSXElement(SMLoc start);
Optional<ESTree::Node *> parseJSXFragment(SMLoc start);
Optional<ESTree::JSXOpeningElementNode *> parseJSXOpeningElement(SMLoc start);
Optional<ESTree::Node *> parseJSXSpreadAttribute();
Optional<ESTree::Node *> parseJSXAttribute();
/// \param children populated with the JSX children.
/// \return the JSXClosingElement or JSXClosingFragment.
Optional<ESTree::Node *> parseJSXChildren(ESTree::NodeList &children);
Optional<ESTree::Node *> parseJSXChildExpression(SMLoc start);
/// Parse JSXClosingElement or JSXClosingFragment.
Optional<ESTree::Node *> parseJSXClosing(SMLoc start);
enum class AllowJSXMemberExpression { No, Yes };
/// \param allowMemberExpression whether JSXMemberExpression (foo.bar) is a
/// valid parse of the JSXElementName.
Optional<ESTree::Node *> parseJSXElementName(
AllowJSXMemberExpression allowJSXMemberExpression);
#endif
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
enum class AllowAnonFunctionType { No, Yes };
Optional<ESTree::Node *> parseTypeAnnotation(
Optional<SMLoc> wrappedStart = None,
AllowAnonFunctionType allowAnonFunctionType =
AllowAnonFunctionType::Yes) {
assert(context_.getParseFlow() || context_.getParseTS());
#if HERMES_PARSE_FLOW
if (context_.getParseFlow())
return parseTypeAnnotationFlow(wrappedStart, allowAnonFunctionType);
#endif
#if HERMES_PARSE_TS
return parseTypeAnnotationTS(wrappedStart);
#endif
}
#endif
#if HERMES_PARSE_FLOW
/// \param wrappedStart if set, the type annotation should be wrapped in a
/// TypeAnnotationNode starting at this location. If not set, the type
/// annotation should not be wrapped in a TypeAnnotationNode.
Optional<ESTree::Node *> parseTypeAnnotationFlow(
Optional<SMLoc> wrappedStart = None,
AllowAnonFunctionType allowAnonFunctionType = AllowAnonFunctionType::Yes);
/// Allow 'declare export type', which is only allowed in 'declare module'.
enum class AllowDeclareExportType { No, Yes };
Optional<ESTree::Node *> parseFlowDeclaration();
Optional<ESTree::Node *> parseDeclareFLow(
SMLoc start,
AllowDeclareExportType allowDeclareExportType);
enum class TypeAliasKind { None, Declare, Opaque, DeclareOpaque };
Optional<ESTree::Node *> parseTypeAliasFlow(SMLoc start, TypeAliasKind kind);
/// \param declareStart if set, parse a DeclareInterfaceNode starting at
/// this location. If not set, parse an InterfaceDeclarationNode.
Optional<ESTree::Node *> parseInterfaceDeclarationFlow(
Optional<SMLoc> declareStart = None);
/// \pre current token is 'extends' or '{'.
/// \param[out] extends the super-interfaces for the parsed interface.
/// \return the body of the interface
Optional<ESTree::Node *> parseInterfaceTailFlow(
SMLoc start,
ESTree::NodeList &extends);
bool parseInterfaceExtends(SMLoc start, ESTree::NodeList &extends);
Optional<ESTree::Node *> parseDeclareFunctionFlow(SMLoc start);
Optional<ESTree::Node *> parseDeclareClassFlow(SMLoc start);
Optional<ESTree::Node *> parseDeclareExportFlow(
SMLoc start,
AllowDeclareExportType allowDeclareExportType);
Optional<ESTree::Node *> parseDeclareModuleFlow(SMLoc start);
Optional<ESTree::Node *> parseExportTypeDeclarationFlow(SMLoc start);
Optional<ESTree::Node *> parseUnionTypeAnnotationFlow();
Optional<ESTree::Node *> parseIntersectionTypeAnnotationFlow();
Optional<ESTree::Node *> parseAnonFunctionWithoutParensTypeAnnotationFlow();
Optional<ESTree::Node *> parsePrefixTypeAnnotationFlow();
Optional<ESTree::Node *> parsePostfixTypeAnnotationFlow();
Optional<ESTree::Node *> parsePrimaryTypeAnnotationFlow();
Optional<ESTree::Node *> parseTupleTypeAnnotationFlow();
Optional<ESTree::Node *> parseFunctionTypeAnnotationFlow();
Optional<ESTree::Node *> parseFunctionTypeAnnotationWithParamsFlow(
SMLoc start,
ESTree::NodeList &¶ms,
ESTree::Node *thisConstraint,
ESTree::Node *rest,
ESTree::Node *typeParams);
Optional<ESTree::Node *> parseFunctionOrGroupTypeAnnotationFlow();
/// Whether to allow 'proto' properties in an object type annotation.
enum class AllowProtoProperty { No, Yes };
/// Whether to allow 'static' properties in an object type annotation.
enum class AllowStaticProperty { No, Yes };
/// Whether to allow spread properties in an object type annotation.
enum class AllowSpreadProperty { No, Yes };
Optional<ESTree::Node *> parseObjectTypeAnnotationFlow(
AllowProtoProperty allowProtoProperty,
AllowStaticProperty allowStaticProperty,
AllowSpreadProperty allowSpreadProperty);
bool parseObjectTypePropertiesFlow(
AllowProtoProperty allowProtoProperty,
AllowStaticProperty allowStaticProperty,
AllowSpreadProperty allowSpreadProperty,
ESTree::NodeList &properties,
ESTree::NodeList &indexers,
ESTree::NodeList &callProperties,
ESTree::NodeList &internalSlots,
bool &inexact);
bool parsePropertyTypeAnnotationFlow(
AllowProtoProperty allowProtoProperty,
AllowStaticProperty allowStaticProperty,
ESTree::NodeList &properties,
ESTree::NodeList &indexers,
ESTree::NodeList &callProperties,
ESTree::NodeList &internalSlots);
/// Current token must be immediately after opening '['.
Optional<ESTree::Node *> parseTypeIndexerPropertyFlow(
SMLoc start,
ESTree::Node *variance,
bool isStatic);
Optional<ESTree::Node *> parseTypePropertyFlow(
SMLoc start,
ESTree::Node *variance,
bool isStatic,
bool proto,
ESTree::Node *key);
Optional<ESTree::Node *>
parseMethodTypePropertyFlow(SMLoc start, bool isStatic, ESTree::Node *key);
Optional<ESTree::Node *> parseGetOrSetTypePropertyFlow(
SMLoc start,
bool isStatic,
bool isGetter,
ESTree::Node *key);
Optional<ESTree::Node *> parseTypeParamsFlow();
Optional<ESTree::Node *> parseTypeParamFlow();
Optional<ESTree::Node *> parseTypeArgsFlow();
/// \param[out] params the parameters, populated by reference.
/// \param[out] thisConstraint the type annotation for 'this'.
/// \return the rest parameter if it exists, nullptr otherwise. None still
/// indicates an error.
Optional<ESTree::FunctionTypeParamNode *>
parseFunctionTypeAnnotationParamsFlow(
ESTree::NodeList ¶ms,
ESTree::NodePtr &thisConstraint);
Optional<ESTree::FunctionTypeParamNode *>
parseFunctionTypeAnnotationParamFlow();
Optional<ESTree::Node *> parseTypeCallPropertyFlow(
SMLoc start,
bool isStatic);
Optional<ESTree::GenericTypeAnnotationNode *> parseGenericTypeFlow();
Optional<ESTree::ClassImplementsNode *> parseClassImplementsFlow();
/// Parse a property which looks like a method, starting at the opening '('.
/// \param typeParams (optional) type params between '<' and '>' before '('.
Optional<ESTree::FunctionTypeAnnotationNode *>
parseMethodishTypeAnnotationFlow(SMLoc start, ESTree::Node *typeParams);
Optional<ESTree::Node *> parsePredicateFlow();
Optional<ESTree::IdentifierNode *> reparseTypeAnnotationAsIdentifierFlow(
ESTree::Node *typeAnnotation);
enum class EnumKind {
String,
Number,
Boolean,
Symbol,
};
static llvh::StringRef enumKindStrFlow(EnumKind kind) {
switch (kind) {
case EnumKind::String:
return "string";
case EnumKind::Number:
return "number";
case EnumKind::Boolean:
return "boolean";
case EnumKind::Symbol:
return "symbol";
}
llvm_unreachable("No other kind of enum");
}
static OptValue<EnumKind> getMemberEnumKindFlow(ESTree::Node *member) {
switch (member->getKind()) {
case ESTree::NodeKind::EnumStringMember:
return EnumKind::String;
case ESTree::NodeKind::EnumNumberMember:
return EnumKind::Number;
case ESTree::NodeKind::EnumBooleanMember:
return EnumKind::Boolean;
default:
return None;
}
}
Optional<ESTree::Node *> parseEnumDeclarationFlow();
Optional<ESTree::Node *> parseEnumBodyFlow(
OptValue<EnumKind> optKind,
Optional<SMLoc> explicitTypeStart);
Optional<ESTree::Node *> parseEnumMemberFlow();
#endif
#if HERMES_PARSE_TS
/// Whether a TS function type is a constructor.
enum class IsConstructorType { No, Yes };
Optional<ESTree::Node *> parseTypeAnnotationTS(
Optional<SMLoc> wrappedStart = None);
Optional<ESTree::Node *> parseTSDeclaration();
Optional<ESTree::Node *> parseTSTypeAliasDeclaration(SMLoc start);
Optional<ESTree::Node *> parseTSInterfaceDeclaration();
Optional<ESTree::Node *> parseTSEnumDeclaration();
Optional<ESTree::Node *> parseTSEnumMember();
Optional<ESTree::Node *> parseTSNamespaceDeclaration();
Optional<ESTree::Node *> parseTSTypeParameters();
Optional<ESTree::Node *> parseTSTypeParameter();
Optional<ESTree::Node *> parseTSUnionType();
Optional<ESTree::Node *> parseTSIntersectionType();
Optional<ESTree::Node *> parseTSTupleType();
Optional<ESTree::Node *> parseTSFunctionOrParenthesizedType(
SMLoc start,
ESTree::Node *typeParams,
IsConstructorType isConstructorType);
bool parseTSFunctionTypeParams(SMLoc start, ESTree::NodeList ¶ms);
Optional<ESTree::Node *> parseTSFunctionTypeParam();
Optional<ESTree::Node *> parseTSFunctionTypeWithParams(
SMLoc start,
ESTree::NodeList &¶ms,
ESTree::Node *typeParams);
Optional<ESTree::Node *> parseTSObjectType();
Optional<ESTree::Node *> parseTSObjectTypeMember();
Optional<ESTree::Node *> parseTSIndexSignature(SMLoc start);
Optional<ESTree::Node *> parseTSPostfixType();
Optional<ESTree::Node *> parseTSPrimaryType();
Optional<ESTree::TSTypeReferenceNode *> parseTSTypeReference();
Optional<ESTree::Node *> parseTSQualifiedName();
Optional<ESTree::Node *> parseTSTypeQuery();
Optional<ESTree::Node *> parseTSTypeArguments();
ESTree::Node *reparseIdentifierAsTSTypeAnnotation(
ESTree::IdentifierNode *ident);
#endif
/// RAII to save and restore the current setting of "strict mode" and
/// "seen directives".
class SaveStrictModeAndSeenDirectives {
JSParserImpl *const parser_;
const bool oldStrictMode_;
const unsigned oldSeenDirectiveSize_;
public:
explicit SaveStrictModeAndSeenDirectives(JSParserImpl *parser)
: parser_(parser),
oldStrictMode_(parser->isStrictMode()),
oldSeenDirectiveSize_(parser->getSeenDirectives().size()) {}
~SaveStrictModeAndSeenDirectives() {
parser_->setStrictMode(oldStrictMode_);
parser_->getSeenDirectives().resize(oldSeenDirectiveSize_);
}
};
/// RAII to track the recursion depth.
class TrackRecursion {
JSParserImpl *const parser_;
public:
TrackRecursion(JSParserImpl *parser) : parser_(parser) {
++parser_->recursionDepth_;
}
~TrackRecursion() {
--parser_->recursionDepth_;
}
};
/// Declare a RAII recursion tracker. Check whether the recursion limit has
/// been exceeded, and if so generate an error and return an empty
/// llvh::Optional<>.
/// The macro only works from inside JSParserImpl methods.
#define CHECK_RECURSION \
TrackRecursion trackRecursion{this}; \
if (recursionDepthCheck()) \
return llvh::None;
};
} // namespace detail
} // namespace parser
} // namespace hermes
#endif // HERMES_PARSER_JSPARSERIMPL_H