Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/frontend/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 51 kB image not shown  

Quelle  FoldConstants.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "frontend/FoldConstants.h"

#include "mozilla/Maybe.h"  // mozilla::Maybe
#include "mozilla/Try.h"    // MOZ_TRY*

#include "jslibmath.h"
#include "jsmath.h"

#include "frontend/FullParseHandler.h"
#include "frontend/ParseNode.h"
#include "frontend/ParseNodeVisitor.h"
#include "frontend/Parser-macros.h"  // MOZ_TRY_VAR_OR_RETURN
#include "frontend/ParserAtom.h"     // ParserAtomsTable, TaggedParserAtomIndex
#include "js/Conversions.h"
#include "js/Stack.h"            // JS::NativeStackLimit
#include "util/StringBuilder.h"  // StringBuilder

using namespace js;
using namespace js::frontend;

using JS::ToInt32;
using JS::ToUint32;

struct FoldInfo {
  FrontendContext* fc;
  ParserAtomsTable& parserAtoms;
  BigIntStencilVector& bigInts;
  FullParseHandler* handler;
};

// Don't use ReplaceNode directly, because we want the constant folder to keep
// the attributes isInParens and isDirectRHSAnonFunction of the old node being
// replaced.
[[nodiscard]] inline bool TryReplaceNode(ParseNode** pnp,
                                         ParseNodeResult result) {
  // convenience check: can call TryReplaceNode(pnp, alloc_parsenode())
  // directly, without having to worry about alloc returning null.
  if (result.isErr()) {
    return false;
  }
  auto* pn = result.unwrap();
  pn->setInParens((*pnp)->isInParens());
  pn->setDirectRHSAnonFunction((*pnp)->isDirectRHSAnonFunction());
  ReplaceNode(pnp, pn);
  return true;
}

static bool ContainsHoistedDeclaration(FoldInfo& info, ParseNode* node,
                                       bool* result);

static bool ListContainsHoistedDeclaration(FoldInfo& info, ListNode* list,
                                           bool* result) {
  for (ParseNode* node : list->contents()) {
    if (!ContainsHoistedDeclaration(info, node, result)) {
      return false;
    }
    if (*result) {
      return true;
    }
  }

  *result = false;
  return true;
}

