Optional JSParserImpl::parsePropertyAssignment()

in lib/Parser/JSParserImpl.cpp [2562:2990]


Optional<ESTree::Node *> JSParserImpl::parsePropertyAssignment(bool eagerly) {
  SMLoc startLoc = tok_->getStartLoc();
  ESTree::NodePtr key = nullptr;

  SaveStrictModeAndSeenDirectives saveStrictModeAndSeenDirectives{this};

  bool computed = false;
  bool generator = false;
  bool async = false;
  bool method = false;

  if (check(getIdent_)) {
    UniqueString *ident = tok_->getResWordOrIdentifier();
    SMRange identRng = tok_->getSourceRange();
    advance();

    // This could either be a getter, or a property named 'get'.
    if (check(TokenKind::colon, TokenKind::l_paren)) {
      // This is just a property.
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
    } else if (context_.getParseTypes() && check(TokenKind::less)) {
      // This is a method definition.
      method = true;
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
#endif
    } else if (check(TokenKind::comma, TokenKind::r_brace)) {
      // If the next token is "," or "}", this is a shorthand property
      // definition.
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
      auto *value = setLocation(
          key,
          key,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
      return setLocation(
          startLoc,
          value,
          new (context_)
              ESTree::PropertyNode(key, value, initIdent_, false, false, true));
    } else {
      // A getter method.
      computed = check(TokenKind::l_square);
      auto optKey = parsePropertyName();
      if (!optKey)
        return None;

      if (!eat(
              TokenKind::l_paren,
              JSLexer::AllowRegExp,
              "in getter declaration",
              "start of getter declaration",
              startLoc))
        return None;
      if (!eat(
              TokenKind::r_paren,
              JSLexer::AllowRegExp,
              "in empty getter parameter list",
              "start of getter declaration",
              startLoc))
        return None;

      ESTree::Node *returnType = nullptr;
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
      if (context_.getParseTypes() && check(TokenKind::colon)) {
        SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start;
        auto optRet = parseTypeAnnotation(annotStart);
        if (!optRet)
          return None;
        returnType = *optRet;
      }
#endif

      llvh::SaveAndRestore<bool> oldParamYield(paramYield_, false);
      llvh::SaveAndRestore<bool> oldParamAwait(paramAwait_, false);
      if (!need(
              TokenKind::l_brace,
              "in getter declaration",
              "start of getter declaration",
              startLoc))
        return None;
      auto block = parseFunctionBody(
          ParamReturn,
          eagerly,
          oldParamYield.get(),
          oldParamAwait.get(),
          JSLexer::AllowRegExp,
          true);
      if (!block)
        return None;

      auto *funcExpr = new (context_) ESTree::FunctionExpressionNode(
          nullptr,
          ESTree::NodeList{},
          block.getValue(),
          nullptr,
          returnType,
          /* predicate */ nullptr,
          false,
          false);
      funcExpr->isMethodDefinition = true;
      setLocation(startLoc, block.getValue(), funcExpr);

      auto *node = new (context_) ESTree::PropertyNode(
          optKey.getValue(), funcExpr, getIdent_, computed, false, false);
      return setLocation(startLoc, block.getValue(), node);
    }
  } else if (check(setIdent_)) {
    UniqueString *ident = tok_->getResWordOrIdentifier();
    SMRange identRng = tok_->getSourceRange();
    advance();

    // This could either be a setter, or a property named 'set'.
    if (check(TokenKind::colon, TokenKind::l_paren)) {
      // This is just a property.
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
    } else if (context_.getParseTypes() && check(TokenKind::less)) {
      // This is a method definition.
      method = true;
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
#endif
    } else if (check(TokenKind::comma, TokenKind::r_brace)) {
      // If the next token is "," or "}", this is a shorthand property
      // definition.
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
      auto *value = setLocation(
          key,
          key,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
      return setLocation(
          startLoc,
          value,
          new (context_)
              ESTree::PropertyNode(key, value, initIdent_, false, false, true));
    } else {
      // A setter method.
      computed = check(TokenKind::l_square);
      auto optKey = parsePropertyName();
      if (!optKey)
        return None;

      llvh::SaveAndRestore<bool> oldParamYield(paramYield_, false);
      llvh::SaveAndRestore<bool> oldParamAwait(paramAwait_, false);

      ESTree::NodeList params;
      eat(TokenKind::l_paren,
          JSLexer::AllowRegExp,
          "in setter declaration",
          "start of setter declaration",
          startLoc);

      // PropertySetParameterList -> FormalParameter -> BindingElement
      auto optParam = parseBindingElement(Param{});
      if (!optParam)
        return None;
      params.push_back(**optParam);

      if (!eat(
              TokenKind::r_paren,
              JSLexer::AllowRegExp,
              "at end of setter parameter list",
              "start of setter declaration",
              startLoc))
        return None;

      ESTree::Node *returnType = nullptr;
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
      if (context_.getParseTypes() && check(TokenKind::colon)) {
        SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start;
        auto optRet = parseTypeAnnotation(annotStart);
        if (!optRet)
          return None;
        returnType = *optRet;
      }
#endif

      if (!need(
              TokenKind::l_brace,
              "in setter declaration",
              "start of setter declaration",
              startLoc))
        return None;
      auto block = parseFunctionBody(
          ParamReturn,
          eagerly,
          oldParamYield.get(),
          oldParamAwait.get(),
          JSLexer::AllowRegExp,
          true);
      if (!block)
        return None;

      auto *funcExpr = new (context_) ESTree::FunctionExpressionNode(
          nullptr,
          std::move(params),
          block.getValue(),
          nullptr,
          returnType,
          /* predicate */ nullptr,
          false,
          false);
      funcExpr->isMethodDefinition = true;
      setLocation(startLoc, block.getValue(), funcExpr);

      auto *node = new (context_) ESTree::PropertyNode(
          optKey.getValue(), funcExpr, setIdent_, computed, false, false);
      return setLocation(startLoc, block.getValue(), node);
    }
  } else if (check(asyncIdent_)) {
    UniqueString *ident = tok_->getResWordOrIdentifier();
    SMRange identRng = tok_->getSourceRange();
    advance();

    // This could either be an async function, or a property named 'async'.
    if (check(TokenKind::colon, TokenKind::l_paren)) {
      // This is just a property (or method) called 'async'.
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
    } else if (context_.getParseTypes() && check(TokenKind::less)) {
      // This is a method definition.
      method = true;
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
#endif
    } else if (check(TokenKind::comma, TokenKind::r_brace)) {
      // If the next token is "," or "}", this is a shorthand property
      // definition.
      key = setLocation(
          identRng,
          identRng,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
      auto *value = setLocation(
          key,
          key,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));
      return setLocation(
          startLoc,
          value,
          new (context_)
              ESTree::PropertyNode(key, value, initIdent_, false, false, true));
    } else {
      // This is an async function, parse the key and set `async` to true.
      async = true;
      method = true;
      generator = checkAndEat(TokenKind::star);
      computed = check(TokenKind::l_square);
      auto optKey = parsePropertyName();
      if (!optKey)
        return None;

      key = optKey.getValue();
    }
  } else if (check(TokenKind::identifier)) {
    auto *ident = tok_->getIdentifier();
    key = setLocation(
        tok_,
        tok_,
        new (context_) ESTree::IdentifierNode(ident, nullptr, false));
    advance();
    // If the next token is "," or "}", this is a shorthand property definition.
    if (check(TokenKind::comma, TokenKind::r_brace)) {
      auto *value = setLocation(
          key,
          key,
          new (context_) ESTree::IdentifierNode(ident, nullptr, false));

      return setLocation(
          startLoc,
          value,
          new (context_)
              ESTree::PropertyNode(key, value, initIdent_, false, false, true));
    }
  } else {
    generator = checkAndEat(TokenKind::star);
    computed = check(TokenKind::l_square);
    auto optKey = parsePropertyName();
    if (!optKey)
      return None;

    key = optKey.getValue();
  }

  ESTree::Node *value;
  bool shorthand = false;

  if (isa<ESTree::IdentifierNode>(key) && check(TokenKind::equal)) {
    // Check for CoverInitializedName: IdentifierReference Initializer
    auto startLoc = advance().Start;
    auto optInit = parseAssignmentExpression();
    if (!optInit)
      return None;

    shorthand = true;

    value = setLocation(
        startLoc,
        getPrevTokenEndLoc(),
        new (context_) ESTree::CoverInitializerNode(*optInit));
  } else if (check(TokenKind::l_paren, TokenKind::less) || async) {
    // Try this branch when we have '(' or '<' to indicate a method
    // or when we know this is async, because async must also indicate a method,
    // and we must avoid parsing ordinary properties from ':'.

    // Parse the MethodDefinition manually here.
    // Do not use `parseClassElement` because we had to parsePropertyName
    // in this function ourselves and check for SingleNameBindings, which are
    // not parsed with `parsePropertyName`.
    // MethodDefinition:
    // PropertyName "(" UniqueFormalParameters ")" "{" FunctionBody "}"
    //               ^
    llvh::SaveAndRestore<bool> oldParamYield(paramYield_, generator);
    llvh::SaveAndRestore<bool> oldParamAwait(paramAwait_, async);

    method = true;

    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,
            "in method definition",
            "start of method definition",
            startLoc))
      return None;

    ESTree::NodeList args{};
    if (!parseFormalParameters(Param{}, args))
      return None;

    ESTree::Node *returnType = nullptr;
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
    if (context_.getParseTypes() && check(TokenKind::colon)) {
      SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start;
      auto optRet = parseTypeAnnotation(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,
        oldParamYield.get(),
        oldParamAwait.get(),
        JSLexer::AllowRegExp,
        true);
    if (!optBody)
      return None;

    auto *funcExpr = new (context_) ESTree::FunctionExpressionNode(
        nullptr,
        std::move(args),
        optBody.getValue(),
        typeParams,
        returnType,
        /* predicate */ nullptr,
        generator,
        async);
    funcExpr->isMethodDefinition = true;
    setLocation(startLoc, optBody.getValue(), funcExpr);

    value = funcExpr;
  } else {
    if (!eat(
            TokenKind::colon,
            JSLexer::AllowRegExp,
            "in property initialization",
            "start of property initialization",
            startLoc))
      return None;

    auto optValue = parseAssignmentExpression();
    if (!optValue)
      return None;
    value = *optValue;
  }

  return setLocation(
      startLoc,
      getPrevTokenEndLoc(),
      new (context_) ESTree::PropertyNode(
          key, value, initIdent_, computed, method, shorthand));
}