/** Add an @this annotation to all functions in the objLit. */
  private void addTypesToFunctions(Node objLit, String thisType) {
    Preconditions.checkState(objLit.isObjectLit());
    for (Node keyNode : objLit.children()) {
      Node value = keyNode.getLastChild();
      if (value != null && value.isFunction()) {
        JSDocInfoBuilder fnDoc = JSDocInfoBuilder.maybeCopyFrom(keyNode.getJSDocInfo());
        fnDoc.recordThisType(
            new JSTypeExpression(new Node(Token.BANG, IR.string(thisType)), VIRTUAL_FILE));
        keyNode.setJSDocInfo(fnDoc.build());
      }
    }

    // Add @this and @return to default property values.
    for (MemberDefinition property : extractProperties(objLit)) {
      if (!property.value.isObjectLit()) {
        continue;
      }
      if (hasShorthandAssignment(property.value)) {
        compiler.report(JSError.make(property.value, POLYMER_SHORTHAND_NOT_SUPPORTED));
        return;
      }

      Node defaultValue = NodeUtil.getFirstPropMatchingKey(property.value, "value");
      if (defaultValue == null || !defaultValue.isFunction()) {
        continue;
      }
      Node defaultValueKey = defaultValue.getParent();
      JSDocInfoBuilder fnDoc = JSDocInfoBuilder.maybeCopyFrom(defaultValueKey.getJSDocInfo());
      fnDoc.recordThisType(
          new JSTypeExpression(new Node(Token.BANG, IR.string(thisType)), VIRTUAL_FILE));
      fnDoc.recordReturnType(getTypeFromProperty(property));
      defaultValueKey.setJSDocInfo(fnDoc.build());
    }
  }
    @Override
    public void visit(NodeTraversal t, Node n, Node parent) {
      switch (n.getType()) {
          // Function calls
        case Token.CALL:
          Node child = n.getFirstChild();
          String name = null;
          // NOTE: The normalization pass insures that local names do not
          // collide with global names.
          if (child.isName()) {
            name = child.getString();
          } else if (child.isFunction()) {
            name = anonFunctionMap.get(child);
          } else if (NodeUtil.isFunctionObjectCall(n)) {
            Preconditions.checkState(NodeUtil.isGet(child));
            Node fnIdentifingNode = child.getFirstChild();
            if (fnIdentifingNode.isName()) {
              name = fnIdentifingNode.getString();
            } else if (fnIdentifingNode.isFunction()) {
              name = anonFunctionMap.get(fnIdentifingNode);
            }
          }

          if (name != null) {
            FunctionState fs = functionMap.get(name);

            // Only visit call-sites for functions that can be inlined.
            if (fs != null) {
              callback.visitCallSite(t, n, fs);
            }
          }
          break;
      }
    }
  /** Traverses a function. */
  private void traverseFunction(Node n, Node parent) {
    Preconditions.checkState(n.getChildCount() == 3);
    Preconditions.checkState(n.isFunction());

    final Node fnName = n.getFirstChild();
    boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n);

    if (!isFunctionExpression) {
      // Functions declarations are in the scope containing the declaration.
      traverseBranch(fnName, n);
    }

    curNode = n;
    pushScope(n);

    if (isFunctionExpression) {
      // Function expression names are only accessible within the function
      // scope.
      traverseBranch(fnName, n);
    }

    final Node args = fnName.getNext();
    final Node body = args.getNext();

    // Args
    traverseBranch(args, n);

    // Body
    // ES6 "arrow" function may not have a block as a body.
    traverseBranch(body, n);

    popScope();
  }
 boolean isDeclaration() {
   Node parent = getParent();
   Node grandparent = parent.getParent();
   return DECLARATION_PARENTS.contains(parent.getType()) ||
       parent.isParamList() &&
       grandparent.isFunction();
 }
  /** Traverses a parse tree recursively with a scope, starting at that scope's root. */
  void traverseAtScope(Scope s) {
    Node n = s.getRootNode();
    if (n.isFunction()) {
      // We need to do some extra magic to make sure that the scope doesn't
      // get re-created when we dive into the function.
      if (inputId == null) {
        inputId = NodeUtil.getInputId(n);
      }
      sourceName = getSourceName(n);
      curNode = n;
      pushScope(s);

      Node args = n.getFirstChild().getNext();
      Node body = args.getNext();
      traverseBranch(args, n);
      traverseBranch(body, n);

      popScope();
    } else if (n.isBlock()) {
      if (inputId == null) {
        inputId = NodeUtil.getInputId(n);
      }
      sourceName = getSourceName(n);
      curNode = n;
      pushScope(s);
      traverseBranch(n, n.getParent());

      popScope();
    } else {
      Preconditions.checkState(s.isGlobal(), "Expected global scope. Got:", s);
      traverseWithScope(n, s);
    }
  }
  private void scanRoot(Node n) {
    if (n.isFunction()) {
      if (inputId == null) {
        inputId = NodeUtil.getInputId(n);
        // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached
        // from the AST.
        // Is it meaningful to build a scope for detached FUNCTION node?
      }

      final Node fnNameNode = n.getFirstChild();
      final Node args = fnNameNode.getNext();
      final Node body = args.getNext();

      // Bleed the function name into the scope, if it hasn't
      // been declared in the outer scope.
      String fnName = fnNameNode.getString();
      if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) {
        declareVar(fnNameNode);
      }

      // Args: Declare function variables
      Preconditions.checkState(args.isParamList());
      for (Node a = args.getFirstChild(); a != null; a = a.getNext()) {
        Preconditions.checkState(a.isName());
        declareVar(a);
      }

      // Body
      scanVars(body);
    } else {
      // It's the global block
      Preconditions.checkState(scope.getParent() == null);
      scanVars(n);
    }
  }
  /**
   * Expand a jQuery.expandedEach call
   *
   * <p>Expanded jQuery.expandedEach calls will replace the GETELEM nodes of a property assignment
   * with GETPROP nodes to allow for renaming.
   */
  private void maybeExpandJqueryEachCall(NodeTraversal t, Node n) {
    Node objectToLoopOver = n.getChildAtIndex(1);

    if (objectToLoopOver == null) {
      return;
    }

    Node callbackFunction = objectToLoopOver.getNext();
    if (callbackFunction == null || !callbackFunction.isFunction()) {
      return;
    }

    // Run the peephole optimizations on the first argument to handle
    // cases like ("a " + "b").split(" ")
    peepholePasses.process(null, n.getChildAtIndex(1));

    // Create a reference tree
    Node nClone = n.cloneTree();

    objectToLoopOver = nClone.getChildAtIndex(1);

    // Check to see if the first argument is something we recognize and can
    // expand.
    if (!objectToLoopOver.isObjectLit()
        && !(objectToLoopOver.isArrayLit() && isArrayLitValidForExpansion(objectToLoopOver))) {
      t.report(n, JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR, (String) null);
      return;
    }

    // Find all references to the callback function arguments
    List<Node> keyNodeReferences = new ArrayList<>();
    List<Node> valueNodeReferences = new ArrayList<>();

    new NodeTraversal(
            compiler,
            new FindCallbackArgumentReferences(
                callbackFunction,
                keyNodeReferences,
                valueNodeReferences,
                objectToLoopOver.isArrayLit()))
        .traverseInnerNode(
            NodeUtil.getFunctionBody(callbackFunction), callbackFunction, t.getScope());

    if (keyNodeReferences.isEmpty()) {
      // We didn't do anything useful ...
      t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String) null);
      return;
    }

    Node fncBlock =
        tryExpandJqueryEachCall(
            t, nClone, callbackFunction, keyNodeReferences, valueNodeReferences);

    if (fncBlock != null && fncBlock.hasChildren()) {
      replaceOriginalJqueryEachCall(n, fncBlock);
    } else {
      // We didn't do anything useful ...
      t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String) null);
    }
  }
  @Override
  public void exitScope(NodeTraversal traversal) {
    Preconditions.checkNotNull(traversal);

    // This is the case when we are exiting the global scope where we had never
    // collected argument access list. Since we do not perform this optimization
    // for the global scope, we will skip this exit point.
    if (currentArgumentsAccess == null) {
      return;
    }

    Node function = traversal.getScopeRoot();
    if (!function.isFunction()) {
      return;
    }

    // Attempt to replace the argument access and if the AST has been change,
    // report back to the compiler.
    if (tryReplaceArguments(traversal.getScope())) {
      traversal.getCompiler().reportCodeChange();
    }

    // After the attempt to replace the arguments. The currentArgumentsAccess
    // is stale and as we exit the Scope, no longer holds all the access to the
    // current scope anymore. We'll pop the access list from the outer scope
    // and set it as currentArgumentsAccess if the outer scope is not the global
    // scope.
    if (!argumentsAccessStack.isEmpty()) {
      currentArgumentsAccess = argumentsAccessStack.pop();
    } else {
      currentArgumentsAccess = null;
    }
  }
  /** @return Whether the name is used in a way that might be a candidate for inlining. */
  static boolean isCandidateUsage(Node name) {
    Node parent = name.getParent();
    Preconditions.checkState(name.isName());
    if (parent.isVar() || parent.isFunction()) {
      // This is a declaration.  Duplicate declarations are handle during
      // function candidate gathering.
      return true;
    }

    if (parent.isCall() && parent.getFirstChild() == name) {
      // This is a normal reference to the function.
      return true;
    }

    // Check for a ".call" to the named function:
    //   CALL
    //     GETPROP/GETELEM
    //       NAME
    //       STRING == "call"
    //     This-Value
    //     Function-parameter-1
    //     ...
    if (NodeUtil.isGet(parent)
        && name == parent.getFirstChild()
        && name.getNext().isString()
        && name.getNext().getString().equals("call")) {
      Node gramps = name.getAncestor(2);
      if (gramps.isCall() && gramps.getFirstChild() == parent) {
        // Yep, a ".call".
        return true;
      }
    }
    return false;
  }
  private String getTypeAnnotation(Node node) {
    // Only add annotations for things with JSDoc, or function literals.
    JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(node);
    if (jsdoc == null && !node.isFunction()) {
      return "";
    }

    JSType type = node.getJSType();
    if (type == null) {
      return "";
    } else if (type.isFunctionType()) {
      return getFunctionAnnotation(node);
    } else if (type.isEnumType()) {
      return "/** @enum {"
          + type.toMaybeEnumType().getElementsType().toAnnotationString()
          + "} */\n";
    } else if (!type.isUnknownType()
        && !type.isEmptyType()
        && !type.isVoidType()
        && !type.isFunctionPrototypeType()) {
      return "/** @type {" + node.getJSType().toAnnotationString() + "} */\n";
    } else {
      return "";
    }
  }
  public void testInlineJSDocAttachment2() {
    Node fn = parse("function f(/**\n" + " * {string}\n" + " */ x) {}").getFirstChild();
    assertTrue(fn.isFunction());

    JSDocInfo info = fn.getFirstChild().getNext().getFirstChild().getJSDocInfo();
    assertNotNull(info);
    assertTypeEquals(STRING_TYPE, info.getType());
  }
    @Override
    public void visit(NodeTraversal t, Node n, Node parent) {
      if (!n.isFunction()) {
        return;
      }

      int id = functionNames.getFunctionId(n);
      if (id < 0) {
        // Function node was added during compilation; don't instrument.
        return;
      }

      String name = functionNames.getFunctionName(n);

      if (!reportFunctionName.isEmpty()) {
        Node body = n.getLastChild();
        Node call =
            IR.call(
                IR.name(reportFunctionName), IR.number(id), IR.string(name), IR.name("arguments"));
        call.putBooleanProp(Node.FREE_CALL, true);
        Node expr = IR.exprResult(call);
        expr.useSourceInfoFromForTree(n);
        body.addChildToFront(expr);
        compiler.reportCodeChange();
      }

      if (!reportFunctionExitName.isEmpty()) {
        (new InstrumentReturns(id, name)).process(n);
      }

      if (!definedFunctionName.isEmpty()) {
        Node call = IR.call(IR.name(definedFunctionName), IR.number(id), IR.string(name));
        call.putBooleanProp(Node.FREE_CALL, true);
        call.useSourceInfoFromForTree(n);
        Node expr = NodeUtil.newExpr(call);

        Node addingRoot = null;
        if (NodeUtil.isFunctionDeclaration(n)) {
          JSModule module = t.getModule();
          addingRoot = compiler.getNodeForCodeInsertion(module);
          addingRoot.addChildToFront(expr);
        } else {
          Node beforeChild = n;
          for (Node ancestor : n.getAncestors()) {
            Token type = ancestor.getType();
            if (type == Token.BLOCK || type == Token.SCRIPT) {
              addingRoot = ancestor;
              break;
            }
            beforeChild = ancestor;
          }
          addingRoot.addChildBefore(expr, beforeChild);
        }
        compiler.reportCodeChange();
      }
    }
 /**
  * Traverse a function out-of-band of normal traversal.
  *
  * @param node The function node.
  * @param scope The scope the function is contained in. Does not fire enter/exit callback events
  *     for this scope.
  */
 public void traverseFunctionOutOfBand(Node node, Scope scope) {
   Preconditions.checkNotNull(scope);
   Preconditions.checkState(node.isFunction());
   Preconditions.checkState(scope.getRootNode() != null);
   if (inputId == null) {
     inputId = NodeUtil.getInputId(node);
   }
   curNode = node.getParent();
   pushScope(scope, true /* quietly */);
   traverseBranch(node, curNode);
   popScope(true /* quietly */);
 }
    /**
     * Creates a new block.
     *
     * @param parent The containing block.
     * @param root The root node of the block.
     */
    BasicBlock(BasicBlock parent, Node root) {
      this.parent = parent;
      this.root = root;

      this.isFunction = root.isFunction();

      if (root.getParent() != null) {
        int pType = root.getParent().getType();
        this.isLoop = pType == Token.DO || pType == Token.WHILE || pType == Token.FOR;
      } else {
        this.isLoop = false;
      }
    }
  /**
   * Constructs this matcher with a Function node that serves as the template to match all other
   * nodes against. The body of the function will be used to match against.
   */
  public TemplateAstMatcher(
      AbstractCompiler compiler,
      Node templateFunctionNode,
      TypeMatchingStrategy typeMatchingStrategy) {
    Preconditions.checkNotNull(compiler);
    Preconditions.checkState(
        templateFunctionNode.isFunction(),
        "Template node must be a function node. Received: %s",
        templateFunctionNode);

    this.compiler = compiler;
    this.templateStart = initTemplate(templateFunctionNode);
    this.typeMatchingStrategy = typeMatchingStrategy;
  }
 private void fillInFormalParameterTypes(
     JSDocInfo jsdoc,
     Node funNode,
     ImmutableList<String> typeParameters,
     DeclaredTypeRegistry registry,
     FunctionTypeBuilder builder,
     boolean ignoreJsdoc /* for when the jsdoc is malformed */) {
   boolean ignoreFunNode = !funNode.isFunction();
   Node params = ignoreFunNode ? null : funNode.getSecondChild();
   ParamIterator iterator = new ParamIterator(params, jsdoc);
   while (iterator.hasNext()) {
     String pname = iterator.nextString();
     Node param = iterator.getNode();
     ParameterKind p = ParameterKind.REQUIRED;
     if (param != null && convention.isOptionalParameter(param)) {
       p = ParameterKind.OPTIONAL;
     } else if (param != null && convention.isVarArgsParameter(param)) {
       p = ParameterKind.REST;
     }
     ParameterType inlineParamType =
         (ignoreJsdoc || ignoreFunNode || param.getJSDocInfo() == null)
             ? null
             : parseParameter(param.getJSDocInfo().getType(), p, registry, typeParameters);
     ParameterType fnParamType = inlineParamType;
     JSTypeExpression jsdocExp = jsdoc == null ? null : jsdoc.getParameterType(pname);
     if (jsdocExp != null) {
       if (inlineParamType == null) {
         fnParamType = parseParameter(jsdocExp, p, registry, typeParameters);
       } else {
         warnings.add(JSError.make(param, TWO_JSDOCS, "formal parameter " + pname));
       }
     }
     JSType t = null;
     if (fnParamType != null) {
       p = fnParamType.kind;
       t = fnParamType.type;
     }
     switch (p) {
       case REQUIRED:
         builder.addReqFormal(t);
         break;
       case OPTIONAL:
         builder.addOptFormal(t);
         break;
       case REST:
         builder.addRestFormals(t != null ? t : JSType.UNKNOWN);
         break;
     }
   }
 }
  @Override
  public CheckLevel level(JSError error) {
    Node node = error.node;
    if (node != null) {
      boolean visitedFunction = false;
      for (Node current = node; current != null; current = current.getParent()) {
        Token type = current.getToken();
        JSDocInfo info = null;

        if (type == Token.FUNCTION) {
          info = NodeUtil.getBestJSDocInfo(current);
          visitedFunction = true;
        } else if (type == Token.SCRIPT) {
          info = current.getJSDocInfo();
        } else if (current.isVar() || current.isAssign()) {
          // There's one edge case we're worried about:
          // if the warning points to an assignment to a function, we
          // want the suppressions on that function to apply.
          // It's OK if we double-count some cases.
          Node rhs = NodeUtil.getRValueOfLValue(current.getFirstChild());
          if (rhs != null) {
            if (rhs.isCast()) {
              rhs = rhs.getFirstChild();
            }

            if (rhs.isFunction() && !visitedFunction) {
              info = NodeUtil.getBestJSDocInfo(current);
            }
          }
        }

        if (info != null) {
          for (String suppressor : info.getSuppressions()) {
            WarningsGuard guard = suppressors.get(suppressor);

            // Some @suppress tags are for other tools, and
            // may not have a warnings guard.
            if (guard != null) {
              CheckLevel newLevel = guard.level(error);
              if (newLevel != null) {
                return newLevel;
              }
            }
          }
        }
      }
    }
    return null;
  }
  @Override
  void add(Node n, Context context) {
    Node parent = n.getParent();
    if (parent != null && (parent.isBlock() || parent.isScript())) {
      if (n.isFunction()) {
        add(getFunctionAnnotation(n));
      } else if (n.isExprResult() && n.getFirstChild().isAssign()) {
        Node rhs = n.getFirstChild().getLastChild();
        add(getTypeAnnotation(rhs));
      } else if (n.isVar() && n.getFirstChild().getFirstChild() != null) {
        add(getTypeAnnotation(n.getFirstChild().getFirstChild()));
      }
    }

    super.add(n, context);
  }
 /**
  * @return True if n is a function node which is explicitly annotated as returning a nullable
  *     type, other than {?}.
  */
 private static boolean isReturnTypeNullable(Node n) {
   if (n == null || !n.isFunction()) {
     return false;
   }
   FunctionType functionType = n.getJSType().toMaybeFunctionType();
   if (functionType == null) {
     // If the JSDoc declares a non-function type on a function node, we still shouldn't crash.
     return false;
   }
   JSType returnType = functionType.getReturnType();
   if (returnType == null || returnType.isUnknownType() || !returnType.isNullable()) {
     return false;
   }
   JSDocInfo info = NodeUtil.getBestJSDocInfo(n);
   return info != null && info.hasReturnType();
 }
 @Override
 public void visit(NodeTraversal t, Node n, Node parent) {
   if (n.isFunction()) {
     checkFunctionUse(t, n);
   } else if (n.isAssign()) {
     checkAssignment(t, n);
   } else if (n.isDelProp()) {
     checkDelete(t, n);
   } else if (n.isObjectLit()) {
     checkObjectLiteralOrClass(t, n);
   } else if (n.isClass()) {
     checkObjectLiteralOrClass(t, n.getLastChild());
   } else if (n.isWith()) {
     checkWith(t, n);
   }
 }
    /**
     * Creates a new block.
     * @param parent The containing block.
     * @param root The root node of the block.
     */
    BasicBlock(BasicBlock parent, Node root) {
      this.parent = parent;

      // only named functions may be hoisted.
      this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root);

      this.isFunction = root.isFunction();

      if (root.getParent() != null) {
        int pType = root.getParent().getType();
        this.isLoop = pType == Token.DO ||
            pType == Token.WHILE ||
            pType == Token.FOR;
      } else {
        this.isLoop = false;
      }
    }
  /**
   * If this returns true, check for @extends and @implements annotations on this node. Otherwise,
   * it's probably an alias for an existing class, so skip those annotations.
   *
   * @return Whether the given node declares a function. True for the following forms:
   *     <li>
   *         <pre>function foo() {}</pre>
   *     <li>
   *         <pre>var foo = function() {};</pre>
   *     <li>
   *         <pre>foo.bar = function() {};</pre>
   */
  private boolean declaresFunction(Node n) {
    if (n.isFunction()) {
      return true;
    }

    if (n.isAssign() && n.getLastChild().isFunction()) {
      return true;
    }

    if (NodeUtil.isNameDeclaration(n)
        && n.getFirstChild().hasChildren()
        && n.getFirstFirstChild().isFunction()) {
      return true;
    }

    return false;
  }
  @Override
  public void enterScope(NodeTraversal traversal) {
    Preconditions.checkNotNull(traversal);

    // This optimization is valid only within a function so we are going to
    // skip over the initial entry to the global scope.
    Node function = traversal.getScopeRoot();
    if (!function.isFunction()) {
      return;
    }

    // Introduces a new access list and stores the access list of the outer
    // scope in the stack if necessary.
    if (currentArgumentsAccess != null) {
      argumentsAccessStack.push(currentArgumentsAccess);
    }
    currentArgumentsAccess = new LinkedList<>();
  }
    private void suppressDefaultValues(Node behaviorValue) {
      for (MemberDefinition property : extractProperties(behaviorValue)) {
        if (!property.value.isObjectLit()) {
          continue;
        }

        Node defaultValue = NodeUtil.getFirstPropMatchingKey(property.value, "value");
        if (defaultValue == null || !defaultValue.isFunction()) {
          continue;
        }
        Node defaultValueKey = defaultValue.getParent();
        JSDocInfoBuilder suppressDoc =
            JSDocInfoBuilder.maybeCopyFrom(defaultValueKey.getJSDocInfo());
        suppressDoc.addSuppression("checkTypes");
        suppressDoc.addSuppression("globalThis");
        defaultValueKey.setJSDocInfo(suppressDoc.build());
      }
    }
  @Override
  public void visit(NodeTraversal t, Node n, Node parent) {
    // TODO(moz): Add support for renaming classes.
    if (!n.isLet() && !n.isConst() && !NodeUtil.isBlockScopedFunctionDeclaration(n)) {
      return;
    }

    Scope scope = t.getScope();
    Node nameNode = n.getFirstChild();
    if (!n.isFunction()
        && !nameNode.hasChildren()
        && (parent == null || !NodeUtil.isEnhancedFor(parent))) {
      nameNode.addChildToFront(IR.name("undefined").useSourceInfoIfMissingFrom(nameNode));
    }

    String oldName = nameNode.getString();
    if (n.isLet() || n.isConst()) {
      blockScopedDeclarations.add(n);
    }
    Scope hoistScope = scope.getClosestHoistScope();
    boolean doRename = false;
    if (scope != hoistScope) {
      doRename = hoistScope.isDeclared(oldName, true) || undeclaredNames.contains(oldName);
      String newName =
          doRename ? oldName + "$" + compiler.getUniqueNameIdSupplier().get() : oldName;
      Var oldVar = scope.getVar(oldName);
      scope.undeclare(oldVar);
      hoistScope.declare(newName, nameNode, oldVar.input);
      if (doRename) {
        nameNode.setString(newName);
        Node scopeRoot = scope.getRootNode();
        if (!renameMap.containsKey(scopeRoot)) {
          renameMap.put(scopeRoot, new HashMap<String, String>());
        }

        renameMap.get(scopeRoot).put(oldName, newName);
      }
    }
    if (doRename) {
      t.getCompiler().reportCodeChange();
    }
  }
