/** If this is an assign to a variable or its property, return it. Otherwise, return null. */
    static Assign maybeCreateAssign(Node assignNode) {
      Preconditions.checkState(NodeUtil.isAssignmentOp(assignNode));

      // Skip one level of GETPROPs or GETELEMs.
      //
      // Don't skip more than one level, because then we get into
      // situations where assigns to properties of properties will always
      // trigger side-effects, and the variable they're on cannot be removed.
      boolean isPropAssign = false;
      Node current = assignNode.getFirstChild();
      if (NodeUtil.isGet(current)) {
        current = current.getFirstChild();
        isPropAssign = true;

        if (current.getType() == Token.GETPROP
            && current.getLastChild().getString().equals("prototype")) {
          // Prototype properties sets should be considered like normal
          // property sets.
          current = current.getFirstChild();
        }
      }

      if (current.getType() == Token.NAME) {
        return new Assign(assignNode, current, isPropAssign);
      }
      return null;
    }
 boolean isLvalue() {
   Node parent = getParent();
   int parentType = parent.getType();
   return (parentType == Token.VAR && nameNode.getFirstChild() != null)
       || parentType == Token.INC
       || parentType == Token.DEC
       || (NodeUtil.isAssignmentOp(parent)
           && parent.getFirstChild() == nameNode)
       || isLhsOfForInExpression(nameNode);
 }
    Assign(Node assignNode, Node nameNode, boolean isPropertyAssign) {
      Preconditions.checkState(NodeUtil.isAssignmentOp(assignNode));
      this.assignNode = assignNode;
      this.nameNode = nameNode;
      this.isPropertyAssign = isPropertyAssign;

      this.mayHaveSecondarySideEffects =
          assignNode.getParent().getType() != Token.EXPR_RESULT
              || NodeUtil.mayHaveSideEffects(assignNode.getFirstChild())
              || NodeUtil.mayHaveSideEffects(assignNode.getLastChild());
    }
 boolean isLvalue() {
   Node parent = getParent();
   int parentType = parent.getType();
   return (parentType == Token.VAR && nameNode.getFirstChild() != null)
       || (parentType == Token.LET && nameNode.getFirstChild() != null)
       || (parentType == Token.CONST && nameNode.getFirstChild() != null)
       || (parentType == Token.DEFAULT_VALUE && parent.getFirstChild() == nameNode)
       || parentType == Token.INC
       || parentType == Token.DEC
       || parentType == Token.CATCH
       || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode)
       || isLhsOfEnhancedForExpression(nameNode)
       || NodeUtil.isLhsByDestructuring(nameNode);
 }
  /**
   * @param n The node in question.
   * @param cfgNode The node to add
   * @param conditional true if the definition is not always executed.
   */
  private void computeMustDef(Node n, Node cfgNode, MustDef output, boolean conditional) {
    switch (n.getType()) {
      case Token.BLOCK:
      case Token.FUNCTION:
        return;

      case Token.WHILE:
      case Token.DO:
      case Token.IF:
        computeMustDef(NodeUtil.getConditionExpression(n), cfgNode, output, conditional);
        return;

      case Token.FOR:
        if (!NodeUtil.isForIn(n)) {
          computeMustDef(NodeUtil.getConditionExpression(n), cfgNode, output, conditional);
        } else {
          // for(x in y) {...}
          Node lhs = n.getFirstChild();
          Node rhs = lhs.getNext();
          if (NodeUtil.isVar(lhs)) {
            lhs = lhs.getLastChild(); // for(var x in y) {...}
          }
          if (NodeUtil.isName(lhs)) {
            addToDefIfLocal(lhs.getString(), cfgNode, rhs, output);
          }
        }
        return;

      case Token.AND:
      case Token.OR:
        computeMustDef(n.getFirstChild(), cfgNode, output, conditional);
        computeMustDef(n.getLastChild(), cfgNode, output, true);
        return;

      case Token.HOOK:
        computeMustDef(n.getFirstChild(), cfgNode, output, conditional);
        computeMustDef(n.getFirstChild().getNext(), cfgNode, output, true);
        computeMustDef(n.getLastChild(), cfgNode, output, true);
        return;

      case Token.VAR:
        for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
          if (c.hasChildren()) {
            computeMustDef(c.getFirstChild(), cfgNode, output, conditional);
            addToDefIfLocal(c.getString(), conditional ? null : cfgNode, c.getFirstChild(), output);
          }
        }
        return;

      default:
        if (NodeUtil.isAssignmentOp(n)) {
          if (NodeUtil.isName(n.getFirstChild())) {
            Node name = n.getFirstChild();
            computeMustDef(name.getNext(), cfgNode, output, conditional);
            addToDefIfLocal(
                name.getString(), conditional ? null : cfgNode, n.getLastChild(), output);
            return;
          } else if (NodeUtil.isGet(n.getFirstChild())) {
            // Treat all assignments to arguments as redefining the
            // parameters itself.
            Node obj = n.getFirstChild().getFirstChild();
            if (NodeUtil.isName(obj) && "arguments".equals(obj.getString())) {
              // TODO(user): More accuracy can be introduced
              // ie: We know exactly what arguments[x] is if x is a constant
              // number.
              escapeParameters(output);
            }
          }
        }

        if (NodeUtil.isName(n) && "arguments".equals(n.getString())) {
          escapeParameters(output);
        }

        // DEC and INC actually defines the variable.
        if (n.getType() == Token.DEC || n.getType() == Token.INC) {
          Node target = n.getFirstChild();
          if (NodeUtil.isName(target)) {
            addToDefIfLocal(target.getString(), conditional ? null : cfgNode, null, output);
            return;
          }
        }

        for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
          computeMustDef(c, cfgNode, output, conditional);
        }
    }
  }