// Determines whether the given ParseNode contains any declarations whose
// visibility will extend outside the node itself -- that is, whether the
// ParseNode contains any var statements.
//
// THIS IS NOT A GENERAL-PURPOSE FUNCTION.  It is only written to work in the
// specific context of deciding that |node|, as one arm of a ParseNodeKind::If
// controlled by a constant condition, contains a declaration that forbids
// |node| being completely eliminated as dead.
static bool ContainsHoistedDeclaration(FoldInfo& info, ParseNode* node,
                                       bool* result) {
  AutoCheckRecursionLimit recursion(info.fc);
  if (!recursion.check(info.fc)) {
    return false;
  }

restart:

  // With a better-typed AST, we would have distinct parse node classes for
  // expressions and for statements and would characterize expressions with
  // ExpressionKind and statements with StatementKind.  Perhaps someday.  In
  // the meantime we must characterize every ParseNodeKind, even the
  // expression/sub-expression ones that, if we handle all statement kinds
  // correctly, we'll never see.
  switch (node->getKind()) {
    // Base case.
    case ParseNodeKind::VarStmt:
      *result = true;
      return true;

    // Non-global lexical declarations are block-scoped (ergo not hoistable).
    case ParseNodeKind::LetDecl:
    case ParseNodeKind::ConstDecl:
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    case ParseNodeKind::UsingDecl:
    case ParseNodeKind::AwaitUsingDecl:
#endif
      MOZ_ASSERT(node->is<ListNode>());
      *result = false;
      return true;

    // Similarly to the lexical declarations above, classes cannot add hoisted
    // declarations
    case ParseNodeKind::ClassDecl:
      MOZ_ASSERT(node->is<ClassNode>());
      *result = false;
      return true;

    // Function declarations *can* be hoisted declarations.  But in the
    // magical world of the rewritten frontend, the declaration necessitated
    // by a nested function statement, not at body level, doesn't require
    // that we preserve an unreachable function declaration node against
    // dead-code removal.
    case ParseNodeKind::Function:
      *result = false;
      return true;

    case ParseNodeKind::Module:
      *result = false;
      return true;

    // Statements with no sub-components at all.
    case ParseNodeKind::EmptyStmt:
      MOZ_ASSERT(node->is<NullaryNode>());
      *result = false;
      return true;

    case ParseNodeKind::DebuggerStmt:
      MOZ_ASSERT(node->is<DebuggerStatement>());
      *result = false;
      return true;

    // Statements containing only an expression have no declarations.
    case ParseNodeKind::ExpressionStmt:
    case ParseNodeKind::ThrowStmt:
    case ParseNodeKind::ReturnStmt:
      MOZ_ASSERT(node->is<UnaryNode>());
      *result = false;
      return true;

    // These two aren't statements in the spec, but we sometimes insert them
    // in statement lists anyway.
    case ParseNodeKind::InitialYield:
    case ParseNodeKind::YieldStarExpr:
    case ParseNodeKind::YieldExpr:
      MOZ_ASSERT(node->is<UnaryNode>());
      *result = false;
      return true;

    // Other statements with no sub-statement components.
    case ParseNodeKind::BreakStmt:
    case ParseNodeKind::ContinueStmt:
    case ParseNodeKind::ImportDecl:
    case ParseNodeKind::ImportSpecList:
    case ParseNodeKind::ImportSpec:
    case ParseNodeKind::ImportNamespaceSpec:
    case ParseNodeKind::ExportFromStmt:
    case ParseNodeKind::ExportDefaultStmt:
    case ParseNodeKind::ExportSpecList:
    case ParseNodeKind::ExportSpec:
    case ParseNodeKind::ExportNamespaceSpec:
    case ParseNodeKind::ExportStmt:
    case ParseNodeKind::ExportBatchSpecStmt:
    case ParseNodeKind::CallImportExpr:
    case ParseNodeKind::CallImportSpec:
    case ParseNodeKind::ImportAttributeList:
    case ParseNodeKind::ImportAttribute:
    case ParseNodeKind::ImportModuleRequest:
      *result = false;
      return true;

    // Statements possibly containing hoistable declarations only in the left
    // half, in ParseNode terms -- the loop body in AST terms.
    case ParseNodeKind::DoWhileStmt:
      return ContainsHoistedDeclaration(info, node->as<BinaryNode>().left(),
                                        result);

    // Statements possibly containing hoistable declarations only in the
    // right half, in ParseNode terms -- the loop body or nested statement
    // (usually a block statement), in AST terms.
    case ParseNodeKind::WhileStmt:
    case ParseNodeKind::WithStmt:
      return ContainsHoistedDeclaration(info, node->as<BinaryNode>().right(),
                                        result);

    case ParseNodeKind::LabelStmt:
      return ContainsHoistedDeclaration(
          info, node->as<LabeledStatement>().statement(), result);

    // Statements with more complicated structures.

    // if-statement nodes may have hoisted declarations in their consequent
    // and alternative components.
    case ParseNodeKind::IfStmt: {
      TernaryNode* ifNode = &node->as<TernaryNode>();
      ParseNode* consequent = ifNode->kid2();
      if (!ContainsHoistedDeclaration(info, consequent, result)) {
        return false;
      }
      if (*result) {
        return true;
      }

      if ((node = ifNode->kid3())) {
        goto restart;
      }

      *result = false;
      return true;
    }

    // try-statements have statements to execute, and one or both of a
    // catch-list and a finally-block.
    case ParseNodeKind::TryStmt: {
      TernaryNode* tryNode = &node->as<TernaryNode>();

      MOZ_ASSERT(tryNode->kid2() || tryNode->kid3(),
                 "must have either catch or finally");

      ParseNode* tryBlock = tryNode->kid1();
      if (!ContainsHoistedDeclaration(info, tryBlock, result)) {
        return false;
      }
      if (*result) {
        return true;
      }

      if (ParseNode* catchScope = tryNode->kid2()) {
        BinaryNode* catchNode =
            &catchScope->as<LexicalScopeNode>().scopeBody()->as<BinaryNode>();
        MOZ_ASSERT(catchNode->isKind(ParseNodeKind::Catch));

        ParseNode* catchStatements = catchNode->right();
        if (!ContainsHoistedDeclaration(info, catchStatements, result)) {
          return false;
        }
        if (*result) {
          return true;
        }
      }

      if (ParseNode* finallyBlock = tryNode->kid3()) {
        return ContainsHoistedDeclaration(info, finallyBlock, result);
      }

      *result = false;
      return true;
    }

    // A switch node's left half is an expression; only its right half (a
    // list of cases/defaults, or a block node) could contain hoisted
    // declarations.
    case ParseNodeKind::SwitchStmt: {
      SwitchStatement* switchNode = &node->as<SwitchStatement>();
      return ContainsHoistedDeclaration(info, &switchNode->lexicalForCaseList(),
                                        result);
    }

    case ParseNodeKind::Case: {
      CaseClause* caseClause = &node->as<CaseClause>();
      return ContainsHoistedDeclaration(info, caseClause->statementList(),
                                        result);
    }

    case ParseNodeKind::ForStmt: {
      ForNode* forNode = &node->as<ForNode>();
      TernaryNode* loopHead = forNode->head();
      MOZ_ASSERT(loopHead->isKind(ParseNodeKind::ForHead) ||
                 loopHead->isKind(ParseNodeKind::ForIn) ||
                 loopHead->isKind(ParseNodeKind::ForOf));

      if (loopHead->isKind(ParseNodeKind::ForHead)) {
        // for (init?; cond?; update?), with only init possibly containing
        // a hoisted declaration.  (Note: a lexical-declaration |init| is
        // (at present) hoisted in SpiderMonkey parlance -- but such
        // hoisting doesn't extend outside of this statement, so it is not
        // hoisting in the sense meant by ContainsHoistedDeclaration.)
        ParseNode* init = loopHead->kid1();
        if (init && init->isKind(ParseNodeKind::VarStmt)) {
          *result = true;
          return true;
        }
      } else {
        MOZ_ASSERT(loopHead->isKind(ParseNodeKind::ForIn) ||
                   loopHead->isKind(ParseNodeKind::ForOf));

        // for each? (target in ...), where only target may introduce
        // hoisted declarations.
        //
        //   -- or --
        //
        // for (target of ...), where only target may introduce hoisted
        // declarations.
        //
        // Either way, if |target| contains a declaration, it's |loopHead|'s
        // first kid.
        ParseNode* decl = loopHead->kid1();
        if (decl && decl->isKind(ParseNodeKind::VarStmt)) {
          *result = true;
          return true;
        }
      }

      ParseNode* loopBody = forNode->body();
      return ContainsHoistedDeclaration(info, loopBody, result);
    }

    case ParseNodeKind::LexicalScope: {
      LexicalScopeNode* scope = &node->as<LexicalScopeNode>();
      ParseNode* expr = scope->scopeBody();

      if (expr->isKind(ParseNodeKind::ForStmt) || expr->is<FunctionNode>()) {
        return ContainsHoistedDeclaration(info, expr, result);
      }

      MOZ_ASSERT(expr->isKind(ParseNodeKind::StatementList));
      return ListContainsHoistedDeclaration(
          info, &scope->scopeBody()->as<ListNode>(), result);
    }

    // List nodes with all non-null children.
    case ParseNodeKind::StatementList:
      return ListContainsHoistedDeclaration(info, &node->as<ListNode>(),
                                            result);

    // Grammar sub-components that should never be reached directly by this
    // method, because some parent component should have asserted itself.
    case ParseNodeKind::ObjectPropertyName:
    case ParseNodeKind::ComputedName:
    case ParseNodeKind::Spread:
    case ParseNodeKind::MutateProto:
    case ParseNodeKind::PropertyDefinition:
    case ParseNodeKind::Shorthand:
    case ParseNodeKind::ConditionalExpr:
    case ParseNodeKind::TypeOfNameExpr:
    case ParseNodeKind::TypeOfExpr:
    case ParseNodeKind::AwaitExpr:
    case ParseNodeKind::VoidExpr:
    case ParseNodeKind::NotExpr:
    case ParseNodeKind::BitNotExpr:
    case ParseNodeKind::DeleteNameExpr:
    case ParseNodeKind::DeletePropExpr:
    case ParseNodeKind::DeleteElemExpr:
    case ParseNodeKind::DeleteOptionalChainExpr:
    case ParseNodeKind::DeleteExpr:
    case ParseNodeKind::PosExpr:
    case ParseNodeKind::NegExpr:
    case ParseNodeKind::PreIncrementExpr:
    case ParseNodeKind::PostIncrementExpr:
    case ParseNodeKind::PreDecrementExpr:
    case ParseNodeKind::PostDecrementExpr:
    case ParseNodeKind::CoalesceExpr:
    case ParseNodeKind::OrExpr:
    case ParseNodeKind::AndExpr:
    case ParseNodeKind::BitOrExpr:
    case ParseNodeKind::BitXorExpr:
    case ParseNodeKind::BitAndExpr:
    case ParseNodeKind::StrictEqExpr:
    case ParseNodeKind::EqExpr:
    case ParseNodeKind::StrictNeExpr:
    case ParseNodeKind::NeExpr:
    case ParseNodeKind::LtExpr:
    case ParseNodeKind::LeExpr:
    case ParseNodeKind::GtExpr:
    case ParseNodeKind::GeExpr:
    case ParseNodeKind::InstanceOfExpr:
    case ParseNodeKind::InExpr:
    case ParseNodeKind::PrivateInExpr:
    case ParseNodeKind::LshExpr:
    case ParseNodeKind::RshExpr:
    case ParseNodeKind::UrshExpr:
    case ParseNodeKind::AddExpr:
    case ParseNodeKind::SubExpr:
    case ParseNodeKind::MulExpr:
    case ParseNodeKind::DivExpr:
    case ParseNodeKind::ModExpr:
    case ParseNodeKind::PowExpr:
    case ParseNodeKind::InitExpr:
    case ParseNodeKind::AssignExpr:
    case ParseNodeKind::AddAssignExpr:
    case ParseNodeKind::SubAssignExpr:
    case ParseNodeKind::CoalesceAssignExpr:
    case ParseNodeKind::OrAssignExpr:
    case ParseNodeKind::AndAssignExpr:
    case ParseNodeKind::BitOrAssignExpr:
    case ParseNodeKind::BitXorAssignExpr:
    case ParseNodeKind::BitAndAssignExpr:
    case ParseNodeKind::LshAssignExpr:
    case ParseNodeKind::RshAssignExpr:
    case ParseNodeKind::UrshAssignExpr:
    case ParseNodeKind::MulAssignExpr:
    case ParseNodeKind::DivAssignExpr:
    case ParseNodeKind::ModAssignExpr:
    case ParseNodeKind::PowAssignExpr:
    case ParseNodeKind::CommaExpr:
    case ParseNodeKind::ArrayExpr:
    case ParseNodeKind::ObjectExpr:
    case ParseNodeKind::PropertyNameExpr:
    case ParseNodeKind::DotExpr:
    case ParseNodeKind::ArgumentsLength:
    case ParseNodeKind::ElemExpr:
    case ParseNodeKind::Arguments:
    case ParseNodeKind::CallExpr:
    case ParseNodeKind::PrivateMemberExpr:
    case ParseNodeKind::OptionalChain:
    case ParseNodeKind::OptionalDotExpr:
    case ParseNodeKind::OptionalElemExpr:
    case ParseNodeKind::OptionalCallExpr:
    case ParseNodeKind::OptionalPrivateMemberExpr:
    case ParseNodeKind::Name:
    case ParseNodeKind::PrivateName:
    case ParseNodeKind::TemplateStringExpr:
    case ParseNodeKind::TemplateStringListExpr:
    case ParseNodeKind::TaggedTemplateExpr:
    case ParseNodeKind::CallSiteObj:
    case ParseNodeKind::StringExpr:
    case ParseNodeKind::RegExpExpr:
    case ParseNodeKind::TrueExpr:
    case ParseNodeKind::FalseExpr:
    case ParseNodeKind::NullExpr:
    case ParseNodeKind::RawUndefinedExpr:
    case ParseNodeKind::ThisExpr:
    case ParseNodeKind::Elision:
    case ParseNodeKind::NumberExpr:
    case ParseNodeKind::BigIntExpr:
    case ParseNodeKind::NewExpr:
    case ParseNodeKind::Generator:
    case ParseNodeKind::ParamsBody:
    case ParseNodeKind::Catch:
    case ParseNodeKind::ForIn:
    case ParseNodeKind::ForOf:
    case ParseNodeKind::ForHead:
    case ParseNodeKind::DefaultConstructor:
    case ParseNodeKind::ClassBodyScope:
    case ParseNodeKind::ClassMethod:
    case ParseNodeKind::ClassField:
    case ParseNodeKind::StaticClassBlock:
    case ParseNodeKind::ClassMemberList:
    case ParseNodeKind::ClassNames:
    case ParseNodeKind::NewTargetExpr:
    case ParseNodeKind::ImportMetaExpr:
    case ParseNodeKind::PosHolder:
    case ParseNodeKind::SuperCallExpr:
    case ParseNodeKind::SuperBase:
    case ParseNodeKind::SetThis:
#ifdef ENABLE_DECORATORS
    case ParseNodeKind::DecoratorList:
#endif
      MOZ_CRASH(
          "ContainsHoistedDeclaration should have indicated false on "
          "some parent node without recurring to test this node");
    case ParseNodeKind::LastUnused:
    case ParseNodeKind::Limit:
      MOZ_CRASH("unexpected sentinel ParseNodeKind in node");

#ifdef ENABLE_RECORD_TUPLE
    case ParseNodeKind::RecordExpr:
    case ParseNodeKind::TupleExpr:
      MOZ_CRASH("Record and Tuple are not supported yet");
#endif
  }

  MOZ_CRASH("invalid node kind");
}