Example #26
0
  private void addThisVar(NodeTraversal t) {
    Scope scope = t.getScope();
    if (scope.isDeclared(THIS_VAR, false)) {
      return;
    }

    Node parent = t.getScopeRoot();
    if (parent.isFunction()) {
      // Add the new node at the beginning of the function body.
      parent = parent.getLastChild();
    }
    if (parent.isSyntheticBlock()) {
      // Add the new node inside the SCRIPT node instead of the
      // synthetic block that contains it.
      parent = parent.getFirstChild();
    }

    Node name = IR.name(THIS_VAR).srcref(parent);
    Node thisVar = IR.var(name, IR.thisNode().srcref(parent));
    thisVar.srcref(parent);
    parent.addChildToFront(thisVar);
    scope.declare(THIS_VAR, name, null, compiler.getInput(parent.getInputId()));
  }
    /**
     * Find function expressions that are called directly in the form of
     * (function(a,b,...){...})(a,b,...) or (function(a,b,...){...}).call(this,a,b, ...)
     */
    public void findFunctionExpressions(NodeTraversal t, Node n) {
      switch (n.getType()) {
          // Functions expressions in the form of:
          //   (function(){})();
        case Token.CALL:
          Node fnNode = null;
          if (n.getFirstChild().isFunction()) {
            fnNode = n.getFirstChild();
          } else if (NodeUtil.isFunctionObjectCall(n)) {
            Node fnIdentifingNode = n.getFirstChild().getFirstChild();
            if (fnIdentifingNode.isFunction()) {
              fnNode = fnIdentifingNode;
            }
          }

          // If a interesting function was discovered, add it.
          if (fnNode != null) {
            Function fn = new FunctionExpression(fnNode, callsSeen++);
            maybeAddFunction(fn, t.getModule());
            anonFns.put(fnNode, fn.getName());
          }
          break;
      }
    }
    FindCallbackArgumentReferences(
        Node functionRoot,
        List<Node> keyReferences,
        List<Node> valueReferences,
        boolean useArrayMode) {
      Preconditions.checkState(functionRoot.isFunction());

      String keyString = null, valueString = null;
      Node callbackParams = NodeUtil.getFunctionParameters(functionRoot);
      Node param = callbackParams.getFirstChild();
      if (param != null) {
        Preconditions.checkState(param.isName());
        keyString = param.getString();

        param = param.getNext();
        if (param != null) {
          Preconditions.checkState(param.isName());
          valueString = param.getString();
        }
      }

      this.keyName = keyString;
      this.valueName = valueString;

      // For arrays, the keyString is the index number of the element.
      // We're interested in the value of the element instead
      if (useArrayMode) {
        this.keyReferences = valueReferences;
        this.valueReferences = keyReferences;
      } else {
        this.keyReferences = keyReferences;
        this.valueReferences = valueReferences;
      }

      this.startingScope = null;
    }
 /** Switches all "this.$.foo" to "this.$['foo']". */
 private void switchDollarSignPropsToBrackets(Node objLit) {
   Preconditions.checkState(objLit.isObjectLit());
   for (Node keyNode : objLit.children()) {
     Node value = keyNode.getFirstChild();
     if (value != null && value.isFunction()) {
       NodeUtil.visitPostOrder(
           value.getLastChild(),
           new NodeUtil.Visitor() {
             @Override
             public void visit(Node n) {
               if (n.isString()
                   && n.getString().equals("$")
                   && n.getParent().isGetProp()
                   && n.getParent().getParent().isGetProp()) {
                 Node dollarChildProp = n.getParent().getParent();
                 dollarChildProp.setType(Token.GETELEM);
                 compiler.reportCodeChange();
               }
             }
           },
           Predicates.<Node>alwaysTrue());
     }
   }
 }
  private void addVarDecls(NodeTraversal t, boolean addThis, boolean addArguments) {
    Scope scope = t.getScope();
    if (scope.isDeclared(THIS_VAR, false)) {
      addThis = false;
    }
    if (scope.isDeclared(ARGUMENTS_VAR, false)) {
      addArguments = false;
    }

    Node parent = t.getScopeRoot();
    if (parent.isFunction()) {
      // Add the new node at the beginning of the function body.
      parent = parent.getLastChild();
    }
    if (parent.isSyntheticBlock() && parent.getFirstChild().isScript()) {
      // Add the new node inside the SCRIPT node instead of the
      // synthetic block that contains it.
      parent = parent.getFirstChild();
    }

    CompilerInput input = compiler.getInput(parent.getInputId());
    if (addArguments) {
      Node name = IR.name(ARGUMENTS_VAR).srcref(parent);
      Node argumentsVar = IR.var(name, IR.name("arguments").srcref(parent));
      argumentsVar.srcref(parent);
      parent.addChildToFront(argumentsVar);
      scope.declare(ARGUMENTS_VAR, name, null, input);
    }
    if (addThis) {
      Node name = IR.name(THIS_VAR).srcref(parent);
      Node thisVar = IR.var(name, IR.thisNode().srcref(parent));
      thisVar.srcref(parent);
      parent.addChildToFront(thisVar);
      scope.declare(THIS_VAR, name, null, input);
    }
  }