private void addStatement(GenerateNodeContext context, Node stmt) {
    CodingConvention convention = compiler.getCodingConvention();

    Node n = context.getNode();
    Node exprRoot = n;
    while (!NodeUtil.isStatementBlock(exprRoot.getParent())) {
      exprRoot = exprRoot.getParent();
    }

    // It's important that any class-building calls (goog.inherits)
    // come right after the class definition, so move the export after that.
    while (true) {
      Node next = exprRoot.getNext();
      if (next != null
          && NodeUtil.isExprCall(next)
          && convention.getClassesDefinedByCall(next.getFirstChild()) != null) {
        exprRoot = next;
      } else {
        break;
      }
    }

    Node block = exprRoot.getParent();
    block.addChildAfter(stmt, exprRoot);
  }
  /** Determine which, if any, of the supported types the call site is. */
  private CallSiteType classifyCallSite(Node callNode) {
    Node parent = callNode.getParent();
    Node grandParent = parent.getParent();

    // Verify the call site:
    if (NodeUtil.isExprCall(parent)) {
      // This is a simple call?  Example: "foo();".
      return CallSiteType.SIMPLE_CALL;
    } else if (NodeUtil.isExprAssign(grandParent)
        && !NodeUtil.isLhs(callNode, parent)
        && parent.getFirstChild().getType() == Token.NAME
        && !NodeUtil.isConstantName(parent.getFirstChild())) {
      // This is a simple assignment.  Example: "x = foo();"
      return CallSiteType.SIMPLE_ASSIGNMENT;
    } else if (parent.getType() == Token.NAME
        && !NodeUtil.isConstantName(parent)
        && grandParent.getType() == Token.VAR
        && grandParent.hasOneChild()) {
      // This is a var declaration.  Example: "var x = foo();"
      // TODO(johnlenz): Should we be checking for constants on the
      // left-hand-side of the assignments (and handling them as EXPRESSION?
      return CallSiteType.VAR_DECL_SIMPLE_ASSIGNMENT;
    } else {
      Node expressionRoot = ExpressionDecomposer.findExpressionRoot(callNode);
      if (expressionRoot != null) {
        ExpressionDecomposer decomposer =
            new ExpressionDecomposer(compiler, safeNameIdSupplier, knownConstants);
        DecompositionType type = decomposer.canExposeExpression(callNode);
        if (type == DecompositionType.MOVABLE) {
          return CallSiteType.EXPRESSION;
        } else if (type == DecompositionType.DECOMPOSABLE) {
          return CallSiteType.DECOMPOSABLE_EXPRESSION;
        } else {
          Preconditions.checkState(type == DecompositionType.UNDECOMPOSABLE);
        }
      }
    }

    return CallSiteType.UNSUPPORTED;
  }
  @Override
  public void process(Node externs, Node root) {
    FindExportableNodes findExportableNodes = new FindExportableNodes(compiler);
    NodeTraversal.traverse(compiler, root, findExportableNodes);
    Map<String, GenerateNodeContext> exports = findExportableNodes.getExports();

    for (Map.Entry<String, GenerateNodeContext> entry : exports.entrySet()) {
      String export = entry.getKey();
      GenerateNodeContext context = entry.getValue();

      // Emit the proper CALL expression.
      // This is an optimization to avoid exporting everything as a symbol
      // because exporting a property is significantly simpler/faster.
      // Only export the property if the parent is being exported or
      // if the parent is "prototype" and the grandparent is being exported.
      String parent = null;
      String grandparent = null;

      Node node = context.getNode().getFirstChild();
      if (node.getType() == Token.GETPROP) {
        parent = node.getFirstChild().getQualifiedName();
        if (node.getFirstChild().getType() == Token.GETPROP
            && getPropertyName(node.getFirstChild()).equals(PROTOTYPE_PROPERTY)) {
          grandparent = node.getFirstChild().getFirstChild().getQualifiedName();
        }
      }

      boolean useExportSymbol = true;
      if (grandparent != null && exports.containsKey(grandparent)) {
        useExportSymbol = false;
      } else if (parent != null && exports.containsKey(parent)) {
        useExportSymbol = false;
      }

      Node call;
      if (useExportSymbol) {
        // exportSymbol(publicPath, object);
        call =
            new Node(
                Token.CALL,
                NodeUtil.newQualifiedNameNode(exportSymbolFunction, context.getNode(), export));
        call.addChildToBack(Node.newString(export));
        call.addChildToBack(NodeUtil.newQualifiedNameNode(export, context.getNode(), export));
      } else {
        // exportProperty(object, publicName, symbol);
        String property = getPropertyName(node);
        call =
            new Node(
                Token.CALL,
                new Node[] {
                  NodeUtil.newQualifiedNameNode(
                      exportPropertyFunction, context.getNode(), exportPropertyFunction),
                  NodeUtil.newQualifiedNameNode(parent, context.getNode(), exportPropertyFunction),
                  Node.newString(property),
                  NodeUtil.newQualifiedNameNode(export, context.getNode(), exportPropertyFunction)
                });
      }

      Node expression = new Node(Token.EXPR_RESULT, call);
      annotate(expression);

      // It's important that any class-building calls (goog.inherits)
      // come right after the class definition, so move the export after that.
      Node insertionPoint = context.getContextNode().getNext();
      CodingConvention convention = compiler.getCodingConvention();
      while (insertionPoint != null
          && NodeUtil.isExprCall(insertionPoint)
          && convention.getClassesDefinedByCall(insertionPoint.getFirstChild()) != null) {
        insertionPoint = insertionPoint.getNext();
      }

      if (insertionPoint == null) {
        context.getScriptNode().addChildToBack(expression);
      } else {
        context.getScriptNode().addChildBefore(expression, insertionPoint);
      }
      compiler.reportCodeChange();
    }
  }