/*
 * Fold from one constant type to another.
 * XXX handles only strings and numbers for now
 */

static bool FoldType(FoldInfo info, ParseNode** pnp, ParseNodeKind kind) {
  const ParseNode* pn = *pnp;
  if (!pn->isKind(kind)) {
    switch (kind) {
      case ParseNodeKind::NumberExpr:
        if (pn->isKind(ParseNodeKind::StringExpr)) {
          auto atom = pn->as<NameNode>().atom();
          double d = info.parserAtoms.toNumber(atom);
          if (!TryReplaceNode(
                  pnp, info.handler->newNumber(d, NoDecimal, pn->pn_pos))) {
            return false;
          }
        }
        break;

      case ParseNodeKind::StringExpr:
        if (pn->isKind(ParseNodeKind::NumberExpr)) {
          TaggedParserAtomIndex atom =
              pn->as<NumericLiteral>().toAtom(info.fc, info.parserAtoms);
          if (!atom) {
            return false;
          }
          if (!TryReplaceNode(
                  pnp, info.handler->newStringLiteral(atom, pn->pn_pos))) {
            return false;
          }
        }
        break;

      default:
        MOZ_CRASH("Invalid type in constant folding FoldType");
    }
  }
  return true;
}

static bool IsEffectless(ParseNode* node) {
  return node->isKind(ParseNodeKind::TrueExpr) ||
         node->isKind(ParseNodeKind::FalseExpr) ||
         node->isKind(ParseNodeKind::StringExpr) ||
         node->isKind(ParseNodeKind::TemplateStringExpr) ||
         node->isKind(ParseNodeKind::NumberExpr) ||
         node->isKind(ParseNodeKind::BigIntExpr) ||
         node->isKind(ParseNodeKind::NullExpr) ||
         node->isKind(ParseNodeKind::RawUndefinedExpr) ||
         node->isKind(ParseNodeKind::Function);
}

enum Truthiness { Truthy, Falsy, Unknown };

static Truthiness Boolish(const FoldInfo& info, ParseNode* pn) {
  switch (pn->getKind()) {
    case ParseNodeKind::NumberExpr:
      return (pn->as<NumericLiteral>().value() != 0 &&
              !std::isnan(pn->as<NumericLiteral>().value()))
                 ? Truthy
                 : Falsy;

    case ParseNodeKind::BigIntExpr:
      return info.bigInts[pn->as<BigIntLiteral>().index()].isZero() ? Falsy
                                                                    : Truthy;

    case ParseNodeKind::StringExpr:
    case ParseNodeKind::TemplateStringExpr:
      return (pn->as<NameNode>().atom() ==
              TaggedParserAtomIndex::WellKnown::empty())
                 ? Falsy
                 : Truthy;

    case ParseNodeKind::TrueExpr:
    case ParseNodeKind::Function:
      return Truthy;

    case ParseNodeKind::FalseExpr:
    case ParseNodeKind::NullExpr:
    case ParseNodeKind::RawUndefinedExpr:
      return Falsy;

    case ParseNodeKind::VoidExpr: {
      // |void <foo>| evaluates to |undefined| which isn't truthy.  But the
      // sense of this method requires that the expression be literally
      // replaceable with true/false: not the case if the nested expression
      // is effectful, might throw, &c.  Walk past the |void| (and nested
      // |void| expressions, for good measure) and check that the nested
      // expression doesn't break this requirement before indicating falsity.
      do {
        pn = pn->as<UnaryNode>().kid();
      } while (pn->isKind(ParseNodeKind::VoidExpr));

      return IsEffectless(pn) ? Falsy : Unknown;
    }

    default:
      return Unknown;
  }
}

