Ejemplo n.º 1
0
    /**
     * @return Whether the definitionSite represents a function whose call signature can be
     *     modified.
     */
    private boolean canChangeSignature(Node function) {
      Definition definition = getFunctionDefinition(function);
      CodingConvention convention = compiler.getCodingConvention();

      Preconditions.checkState(!definition.isExtern());

      Collection<UseSite> useSites = defFinder.getUseSites(definition);
      for (UseSite site : useSites) {
        Node parent = site.node.getParent();

        // This was a use site removed by something else before we run.
        // 1. By another pass before us which means the definition graph is
        //    no updated properly.
        // 2. By the continuations algorithm above.
        if (parent == null) {
          continue; // Ignore it.
        }

        // Ignore references within goog.inherits calls.
        if (NodeUtil.isCall(parent) && convention.getClassesDefinedByCall(parent) != null) {
          continue;
        }

        // Accessing the property directly prevents rewrite.
        if (!SimpleDefinitionFinder.isCallOrNewSite(site)) {
          if (!(NodeUtil.isGetProp(parent) && NodeUtil.isFunctionObjectCall(parent.getParent()))) {
            return false;
          }
        }

        if (NodeUtil.isFunctionObjectApply(parent)) {
          return false;
        }

        // TODO(johnlenz): support specialization

        // Multiple definitions prevent rewrite.
        // Attempt to validate the state of the simple definition finder.
        Node nameNode = site.node;
        Collection<Definition> singleSiteDefinitions =
            defFinder.getDefinitionsReferencedAt(nameNode);
        Preconditions.checkState(singleSiteDefinitions.size() == 1);
        Preconditions.checkState(singleSiteDefinitions.contains(definition));
      }

      return true;
    }
Ejemplo n.º 2
0
  private boolean isRemovableVar(Var var) {
    // Global variables are off-limits if the user might be using them.
    if (!removeGlobals && var.isGlobal()) {
      return false;
    }

    // Referenced variables are off-limits.
    if (referenced.contains(var)) {
      return false;
    }

    // Exported variables are off-limits.
    if (codingConvention.isExported(var.getName())) {
      return false;
    }

    return true;
  }
Ejemplo n.º 3
0
  /**
   * Traverses everything in the current scope and marks variables that are referenced.
   *
   * <p>During traversal, we identify subtrees that will only be referenced if their enclosing
   * variables are referenced. Instead of traversing those subtrees, we create a continuation for
   * them, and traverse them lazily.
   */
  private void traverseNode(Node n, Node parent, Scope scope) {
    int type = n.getType();
    Var var = null;
    switch (type) {
      case Token.FUNCTION:
        // If this function is a removable var, then create a continuation
        // for it instead of traversing immediately.
        if (NodeUtil.isFunctionDeclaration(n)) {
          var = scope.getVar(n.getFirstChild().getString());
        }

        if (var != null && isRemovableVar(var)) {
          continuations.put(var, new Continuation(n, scope));
        } else {
          traverseFunction(n, scope);
        }
        return;

      case Token.ASSIGN:
        Assign maybeAssign = Assign.maybeCreateAssign(n);
        if (maybeAssign != null) {
          // Put this in the assign map. It might count as a reference,
          // but we won't know that until we have an index of all assigns.
          var = scope.getVar(maybeAssign.nameNode.getString());
          if (var != null) {
            assignsByVar.put(var, maybeAssign);
            assignsByNode.put(maybeAssign.nameNode, maybeAssign);

            if (isRemovableVar(var) && !maybeAssign.mayHaveSecondarySideEffects) {
              // If the var is unreferenced and performing this assign has
              // no secondary side effects, then we can create a continuation
              // for it instead of traversing immediately.
              continuations.put(var, new Continuation(n, scope));
              return;
            }
          }
        }
        break;

      case Token.CALL:
        // Look for calls to inheritance-defining calls (such as goog.inherits).
        SubclassRelationship subclassRelationship = codingConvention.getClassesDefinedByCall(n);
        if (subclassRelationship != null) {
          Var subclassVar = scope.getVar(subclassRelationship.subclassName);
          // Don't try to track the inheritance calls for non-globals. It would
          // be more correct to only not track when the subclass does not
          // reference a constructor, but checking that it is a global is
          // easier and mostly the same.
          if (subclassVar != null && subclassVar.isGlobal() && !referenced.contains(subclassVar)) {
            // Save a reference to the EXPR node.
            inheritsCalls.put(subclassVar, parent);
            continuations.put(subclassVar, new Continuation(n, scope));
            return;
          }
        }
        break;

      case Token.NAME:
        var = scope.getVar(n.getString());
        if (parent.getType() == Token.VAR) {
          Node value = n.getFirstChild();
          if (value != null
              && var != null
              && isRemovableVar(var)
              && !NodeUtil.mayHaveSideEffects(value)) {
            // If the var is unreferenced and creating its value has no side
            // effects, then we can create a continuation for it instead
            // of traversing immediately.
            continuations.put(var, new Continuation(n, scope));
            return;
          }
        } else {

          // If arguments is escaped, we just assume the worst and continue
          // on all the parameters.
          if ("arguments".equals(n.getString()) && scope.isLocal()) {
            Node lp = scope.getRootNode().getFirstChild().getNext();
            for (Node a = lp.getFirstChild(); a != null; a = a.getNext()) {
              markReferencedVar(scope.getVar(a.getString()));
            }
          }

          // All name references that aren't declarations or assigns
          // are references to other vars.
          if (var != null) {
            // If that var hasn't already been marked referenced, then
            // start tracking it.  If this is an assign, do nothing
            // for now.
            if (isRemovableVar(var)) {
              if (!assignsByNode.containsKey(n)) {
                markReferencedVar(var);
              }
            } else {
              markReferencedVar(var);
            }
          }
        }
        break;
    }

    for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
      traverseNode(c, n, scope);
    }
  }