Optional JSParserImpl::parseFunctionHelper()

in lib/Parser/JSParserImpl.cpp [343:558]


Optional<ESTree::FunctionLikeNode *> JSParserImpl::parseFunctionHelper(
    Param param,
    bool isDeclaration,
    bool forceEagerly) {
  // function or async function
  assert(check(TokenKind::rw_function) || check(asyncIdent_));
  bool isAsync = check(asyncIdent_);

  SMLoc startLoc = advance().Start;

  if (isAsync) {
    // async function
    //       ^
    advance();
  }

  bool isGenerator = checkAndEat(TokenKind::star);

  // newParamYield setting per the grammar:
  // FunctionDeclaration: BindingIdentifier[?Yield, ?Await]
  // FunctionExpression: BindingIdentifier[~Yield, ~Await]
  // GeneratorFunctionDeclaration: BindingIdentifier[?Yield, ?Await]
  // GeneratorFunctionExpression: BindingIdentifier[+Yield, ~Await]
  // AsyncFunctionDeclaration: BindingIdentifier[?Yield, ?Await]
  // AsyncFunctionExpression: BindingIdentifier[+Yield, +Await]
  // AsyncGeneratorDeclaration: BindingIdentifier[?Yield, ?Await]
  // AsyncGeneratorExpression: BindingIdentifier[+Yield, +Await]
  bool nameParamYield = isDeclaration ? paramYield_ : isGenerator;
  llvh::SaveAndRestore<bool> saveNameParamYield(paramYield_, nameParamYield);
  bool nameParamAwait = isDeclaration ? paramAwait_ : isAsync;
  llvh::SaveAndRestore<bool> saveNameParamAwait(paramAwait_, nameParamAwait);

  // identifier
  auto optId = parseBindingIdentifier(Param{});
  // If this is a default function declaration, then we can match
  // [+Default] function ( FormalParameters ) { FunctionBody }
  // so the identifier is optional and we can make it nullptr.
  if (isDeclaration && !param.has(ParamDefault) && !optId) {
    errorExpected(
        TokenKind::identifier,
        "after 'function'",
        "location of 'function'",
        startLoc);
    return None;
  }

  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 HERMES_PARSE_TS
  if (context_.getParseTS() && check(TokenKind::less)) {
    auto optTypeParams = parseTSTypeParameters();
    if (!optTypeParams)
      return None;
    typeParams = *optTypeParams;
  }
#endif

  // (
  if (!need(
          TokenKind::l_paren,
          "at start of function parameter list",
          isDeclaration ? "function declaration starts here"
                        : "function expression starts here",
          startLoc)) {
    return None;
  }

  ESTree::NodeList paramList;

  llvh::SaveAndRestore<bool> saveArgsAndBodyParamYield(
      paramYield_, isGenerator);
  llvh::SaveAndRestore<bool> saveArgsAndBodyParamAwait(paramAwait_, isAsync);

  if (!parseFormalParameters(param, paramList))
    return None;

  ESTree::Node *returnType = nullptr;
  ESTree::Node *predicate = nullptr;
#if HERMES_PARSE_FLOW
  if (context_.getParseFlow() && check(TokenKind::colon)) {
    SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start;
    if (!check(checksIdent_)) {
      auto optRet = parseTypeAnnotationFlow(annotStart);
      if (!optRet)
        return None;
      returnType = *optRet;
    }

    if (check(checksIdent_)) {
      auto optPred = parsePredicateFlow();
      if (!optPred)
        return None;
      predicate = *optPred;
    }
  }
#endif
#if HERMES_PARSE_TS
  if (context_.getParseTS() && check(TokenKind::colon)) {
    SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start;
    if (!check(checksIdent_)) {
      auto optRet = parseTypeAnnotationTS(annotStart);
      if (!optRet)
        return None;
      returnType = *optRet;
    }
  }
#endif

  // {
  if (!need(
          TokenKind::l_brace,
          isDeclaration ? "in function declaration" : "in function expression",
          isDeclaration ? "start of function declaration"
                        : "start of function expression",
          startLoc)) {
    return None;
  }

  SaveStrictModeAndSeenDirectives saveStrictModeAndSeenDirectives{this};

  // Grammar context to be used when lexing the closing brace.
  auto grammarContext =
      isDeclaration ? JSLexer::AllowRegExp : JSLexer::AllowDiv;

  if (pass_ == PreParse) {
    // Create the nodes we want to keep before the AllocationScope.
    ESTree::FunctionLikeNode *node;

    if (isDeclaration) {
      auto *decl = new (context_) ESTree::FunctionDeclarationNode(
          optId ? *optId : nullptr,
          std::move(paramList),
          nullptr,
          typeParams,
          returnType,
          predicate,
          isGenerator,
          isAsync);
      // Initialize the node with a blank body.
      decl->_body = new (context_) ESTree::BlockStatementNode({});
      node = decl;
    } else {
      auto *expr = new (context_) ESTree::FunctionExpressionNode(
          optId ? *optId : nullptr,
          std::move(paramList),
          nullptr,
          typeParams,
          returnType,
          predicate,
          isGenerator,
          isAsync);
      // Initialize the node with a blank body.
      expr->_body = new (context_) ESTree::BlockStatementNode({});
      node = expr;
    }

    AllocationScope scope(context_.getAllocator());
    auto body = parseFunctionBody(
        Param{},
        false,
        saveArgsAndBodyParamYield.get(),
        saveArgsAndBodyParamAwait.get(),
        grammarContext,
        true);
    if (!body)
      return None;

    return setLocation(startLoc, body.getValue(), node);
  }

  auto parsedBody = parseFunctionBody(
      Param{},
      forceEagerly,
      saveArgsAndBodyParamYield.get(),
      saveArgsAndBodyParamAwait.get(),
      grammarContext,
      true);
  if (!parsedBody)
    return None;
  auto *body = parsedBody.getValue();

  ESTree::FunctionLikeNode *node;
  if (isDeclaration) {
    auto *decl = new (context_) ESTree::FunctionDeclarationNode(
        optId ? *optId : nullptr,
        std::move(paramList),
        body,
        typeParams,
        returnType,
        predicate,
        isGenerator,
        isAsync);
    node = decl;
  } else {
    auto *expr = new (context_) ESTree::FunctionExpressionNode(
        optId ? *optId : nullptr,
        std::move(paramList),
        body,
        typeParams,
        returnType,
        predicate,
        isGenerator,
        isAsync);
    node = expr;
  }
  return setLocation(startLoc, body, node);
}