static bool SimplifyCondition(FoldInfo info, ParseNode** nodePtr) {
  // Conditions fold like any other expression, but then they sometimes can be
  // further folded to constants. *nodePtr should already have been
  // constant-folded.

  ParseNode* node = *nodePtr;
  if (Truthiness t = Boolish(info, node); t != Unknown) {
    // We can turn function nodes into constant nodes here, but mutating
    // function nodes is tricky --- in particular, mutating a function node
    // that appears on a method list corrupts the method list. However,
    // methods are M's in statements of the form 'this.foo = M;', which we
    // never fold, so we're okay.
    if (!TryReplaceNode(nodePtr, info.handler->newBooleanLiteral(
                                     t == Truthy, node->pn_pos))) {
      return false;
    }
  }

  return true;
}

static bool FoldTypeOfExpr(FoldInfo info, ParseNode** nodePtr) {
  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
  MOZ_ASSERT(node->isKind(ParseNodeKind::TypeOfExpr));
  ParseNode* expr = node->kid();

  // Constant-fold the entire |typeof| if given a constant with known type.
  TaggedParserAtomIndex result;
  if (expr->isKind(ParseNodeKind::StringExpr) ||
      expr->isKind(ParseNodeKind::TemplateStringExpr)) {
    result = TaggedParserAtomIndex::WellKnown::string();
  } else if (expr->isKind(ParseNodeKind::NumberExpr)) {
    result = TaggedParserAtomIndex::WellKnown::number();
  } else if (expr->isKind(ParseNodeKind::BigIntExpr)) {
    result = TaggedParserAtomIndex::WellKnown::bigint();
  } else if (expr->isKind(ParseNodeKind::NullExpr)) {
    result = TaggedParserAtomIndex::WellKnown::object();
  } else if (expr->isKind(ParseNodeKind::TrueExpr) ||
             expr->isKind(ParseNodeKind::FalseExpr)) {
    result = TaggedParserAtomIndex::WellKnown::boolean();
  } else if (expr->is<FunctionNode>()) {
    result = TaggedParserAtomIndex::WellKnown::function();
  }

  if (result) {
    if (!TryReplaceNode(nodePtr,
                        info.handler->newStringLiteral(result, node->pn_pos))) {
      return false;
    }
  }

  return true;
}

static bool FoldDeleteExpr(FoldInfo info, ParseNode** nodePtr) {
  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();

  MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteExpr));
  ParseNode* expr = node->kid();

  // Expression deletion evaluates the expression, then evaluates to true.
  // For effectless expressions, eliminate the expression evaluation.
  if (IsEffectless(expr)) {
    if (!TryReplaceNode(nodePtr,
                        info.handler->newBooleanLiteral(true, node->pn_pos))) {
      return false;
    }
  }

  return true;
}

static bool FoldDeleteElement(FoldInfo info, ParseNode** nodePtr) {
  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
  MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteElemExpr));
  ParseNode* expr = node->kid();

  // If we're deleting an element, but constant-folding converted our
  // element reference into a dotted property access, we must *also*
  // morph the node's kind.
  //
  // In principle this also applies to |super["foo"] -> super.foo|,
  // but we don't constant-fold |super["foo"]| yet.
  MOZ_ASSERT(expr->isKind(ParseNodeKind::ElemExpr) ||
             expr->isKind(ParseNodeKind::DotExpr));
  if (expr->isKind(ParseNodeKind::DotExpr)) {
    // newDelete will detect and use DeletePropExpr
    if (!TryReplaceNode(nodePtr,
                        info.handler->newDelete(node->pn_pos.begin, expr))) {
      return false;
    }
    MOZ_ASSERT((*nodePtr)->getKind() == ParseNodeKind::DeletePropExpr);
  }

  return true;
}

static bool FoldNot(FoldInfo info, ParseNode** nodePtr) {
  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
  MOZ_ASSERT(node->isKind(ParseNodeKind::NotExpr));

  if (!SimplifyCondition(info, node->unsafeKidReference())) {
    return false;
  }

  ParseNode* expr = node->kid();

  if (expr->isKind(ParseNodeKind::TrueExpr) ||
      expr->isKind(ParseNodeKind::FalseExpr)) {
    bool newval = !expr->isKind(ParseNodeKind::TrueExpr);

    if (!TryReplaceNode(
            nodePtr, info.handler->newBooleanLiteral(newval, node->pn_pos))) {
      return false;
    }
  }

  return true;
}

static bool FoldUnaryArithmetic(FoldInfo info, ParseNode** nodePtr) {
  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
  MOZ_ASSERT(node->isKind(ParseNodeKind::BitNotExpr) ||
                 node->isKind(ParseNodeKind::PosExpr) ||
                 node->isKind(ParseNodeKind::NegExpr),
             "need a different method for this node kind");

  ParseNode* expr = node->kid();

  if (expr->isKind(ParseNodeKind::NumberExpr) ||
      expr->isKind(ParseNodeKind::TrueExpr) ||
      expr->isKind(ParseNodeKind::FalseExpr)) {
    double d = expr->isKind(ParseNodeKind::NumberExpr)
                   ? expr->as<NumericLiteral>().value()
                   : double(expr->isKind(ParseNodeKind::TrueExpr));

    if (node->isKind(ParseNodeKind::BitNotExpr)) {
      d = ~ToInt32(d);
    } else if (node->isKind(ParseNodeKind::NegExpr)) {
      d = -d;
    } else {
      MOZ_ASSERT(node->isKind(ParseNodeKind::PosExpr));  // nothing to do
    }

    if (!TryReplaceNode(nodePtr,
                        info.handler->newNumber(d, NoDecimal, node->pn_pos))) {
      return false;
    }
  } else if (expr->is<BigIntLiteral>()) {
    auto* literal = &expr->as<BigIntLiteral>();
    auto& bigInt = info.bigInts[literal->index()];

    if (node->isKind(ParseNodeKind::BitNotExpr)) {
      if (bigInt.inplaceBitNot()) {
        return TryReplaceNode(nodePtr, literal);
      }
    } else if (node->isKind(ParseNodeKind::NegExpr)) {
      if (bigInt.inplaceNegate()) {
        return TryReplaceNode(nodePtr, literal);
      }
    } else {
      MOZ_ASSERT(node->isKind(ParseNodeKind::PosExpr));  // nothing to do
    }
  }

  return true;
}

