/**
   * Updates the first initialization (a.k.a "declaration") of a global name that occurs at a VAR
   * node. See comment for {@link #updateObjLitOrFunctionDeclaration}.
   *
   * @param n An object representing a global name (e.g. "a")
   */
  private void updateObjLitOrFunctionDeclarationAtVarNode(Name n, boolean canCollapseChildNames) {
    if (!canCollapseChildNames) {
      return;
    }

    Ref ref = n.getDeclaration();
    String name = ref.node.getString();
    Node rvalue = ref.node.getFirstChild();
    Node varNode = ref.node.getParent();
    Node grandparent = varNode.getParent();

    boolean isObjLit = rvalue.isObjectLit();
    int numChanges = 0;

    if (isObjLit) {
      numChanges +=
          declareVarsForObjLitValues(
              n, name, rvalue, varNode, grandparent.getChildBefore(varNode), grandparent);
    }

    numChanges += addStubsForUndeclaredProperties(n, name, grandparent, varNode);

    if (isObjLit && n.canEliminate()) {
      varNode.removeChild(ref.node);
      if (!varNode.hasChildren()) {
        grandparent.removeChild(varNode);
      }
      numChanges++;

      // Clear out the object reference, since we've eliminated it from the
      // parse tree.
      ref.node = null;
    }

    if (numChanges > 0) {
      compiler.reportCodeChange();
    }
  }
  /**
   * Updates the first initialization (a.k.a "declaration") of a global name that occurs at an
   * ASSIGN node. See comment for {@link #updateObjLitOrFunctionDeclaration}.
   *
   * @param n An object representing a global name (e.g. "a", "a.b.c")
   * @param alias The flattened name for {@code n} (e.g. "a", "a$b$c")
   */
  private void updateObjLitOrFunctionDeclarationAtAssignNode(
      Name n, String alias, boolean canCollapseChildNames) {
    // NOTE: It's important that we don't add additional nodes
    // (e.g. a var node before the exprstmt) because the exprstmt might be
    // the child of an if statement that's not inside a block).

    Ref ref = n.getDeclaration();
    Node rvalue = ref.node.getNext();
    Node varNode = new Node(Token.VAR);
    Node varParent = ref.node.getAncestor(3);
    Node grandparent = ref.node.getAncestor(2);
    boolean isObjLit = rvalue.isObjectLit();
    boolean insertedVarNode = false;

    if (isObjLit && n.canEliminate()) {
      // Eliminate the object literal altogether.
      varParent.replaceChild(grandparent, varNode);
      ref.node = null;
      insertedVarNode = true;

    } else if (!n.isSimpleName()) {
      // Create a VAR node to declare the name.
      if (rvalue.isFunction()) {
        checkForHosedThisReferences(rvalue, n.docInfo, n);
      }

      ref.node.getParent().removeChild(rvalue);

      Node nameNode = NodeUtil.newName(compiler, alias, ref.node.getAncestor(2), n.getFullName());

      JSDocInfo info = NodeUtil.getBestJSDocInfo(ref.node.getParent());
      if (ref.node.getLastChild().getBooleanProp(Node.IS_CONSTANT_NAME)
          || (info != null && info.isConstant())) {
        nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
      }

      if (info != null) {
        varNode.setJSDocInfo(info);
      }
      varNode.addChildToBack(nameNode);
      nameNode.addChildToFront(rvalue);
      varParent.replaceChild(grandparent, varNode);

      // Update the node ancestry stored in the reference.
      ref.node = nameNode;
      insertedVarNode = true;
    }

    if (canCollapseChildNames) {
      if (isObjLit) {
        declareVarsForObjLitValues(
            n, alias, rvalue, varNode, varParent.getChildBefore(varNode), varParent);
      }

      addStubsForUndeclaredProperties(n, alias, varParent, varNode);
    }

    if (insertedVarNode) {
      if (!varNode.hasChildren()) {
        varParent.removeChild(varNode);
      }
      compiler.reportCodeChange();
    }
  }