static bool FoldAndOrCoalesce(FoldInfo info, ParseNode** nodePtr) {
  ListNode* node = &(*nodePtr)->as<ListNode>();

  MOZ_ASSERT(node->isKind(ParseNodeKind::AndExpr) ||
             node->isKind(ParseNodeKind::CoalesceExpr) ||
             node->isKind(ParseNodeKind::OrExpr));

  bool isOrNode = node->isKind(ParseNodeKind::OrExpr);
  bool isAndNode = node->isKind(ParseNodeKind::AndExpr);
  bool isCoalesceNode = node->isKind(ParseNodeKind::CoalesceExpr);
  ParseNode** elem = node->unsafeHeadReference();
  do {
    Truthiness t = Boolish(info, *elem);

    // If we don't know the constant-folded node's truthiness, we can't
    // reduce this node with its surroundings.  Continue folding any
    // remaining nodes.
    if (t == Unknown) {
      elem = &(*elem)->pn_next;
      continue;
    }

    bool isTruthyCoalesceNode =
        isCoalesceNode && !((*elem)->isKind(ParseNodeKind::NullExpr) ||
                            (*elem)->isKind(ParseNodeKind::VoidExpr) ||
                            (*elem)->isKind(ParseNodeKind::RawUndefinedExpr));
    bool canShortCircuit = (isOrNode && t == Truthy) ||
                           (isAndNode && t == Falsy) || isTruthyCoalesceNode;

    // If the constant-folded node's truthiness will terminate the
    // condition -- `a || true || expr` or `b && false && expr` or
    // `false ?? c ?? expr` -- then trailing nodes will never be
    // evaluated.  Truncate the list after the known-truthiness node,
    // as it's the overall result.
    if (canShortCircuit) {
      for (ParseNode* next = (*elem)->pn_next; next; next = next->pn_next) {
        node->unsafeDecrementCount();
      }

      // Terminate the original and/or list at the known-truthiness
      // node.
      (*elem)->pn_next = nullptr;
      elem = &(*elem)->pn_next;
      break;
    }

    // We've encountered a vacuous node that'll never short-circuit
    // evaluation.
    if ((*elem)->pn_next) {
      // This node is never the overall result when there are
      // subsequent nodes.  Remove it.
      ParseNode* elt = *elem;
      *elem = elt->pn_next;
      node->unsafeDecrementCount();
    } else {
      // Otherwise this node is the result of the overall expression,
      // so leave it alone.  And we're done.
      elem = &(*elem)->pn_next;
      break;
    }
  } while (*elem);

  node->unsafeReplaceTail(elem);

  // If we removed nodes, we may have to replace a one-element list with
  // its element.
  if (node->count() == 1) {
    ParseNode* first = node->head();
    if (!TryReplaceNode(nodePtr, first)) {
      ;
      return false;
    }
  }

  return true;
}

static bool Fold(FoldInfo info, ParseNode** pnp);

static bool FoldConditional(FoldInfo info, ParseNode** nodePtr) {
  ParseNode** nextNode = nodePtr;

  do {
    // |nextNode| on entry points to the C?T:F expression to be folded.
    // Reset it to exit the loop in the common case where F isn't another
    // ?: expression.
    nodePtr = nextNode;
    nextNode = nullptr;

    TernaryNode* node = &(*nodePtr)->as<TernaryNode>();
    MOZ_ASSERT(node->isKind(ParseNodeKind::ConditionalExpr));

    ParseNode** expr = node->unsafeKid1Reference();
    if (!Fold(info, expr)) {
      return false;
    }
    if (!SimplifyCondition(info, expr)) {
      return false;
    }

    ParseNode** ifTruthy = node->unsafeKid2Reference();
    if (!Fold(info, ifTruthy)) {
      return false;
    }

    ParseNode** ifFalsy = node->unsafeKid3Reference();

    // If our C?T:F node has F as another ?: node, *iteratively* constant-
    // fold F *after* folding C and T (and possibly eliminating C and one
    // of T/F entirely); otherwise fold F normally.  Making |nextNode| non-
    // null causes this loop to run again to fold F.
    //
    // Conceivably we could instead/also iteratively constant-fold T, if T
    // were more complex than F.  Such an optimization is unimplemented.
    if ((*ifFalsy)->isKind(ParseNodeKind::ConditionalExpr)) {
      MOZ_ASSERT((*ifFalsy)->is<TernaryNode>());
      nextNode = ifFalsy;
    } else {
      if (!Fold(info, ifFalsy)) {
        return false;
      }
    }

    // Try to constant-fold based on the condition expression.
    Truthiness t = Boolish(info, *expr);
    if (t == Unknown) {
      continue;
    }

    // Otherwise reduce 'C ? T : F' to T or F as directed by C.
    ParseNode* replacement = t == Truthy ? *ifTruthy : *ifFalsy;

    // Otherwise perform a replacement.  This invalidates |nextNode|, so
    // reset it (if the replacement requires folding) or clear it (if
    // |ifFalsy| is dead code) as needed.
    if (nextNode) {
      nextNode = (*nextNode == replacement) ? nodePtr : nullptr;
    }
    if (!TryReplaceNode(nodePtr, replacement)) {
      return false;
    }
  } while (nextNode);

  return true;
}

static bool FoldIf(FoldInfo info, ParseNode** nodePtr) {
  ParseNode** nextNode = nodePtr;

  do {
    // |nextNode| on entry points to the initial |if| to be folded.  Reset
    // it to exit the loop when the |else| arm isn't another |if|.
    nodePtr = nextNode;
    nextNode = nullptr;

    TernaryNode* node = &(*nodePtr)->as<TernaryNode>();
    MOZ_ASSERT(node->isKind(ParseNodeKind::IfStmt));

    ParseNode** expr = node->unsafeKid1Reference();
    if (!Fold(info, expr)) {
      return false;
    }
    if (!SimplifyCondition(info, expr)) {
      return false;
    }

    ParseNode** consequent = node->unsafeKid2Reference();
    if (!Fold(info, consequent)) {
      return false;
    }

    ParseNode** alternative = node->unsafeKid3Reference();
    if (*alternative) {
      // If in |if (C) T; else F;| we have |F| as another |if|,
      // *iteratively* constant-fold |F| *after* folding |C| and |T| (and
      // possibly completely replacing the whole thing with |T| or |F|);
      // otherwise fold F normally.  Making |nextNode| non-null causes
      // this loop to run again to fold F.
      if ((*alternative)->isKind(ParseNodeKind::IfStmt)) {
        MOZ_ASSERT((*alternative)->is<TernaryNode>());
        nextNode = alternative;
      } else {
        if (!Fold(info, alternative)) {
          return false;
        }
      }
    }

    // Eliminate the consequent or alternative if the condition has
    // constant truthiness.
    Truthiness t = Boolish(info, *expr);
    if (t == Unknown) {
      continue;
    }

    // Careful!  Either of these can be null: |replacement| in |if (0) T;|,
    // and |discarded| in |if (true) T;|.
    ParseNode* replacement;
    ParseNode* discarded;
    if (t == Truthy) {
      replacement = *consequent;
      discarded = *alternative;
    } else {
      replacement = *alternative;
      discarded = *consequent;
    }

    bool performReplacement = true;
    if (discarded) {
      // A declaration that hoists outside the discarded arm prevents the
      // |if| from being folded away.
      bool containsHoistedDecls;
      if (!ContainsHoistedDeclaration(info, discarded, &containsHoistedDecls)) {
        return false;
      }

      performReplacement = !containsHoistedDecls;
    }

    if (!performReplacement) {
      continue;
    }

    if (!replacement) {
      // If there's no replacement node, we have a constantly-false |if|
      // with no |else|.  Replace the entire thing with an empty
      // statement list.
      if (!TryReplaceNode(nodePtr,
                          info.handler->newStatementList(node->pn_pos))) {
        return false;
      }
    } else {
      // Replacement invalidates |nextNode|, so reset it (if the
      // replacement requires folding) or clear it (if |alternative|
      // is dead code) as needed.
      if (nextNode) {
        nextNode = (*nextNode == replacement) ? nodePtr : nullptr;
      }
      ReplaceNode(nodePtr, replacement);
    }
  } while (nextNode);

  return true;
}

static double ComputeBinary(ParseNodeKind kind, double left, double right) {
  if (kind == ParseNodeKind::AddExpr) {
    return left + right;
  }

  if (kind == ParseNodeKind::SubExpr) {
    return left - right;
  }

  if (kind == ParseNodeKind::MulExpr) {
    return left * right;
  }

  if (kind == ParseNodeKind::ModExpr) {
    return NumberMod(left, right);
  }

  if (kind == ParseNodeKind::UrshExpr) {
    return ToUint32(left) >> (ToUint32(right) & 31);
  }

  if (kind == ParseNodeKind::DivExpr) {
    return NumberDiv(left, right);
  }

  MOZ_ASSERT(kind == ParseNodeKind::LshExpr || kind == ParseNodeKind::RshExpr);

  int32_t i = ToInt32(left);
  uint32_t j = ToUint32(right) & 31;
  return int32_t((kind == ParseNodeKind::LshExpr) ? uint32_t(i) << j : i >> j);
}

static bool FoldBinaryArithmetic(FoldInfo info, ParseNode** nodePtr) {
  ListNode* node = &(*nodePtr)->as<ListNode>();
  MOZ_ASSERT(node->isKind(ParseNodeKind::SubExpr) ||
             node->isKind(ParseNodeKind::MulExpr) ||
             node->isKind(ParseNodeKind::LshExpr) ||
             node->isKind(ParseNodeKind::RshExpr) ||
             node->isKind(ParseNodeKind::UrshExpr) ||
             node->isKind(ParseNodeKind::DivExpr) ||
             node->isKind(ParseNodeKind::ModExpr));
  MOZ_ASSERT(node->count() >= 2);

  // Fold each operand to a number if possible.
  ParseNode** listp = node->unsafeHeadReference();
  for (; *listp; listp = &(*listp)->pn_next) {
    if (!FoldType(info, listp, ParseNodeKind::NumberExpr)) {
      return false;
    }
  }
  node->unsafeReplaceTail(listp);

  // Now fold all leading numeric terms together into a single number.
  // (Trailing terms for the non-shift operations can't be folded together
  // due to floating point imprecision.  For example, if |x === -2**53|,
  // |x - 1 - 1 === -2**53| but |x - 2 === -2**53 - 2|.  Shifts could be
  // folded, but it doesn't seem worth the effort.)
  ParseNode** elem = node->unsafeHeadReference();
  ParseNode** next = &(*elem)->pn_next;
  if ((*elem)->isKind(ParseNodeKind::NumberExpr)) {
    ParseNodeKind kind = node->getKind();
    while (true) {
      if (!*next || !(*next)->isKind(ParseNodeKind::NumberExpr)) {
        break;
      }

      double d = ComputeBinary(kind, (*elem)->as<NumericLiteral>().value(),
                               (*next)->as<NumericLiteral>().value());

      TokenPos pos((*elem)->pn_pos.begin, (*next)->pn_pos.end);
      if (!TryReplaceNode(elem, info.handler->newNumber(d, NoDecimal, pos))) {
        return false;
      }

      (*elem)->pn_next = (*next)->pn_next;
      next = &(*elem)->pn_next;
      node->unsafeDecrementCount();
    }

    if (node->count() == 1) {
      MOZ_ASSERT(node->head() == *elem);
      MOZ_ASSERT((*elem)->isKind(ParseNodeKind::NumberExpr));

      if (!TryReplaceNode(nodePtr, *elem)) {
        return false;
      }
    }
  }

  return true;
}

static bool FoldExponentiation(FoldInfo info, ParseNode** nodePtr) {
  ListNode* node = &(*nodePtr)->as<ListNode>();
  MOZ_ASSERT(node->isKind(ParseNodeKind::PowExpr));
  MOZ_ASSERT(node->count() >= 2);

  // Fold each operand, ideally into a number.
  ParseNode** listp = node->unsafeHeadReference();
  for (; *listp; listp = &(*listp)->pn_next) {
    if (!FoldType(info, listp, ParseNodeKind::NumberExpr)) {
      return false;
    }
  }

  node->unsafeReplaceTail(listp);

  // Unlike all other binary arithmetic operators, ** is right-associative:
  // 2**3**5 is 2**(3**5), not (2**3)**5.  As list nodes singly-link their
  // children, full constant-folding requires either linear space or dodgy
  // in-place linked list reversal.  So we only fold one exponentiation: it's
  // easy and addresses common cases like |2**32|.
  if (node->count() > 2) {
    return true;
  }

  ParseNode* base = node->head();
  ParseNode* exponent = base->pn_next;
  if (!base->isKind(ParseNodeKind::NumberExpr) ||
      !exponent->isKind(ParseNodeKind::NumberExpr)) {
    return true;
  }

  double d1 = base->as<NumericLiteral>().value();
  double d2 = exponent->as<NumericLiteral>().value();

  return TryReplaceNode(nodePtr, info.handler->newNumber(
                                     ecmaPow(d1, d2), NoDecimal, node->pn_pos));
}

static bool FoldElement(FoldInfo info, ParseNode** nodePtr) {
  PropertyByValue* elem = &(*nodePtr)->as<PropertyByValue>();

  ParseNode* expr = &elem->expression();
  ParseNode* key = &elem->key();
  TaggedParserAtomIndex name;
  if (key->isKind(ParseNodeKind::StringExpr)) {
    auto keyIndex = key->as<NameNode>().atom();
    uint32_t index;
    if (info.parserAtoms.isIndex(keyIndex, &index)) {
      // Optimization 1: We have something like expr["100"]. This is
      // equivalent to expr[100] which is faster.
      if (!TryReplaceNode(
              elem->unsafeRightReference(),
              info.handler->newNumber(index, NoDecimal, key->pn_pos))) {
        return false;
      }
      key = &elem->key();
    } else {
      name = keyIndex;
    }
  } else if (key->isKind(ParseNodeKind::NumberExpr)) {
    auto* numeric = &key->as<NumericLiteral>();
    double number = numeric->value();
    if (number != ToUint32(number)) {
      // Optimization 2: We have something like expr[3.14]. The number
      // isn't an array index, so it converts to a string ("3.14"),
      // enabling optimization 3 below.
      name = numeric->toAtom(info.fc, info.parserAtoms);
      if (!name) {
        return false;
      }
    }
  }

  // If we don't have a name, we can't optimize to getprop.
  if (!name) {
    return true;
  }

  // Optimization 3: We have expr["foo"] where foo is not an index.  Convert
  // to a property access (like expr.foo) that optimizes better downstream.

  NameNode* propertyNameExpr;
  MOZ_TRY_VAR_OR_RETURN(propertyNameExpr,
                        info.handler->newPropertyName(name, key->pn_pos),
                        false);
  if (!TryReplaceNode(
          nodePtr, info.handler->newPropertyAccess(expr, propertyNameExpr))) {
    return false;
  }

  return true;
}

static bool FoldAdd(FoldInfo info, ParseNode** nodePtr) {
  ListNode* node = &(*nodePtr)->as<ListNode>();

  MOZ_ASSERT(node->isKind(ParseNodeKind::AddExpr));
  MOZ_ASSERT(node->count() >= 2);

  // Fold leading numeric operands together:
  //
  //   (1 + 2 + x)  becomes  (3 + x)
  //
  // Don't go past the leading operands: additions after a string are
  // string concatenations, not additions: ("1" + 2 + 3 === "123").
  ParseNode** current = node->unsafeHeadReference();
  ParseNode** next = &(*current)->pn_next;
  if ((*current)->isKind(ParseNodeKind::NumberExpr)) {
    do {
      if (!(*next)->isKind(ParseNodeKind::NumberExpr)) {
        break;
      }

      double left = (*current)->as<NumericLiteral>().value();
      double right = (*next)->as<NumericLiteral>().value();
      TokenPos pos((*current)->pn_pos.begin, (*next)->pn_pos.end);

      if (!TryReplaceNode(
              current, info.handler->newNumber(left + right, NoDecimal, pos))) {
        return false;
      }

      (*current)->pn_next = (*next)->pn_next;
      next = &(*current)->pn_next;

      node->unsafeDecrementCount();
    } while (*next);
  }

  // If any operands remain, attempt string concatenation folding.
  do {
    // If no operands remain, we're done.
    if (!*next) {
      break;
    }

    // (number + string) is string concatenation *only* at the start of
    // the list: (x + 1 + "2" !== x + "12") when x is a number.
    if ((*current)->isKind(ParseNodeKind::NumberExpr) &&
        (*next)->isKind(ParseNodeKind::StringExpr)) {
      if (!FoldType(info, current, ParseNodeKind::StringExpr)) {
        return false;
      }
      next = &(*current)->pn_next;
    }

    // The first string forces all subsequent additions to be
    // string concatenations.
    do {
      if ((*current)->isKind(ParseNodeKind::StringExpr)) {
        break;
      }

      current = next;
      next = &(*current)->pn_next;
    } while (*next);

    // If there's nothing left to fold, we're done.
    if (!*next) {
      break;
    }

    do {
      // Concat all strings.
      MOZ_ASSERT((*current)->isKind(ParseNodeKind::StringExpr));

      // To avoid unnecessarily copy when there's no strings after the
      // first item, lazily construct StringBuilder and append the first item.
      mozilla::Maybe<StringBuilder> accum;
      TaggedParserAtomIndex firstAtom;
      firstAtom = (*current)->as<NameNode>().atom();

      do {
        // Try folding the next operand to a string.
        if (!FoldType(info, next, ParseNodeKind::StringExpr)) {
          return false;
        }

        // Stop glomming once folding doesn't produce a string.
        if (!(*next)->isKind(ParseNodeKind::StringExpr)) {
          break;
        }

        if (!accum) {
          accum.emplace(info.fc);
          if (!accum->append(info.parserAtoms, firstAtom)) {
            return false;
          }
        }
        // Append this string and remove the node.
        if (!accum->append(info.parserAtoms, (*next)->as<NameNode>().atom())) {
          return false;
        }

        (*current)->pn_next = (*next)->pn_next;
        next = &(*current)->pn_next;

        node->unsafeDecrementCount();
      } while (*next);

      // Replace with concatenation if we multiple nodes.
      if (accum) {
        auto combination = accum->finishParserAtom(info.parserAtoms, info.fc);
        if (!combination) {
          return false;
        }

        // Replace |current|'s string with the entire combination.
        MOZ_ASSERT((*current)->isKind(ParseNodeKind::StringExpr));
        (*current)->as<NameNode>().setAtom(combination);
      }

      // If we're out of nodes, we're done.
      if (!*next) {
        break;
      }

      current = next;
      next = &(*current)->pn_next;

      // If we're out of nodes *after* the non-foldable-to-string
      // node, we're done.
      if (!*next) {
        break;
      }

      // Otherwise find the next node foldable to a string, and loop.
      do {
        current = next;

        if (!FoldType(info, current, ParseNodeKind::StringExpr)) {
          return false;
        }
        next = &(*current)->pn_next;
      } while (!(*current)->isKind(ParseNodeKind::StringExpr) && *next);
    } while (*next);
  } while (false);

  MOZ_ASSERT(!*next, "must have considered all nodes here");
  MOZ_ASSERT(!(*current)->pn_next, "current node must be the last node");

  node->unsafeReplaceTail(&(*current)->pn_next);

  if (node->count() == 1) {
    // We reduced the list to a constant.  Replace the ParseNodeKind::Add node
    // with that constant.
    ReplaceNode(nodePtr, *current);
  }

  return true;
}

class FoldVisitor : public RewritingParseNodeVisitor<FoldVisitor> {
  using Base = RewritingParseNodeVisitor;

  ParserAtomsTable& parserAtoms;
  BigIntStencilVector& bigInts;
  FullParseHandler* handler;

  FoldInfo info() const { return FoldInfo{fc_, parserAtoms, bigInts, handler}; }

 public:
  FoldVisitor(FrontendContext* fc, ParserAtomsTable& parserAtoms,
              BigIntStencilVector& bigInts, FullParseHandler* handler)
      : RewritingParseNodeVisitor(fc),
        parserAtoms(parserAtoms),
        bigInts(bigInts),
        handler(handler) {}

  bool visitElemExpr(ParseNode*& pn) {
    return Base::visitElemExpr(pn) && FoldElement(info(), &pn);
  }

  bool visitTypeOfExpr(ParseNode*& pn) {
    return Base::visitTypeOfExpr(pn) && FoldTypeOfExpr(info(), &pn);
  }

  bool visitDeleteExpr(ParseNode*& pn) {
    return Base::visitDeleteExpr(pn) && FoldDeleteExpr(info(), &pn);
  }

  bool visitDeleteElemExpr(ParseNode*& pn) {
    return Base::visitDeleteElemExpr(pn) && FoldDeleteElement(info(), &pn);
  }

  bool visitNotExpr(ParseNode*& pn) {
    return Base::visitNotExpr(pn) && FoldNot(info(), &pn);
  }

  bool visitBitNotExpr(ParseNode*& pn) {
    return Base::visitBitNotExpr(pn) && FoldUnaryArithmetic(info(), &pn);
  }

  bool visitPosExpr(ParseNode*& pn) {
    return Base::visitPosExpr(pn) && FoldUnaryArithmetic(info(), &pn);
  }

  bool visitNegExpr(ParseNode*& pn) {
    return Base::visitNegExpr(pn) && FoldUnaryArithmetic(info(), &pn);
  }

  bool visitPowExpr(ParseNode*& pn) {
    return Base::visitPowExpr(pn) && FoldExponentiation(info(), &pn);
  }

  bool visitMulExpr(ParseNode*& pn) {
    return Base::visitMulExpr(pn) && FoldBinaryArithmetic(info(), &pn);
  }

  bool visitDivExpr(ParseNode*& pn) {
    return Base::visitDivExpr(pn) && FoldBinaryArithmetic(info(), &pn);
  }

  bool visitModExpr(ParseNode*& pn) {
    return Base::visitModExpr(pn) && FoldBinaryArithmetic(info(), &pn);
  }

  bool visitAddExpr(ParseNode*& pn) {
    return Base::visitAddExpr(pn) && FoldAdd(info(), &pn);
  }

  bool visitSubExpr(ParseNode*& pn) {
    return Base::visitSubExpr(pn) && FoldBinaryArithmetic(info(), &pn);
  }

  bool visitLshExpr(ParseNode*& pn) {
    return Base::visitLshExpr(pn) && FoldBinaryArithmetic(info(), &pn);
  }

  bool visitRshExpr(ParseNode*& pn) {
    return Base::visitRshExpr(pn) && FoldBinaryArithmetic(info(), &pn);
  }

  bool visitUrshExpr(ParseNode*& pn) {
    return Base::visitUrshExpr(pn) && FoldBinaryArithmetic(info(), &pn);
  }

  bool visitAndExpr(ParseNode*& pn) {
    // Note that this does result in the unfortunate fact that dead arms of this
    // node get constant folded. The same goes for visitOr and visitCoalesce.
    return Base::visitAndExpr(pn) && FoldAndOrCoalesce(info(), &pn);
  }

  bool visitOrExpr(ParseNode*& pn) {
    return Base::visitOrExpr(pn) && FoldAndOrCoalesce(info(), &pn);
  }

  bool visitCoalesceExpr(ParseNode*& pn) {
    return Base::visitCoalesceExpr(pn) && FoldAndOrCoalesce(info(), &pn);
  }

  bool visitConditionalExpr(ParseNode*& pn) {
    // Don't call base-class visitConditional because FoldConditional processes
    // pn's child nodes specially to save stack space.
    return FoldConditional(info(), &pn);
  }

 private:
  bool internalVisitCall(BinaryNode* node) {
    MOZ_ASSERT(node->isKind(ParseNodeKind::CallExpr) ||
               node->isKind(ParseNodeKind::OptionalCallExpr) ||
               node->isKind(ParseNodeKind::SuperCallExpr) ||
               node->isKind(ParseNodeKind::NewExpr) ||
               node->isKind(ParseNodeKind::TaggedTemplateExpr));

    // Don't fold a parenthesized callable component in an invocation, as this
    // might cause a different |this| value to be used, changing semantics:
    //
    //   var prop = "global";
    //   var obj = { prop: "obj", f: function() { return this.prop; } };
    //   assertEq((true ? obj.f : null)(), "global");
    //   assertEq(obj.f(), "obj");
    //   assertEq((true ? obj.f : null)``, "global");
    //   assertEq(obj.f``, "obj");
    //
    // As an exception to this, we do allow folding the function in
    // `(function() { ... })()` (the module pattern), because that lets us
    // constant fold code inside that function.
    //
    // See bug 537673 and bug 1182373.
    ParseNode* callee = node->left();
    if (node->isKind(ParseNodeKind::NewExpr) || !callee->isInParens() ||
        callee->is<FunctionNode>()) {
      if (!visit(*node->unsafeLeftReference())) {
        return false;
      }
    }

    if (!visit(*node->unsafeRightReference())) {
      return false;
    }

    return true;
  }

 public:
  bool visitCallExpr(ParseNode*& pn) {
    return internalVisitCall(&pn->as<BinaryNode>());
  }

  bool visitOptionalCallExpr(ParseNode*& pn) {
    return internalVisitCall(&pn->as<BinaryNode>());
  }

  bool visitNewExpr(ParseNode*& pn) {
    return internalVisitCall(&pn->as<BinaryNode>());
  }

  bool visitSuperCallExpr(ParseNode*& pn) {
    return internalVisitCall(&pn->as<BinaryNode>());
  }

  bool visitTaggedTemplateExpr(ParseNode*& pn) {
    return internalVisitCall(&pn->as<BinaryNode>());
  }

  bool visitIfStmt(ParseNode*& pn) {
    // Don't call base-class visitIf because FoldIf processes pn's child nodes
    // specially to save stack space.
    return FoldIf(info(), &pn);
  }

  bool visitForStmt(ParseNode*& pn) {
    if (!Base::visitForStmt(pn)) {
      return false;
    }

    ForNode& stmt = pn->as<ForNode>();
    if (stmt.left()->isKind(ParseNodeKind::ForHead)) {
      TernaryNode& head = stmt.left()->as<TernaryNode>();
      ParseNode** test = head.unsafeKid2Reference();
      if (*test) {
        if (!SimplifyCondition(info(), test)) {
          return false;
        }
        if ((*test)->isKind(ParseNodeKind::TrueExpr)) {
          *test = nullptr;
        }
      }
    }

    return true;
  }

  bool visitWhileStmt(ParseNode*& pn) {
    BinaryNode& node = pn->as<BinaryNode>();
    return Base::visitWhileStmt(pn) &&
           SimplifyCondition(info(), node.unsafeLeftReference());
  }

  bool visitDoWhileStmt(ParseNode*& pn) {
    BinaryNode& node = pn->as<BinaryNode>();
    return Base::visitDoWhileStmt(pn) &&
           SimplifyCondition(info(), node.unsafeRightReference());
  }

  bool visitFunction(ParseNode*& pn) {
    FunctionNode& node = pn->as<FunctionNode>();

    // Don't constant-fold inside "use asm" code, as this could create a parse
    // tree that doesn't type-check as asm.js.
    if (node.funbox()->useAsmOrInsideUseAsm()) {
      return true;
    }

    return Base::visitFunction(pn);
  }

  bool visitArrayExpr(ParseNode*& pn) {
    if (!Base::visitArrayExpr(pn)) {
      return false;
    }

    ListNode* list = &pn->as<ListNode>();
    // Empty arrays are non-constant, since we cannot easily determine their
    // type.
    if (list->hasNonConstInitializer() && list->count() > 0) {
      for (ParseNode* node : list->contents()) {
        if (!node->isConstant()) {
          return true;
        }
      }
      list->unsetHasNonConstInitializer();
    }
    return true;
  }

  bool visitObjectExpr(ParseNode*& pn) {
    if (!Base::visitObjectExpr(pn)) {
      return false;
    }

    ListNode* list = &pn->as<ListNode>();
    if (list->hasNonConstInitializer()) {
      for (ParseNode* node : list->contents()) {
        if (node->getKind() != ParseNodeKind::PropertyDefinition) {
          return true;
        }
        BinaryNode* binary = &node->as<BinaryNode>();
        if (binary->left()->isKind(ParseNodeKind::ComputedName)) {
          return true;
        }
        if (!binary->right()->isConstant()) {
          return true;
        }
      }
      list->unsetHasNonConstInitializer();
    }
    return true;
  }
};

static bool Fold(FrontendContext* fc, ParserAtomsTable& parserAtoms,
                 BigIntStencilVector& bigInts, FullParseHandler* handler,
                 ParseNode** pnp) {
  FoldVisitor visitor(fc, parserAtoms, bigInts, handler);
  return visitor.visit(*pnp);
}
static bool Fold(FoldInfo info, ParseNode** pnp) {
  return Fold(info.fc, info.parserAtoms, info.bigInts, info.handler, pnp);
}

bool frontend::FoldConstants(FrontendContext* fc, ParserAtomsTable& parserAtoms,
                             BigIntStencilVector& bigInts, ParseNode** pnp,
                             FullParseHandler* handler) {
  return Fold(fc, parserAtoms, bigInts, handler, pnp);
}

Messung V0.5
C=83 H=93 G=87

¤ Dauer der Verarbeitung: 0.9 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.