@Override
  public void process(Node externs, Node root) {
    Preconditions.checkState(compiler.getLifeCycleStage().isNormalized());

    NodeTraversal.traverseEs6(compiler, root, new FindCandidateFunctions());
    if (fns.isEmpty()) {
      return; // Nothing left to do.
    }
    NodeTraversal.traverseEs6(compiler, root, new FindCandidatesReferences(fns, anonFns));
    trimCandidatesNotMeetingMinimumRequirements();
    if (fns.isEmpty()) {
      return; // Nothing left to do.
    }

    // Store the set of function names eligible for inlining and use this to
    // prevent function names from being moved into temporaries during
    // expression decomposition. If this movement were allowed it would prevent
    // the Inline callback from finding the function calls.
    //
    // This pass already assumes these are constants, so this is safe for anyone
    // using function inlining.
    //
    Set<String> fnNames = new HashSet<>(fns.keySet());
    injector.setKnownConstants(fnNames);

    trimCandidatesUsingOnCost();
    if (fns.isEmpty()) {
      return; // Nothing left to do.
    }
    resolveInlineConflicts();
    decomposeExpressions();
    NodeTraversal.traverseEs6(compiler, root, new CallVisitor(fns, anonFns, new Inline(injector)));

    removeInlinedFunctions();
  }
 @Override
 public void enterScope(NodeTraversal t) {
   // Only infer the entry root, rather than the scope root.
   // This ensures that incremental compilation only touches the root
   // that's been swapped out.
   inferScope(t.getCurrentNode(), t.getScope());
 }
  public void process(Node externs, Node root) {
    Node initCode = null;
    if (initCodeSource.length() != 0) {
      Node initCodeRoot = compiler.parseSyntheticCode(templateFilename + ":init", initCodeSource);
      if (initCodeRoot != null && initCodeRoot.getFirstChild() != null) {
        initCode = initCodeRoot.removeChildren();
      } else {
        return; // parse failure
      }
    }

    NodeTraversal.traverse(compiler, root, new RemoveCallback(declarationsToRemove));
    NodeTraversal.traverse(compiler, root, new InstrumentCallback());

    if (appNameSetter.length() != 0) {
      Node call =
          new Node(
              Token.CALL, Node.newString(Token.NAME, appNameSetter), Node.newString(appNameStr));
      Node expr = new Node(Token.EXPR_RESULT, call);

      Node addingRoot = compiler.getNodeForCodeInsertion(null);
      addingRoot.addChildrenToFront(expr);
      compiler.reportCodeChange();
    }

    if (initCode != null) {
      Node addingRoot = compiler.getNodeForCodeInsertion(null);
      addingRoot.addChildrenToFront(initCode);
      compiler.reportCodeChange();
    }
  }
  /** For each node, update the block stack and reference collection as appropriate. */
  @Override
  public void visit(NodeTraversal t, Node n, Node parent) {
    if (n.isName()
        || n.isRest()
        || (n.isStringKey() && parent.isObjectPattern() && !n.hasChildren())) {
      Var v;
      if (n.getString().equals("arguments")) {
        v = t.getScope().getArgumentsVar();
      } else {
        v = t.getScope().getVar(n.getString());
      }

      if (v != null) {
        if (varFilter.apply(v)) {
          addReference(v, new Reference(n, t, peek(blockStack)));
        }

        if (v.getParentNode() != null
            && NodeUtil.isHoistedFunctionDeclaration(v.getParentNode())
            &&
            // If we're only traversing a narrow scope, do not try to climb outside.
            (narrowScope == null || narrowScope.getDepth() <= v.getScope().getDepth())) {
          outOfBandTraversal(v);
        }
      }
    }

    if (isBlockBoundary(n, parent)) {
      pop(blockStack);
    }
  }
    public void findNamedFunctions(NodeTraversal t, Node n, Node parent) {
      if (!NodeUtil.isStatement(n)) {
        // There aren't any interesting functions here.
        return;
      }

      switch (n.getType()) {
          // Functions expressions in the form of:
          //   var fooFn = function(x) { return ... }
        case Token.VAR:
          Preconditions.checkState(n.hasOneChild());
          Node nameNode = n.getFirstChild();
          if (nameNode.isName()
              && nameNode.hasChildren()
              && nameNode.getFirstChild().isFunction()) {
            maybeAddFunction(new FunctionVar(n), t.getModule());
          }
          break;

          // Named functions
          // function Foo(x) { return ... }
        case Token.FUNCTION:
          Preconditions.checkState(NodeUtil.isStatementBlock(parent) || parent.isLabel());
          if (!NodeUtil.isFunctionExpression(n)) {
            Function fn = new NamedFunction(n);
            maybeAddFunction(fn, t.getModule());
          }
          break;
      }
    }
  public void testReport() {
    final List<JSError> errors = new ArrayList<JSError>();

    Compiler compiler =
        new Compiler(
            new BasicErrorManager() {

              @Override
              public void report(CheckLevel level, JSError error) {
                errors.add(error);
              }

              @Override
              public void println(CheckLevel level, JSError error) {}

              @Override
              protected void printSummary() {}
            });
    compiler.initCompilerOptionsIfTesting();

    NodeTraversal t = new NodeTraversal(compiler, null);
    DiagnosticType dt = DiagnosticType.warning("FOO", "{0}, {1} - {2}");

    t.report(null, dt, "Foo", "Bar", "Hello");
    assertEquals(1, errors.size());
    assertEquals("Foo, Bar - Hello", errors.get(0).description);
  }
  @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;
    }
  }
 /** Checks for illegal declarations. */
 private void checkDeclaration(NodeTraversal t, Node n) {
   if ("eval".equals(n.getString())) {
     t.report(n, EVAL_DECLARATION);
   } else if ("arguments".equals(n.getString())) {
     t.report(n, ARGUMENTS_DECLARATION);
   }
 }
  /**
   * 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 visit(NodeTraversal t, Node n, Node parent) {
      if (!NodeUtil.isReferenceName(n)) {
        return;
      }

      Scope referencedIn = t.getScope();
      String oldName = n.getString();
      Scope current = referencedIn;
      boolean doRename = false;
      String newName = null;
      while (current != null) {
        Map<String, String> renamesAtCurrentLevel = renameMap.get(current.getRootNode());
        if (current.isDeclared(oldName, false)) {
          return;
        } else if (renamesAtCurrentLevel != null && renamesAtCurrentLevel.containsKey(oldName)) {
          doRename = true;
          newName = renamesAtCurrentLevel.get(oldName);
          break;
        } else {
          current = current.getParent();
        }
      }
      if (doRename) {
        n.setString(newName);
        t.getCompiler().reportCodeChange();
      }
    }
 public void exitScope(NodeTraversal t) {
   Scope scope = t.getScope();
   Node node = t.getCurrentNode();
   if (scope.isLocal()) {
     inferTypes(t, node, scope);
   }
 }
  public void testUnexpectedException() {
    final String TEST_EXCEPTION = "test me";

    NodeTraversal.Callback cb =
        new NodeTraversal.AbstractPostOrderCallback() {

          public void visit(NodeTraversal t, Node n, Node parent) {
            throw new RuntimeException(TEST_EXCEPTION);
          }
        };

    Compiler compiler = new Compiler();
    NodeTraversal t = new NodeTraversal(compiler, cb);
    String code = "function foo() {}";
    Node tree = parse(compiler, code);

    try {
      t.traverse(tree);
      fail("Expected RuntimeException");
    } catch (RuntimeException e) {
      assertTrue(
          e.getMessage()
              .startsWith(
                  "INTERNAL COMPILER ERROR.\n" + "Please report this problem.\n" + "test me"));
    }
  }
  public void testGetCurrentNode() {
    Compiler compiler = new Compiler();
    ScopeCreator creator = new SyntacticScopeCreator(compiler);
    ExpectNodeOnEnterScope callback = new ExpectNodeOnEnterScope();
    NodeTraversal t = new NodeTraversal(compiler, callback, creator);

    String code = "" + "var a; " + "function foo() {" + "  var b;" + "}";

    Node tree = parse(compiler, code);
    Scope topScope = creator.createScope(tree, null);

    // Calling #traverseWithScope uses the given scope but starts traversal at
    // the given node.
    callback.expect(tree.getFirstChild(), tree);
    t.traverseWithScope(tree.getFirstChild(), topScope);
    callback.assertEntered();

    // Calling #traverse creates a new scope with the given node as the root.
    callback.expect(tree.getFirstChild(), tree.getFirstChild());
    t.traverse(tree.getFirstChild());
    callback.assertEntered();

    // Calling #traverseAtScope starts traversal from the scope's root.
    Node fn = tree.getFirstChild().getNext();
    Scope fnScope = creator.createScope(fn, topScope);
    callback.expect(fn, fn);
    t.traverseAtScope(fnScope);
    callback.assertEntered();
  }
  public void testGetScopeRoot() {
    Compiler compiler = new Compiler();
    NodeTraversal t =
        new NodeTraversal(
            compiler,
            new NodeTraversal.ScopedCallback() {

              public void enterScope(NodeTraversal t) {
                Node root1 = t.getScopeRoot();
                Node root2 = t.getScope().getRootNode();
                assertEquals(root1, root2);
              }

              public void exitScope(NodeTraversal t) {}

              public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
                return true;
              }

              public void visit(NodeTraversal t, Node n, Node parent) {}
            });

    String code = "" + "var a; " + "function foo() {" + "  var b" + "}";
    Node tree = parse(compiler, code);
    t.traverse(tree);
  }
 public void enterScope(NodeTraversal t) {
   if (t.inGlobalScope()) {
     scopes.push(typeSystem.getRootScope());
   } else {
     scopes.push(typeSystem.getFunctionScope(t.getScopeRoot()));
   }
 }
 /** Checks that object literal keys or class method names are valid. */
 private static void checkObjectLiteralOrClass(NodeTraversal t, Node n) {
   Set<String> getters = new HashSet<>();
   Set<String> setters = new HashSet<>();
   for (Node key = n.getFirstChild(); key != null; key = key.getNext()) {
     if (!key.isSetterDef()) {
       // normal property and getter cases
       if (!getters.add(key.getString())) {
         if (n.isClassMembers()) {
           t.report(key, DUPLICATE_CLASS_METHODS);
         } else {
           t.report(key, DUPLICATE_OBJECT_KEY);
         }
       }
     }
     if (!key.isGetterDef()) {
       // normal property and setter cases
       if (!setters.add(key.getString())) {
         if (n.isClassMembers()) {
           t.report(key, DUPLICATE_CLASS_METHODS);
         } else {
           t.report(key, DUPLICATE_OBJECT_KEY);
         }
       }
     }
   }
 }
  @Override
  public void process(Node externs, Node root) {
    Node initCode = null;
    if (!initCodeSource.isEmpty()) {
      Node initCodeRoot = compiler.parseSyntheticCode("template:init", initCodeSource);
      if (initCodeRoot != null && initCodeRoot.getFirstChild() != null) {
        initCode = initCodeRoot.removeChildren();
      } else {
        return; // parse failure
      }
    }

    NodeTraversal.traverseEs6(compiler, root, new RemoveCallback(declarationsToRemove));
    NodeTraversal.traverseEs6(compiler, root, new InstrumentCallback());

    if (!appNameSetter.isEmpty()) {
      Node call = IR.call(IR.name(appNameSetter), IR.string(appNameStr));
      call.putBooleanProp(Node.FREE_CALL, true);
      Node expr = IR.exprResult(call);

      Node addingRoot = compiler.getNodeForCodeInsertion(null);
      addingRoot.addChildrenToFront(expr.useSourceInfoIfMissingFromForTree(addingRoot));
      compiler.reportCodeChange();
    }

    if (initCode != null) {
      Node addingRoot = compiler.getNodeForCodeInsertion(null);
      addingRoot.addChildrenToFront(initCode);
      compiler.reportCodeChange();
    }
  }
Example #18
0
  @Override
  public void process(Node externs, Node root) {
    assignmentLog = new StringBuilder();

    // Do variable reference counting.
    NodeTraversal.traverse(compiler, externs, new ProcessVars(true));
    NodeTraversal.traverse(compiler, root, new ProcessVars(false));

    // Make sure that new names don't overlap with extern names.
    reservedNames.addAll(externNames);

    // Rename vars, sorted by frequency of occurrence to minimize code size.
    SortedSet<Assignment> varsByFrequency = new TreeSet<Assignment>(FREQUENCY_COMPARATOR);
    varsByFrequency.addAll(assignments.values());

    if (shouldShadow) {
      new ShadowVariables(compiler, assignments, varsByFrequency, pseudoNameMap)
          .process(externs, root);
    }

    // First try to reuse names from an earlier compilation.
    if (prevUsedRenameMap != null) {
      reusePreviouslyUsedVariableMap();
    }

    // Assign names, sorted by descending frequency to minimize code size.
    assignNames(varsByFrequency);

    boolean changed = false;

    // Rename the globals!
    for (Node n : globalNameNodes) {
      String newName = getNewGlobalName(n);
      // Note: if newName is null, then oldName is an extern.
      if (newName != null) {
        n.setString(newName);
        changed = true;
      }
    }

    // Rename the locals!
    int count = 0;
    for (Node n : localNameNodes) {
      String newName = getNewLocalName(n);
      if (newName != null) {
        n.setString(newName);
        changed = true;
      }
      count++;
    }

    if (changed) {
      compiler.reportCodeChange();
    }

    // Lastly, write the name assignments to the debug log.
    compiler.addToDebugLog("JS var assignments:\n" + assignmentLog);
    assignmentLog = null;
  }
Example #19
0
  @Override
  public void process(Node externs, Node root) {

    NameAnonyms na = new NameAnonyms(root, compiler, externs);
    NodeTraversal.traverse(compiler, root, na);

    NodeTraversal.traverse(compiler, root, functionListExtractor);
  }
 /** Checks that variables, functions, and arguments are not deleted. */
 private static void checkDelete(NodeTraversal t, Node n) {
   if (n.getFirstChild().isName()) {
     Var v = t.getScope().getVar(n.getFirstChild().getString());
     if (v != null) {
       t.report(n, DELETE_VARIABLE);
     }
   }
 }
    @Override
    public void visit(NodeTraversal t, Node n, Node parent) {
      if ((t.inGlobalScope() && inlineGlobalFunctions)
          || (!t.inGlobalScope() && inlineLocalFunctions)) {
        findNamedFunctions(t, n, parent);

        findFunctionExpressions(t, n);
      }
    }
Example #22
0
  @Override
  public void visit(NodeTraversal t, Node n, Node parent) {
    if (n.isString() && !parent.isGetProp() && !parent.isRegExp()) {

      String str = n.getString();

      // "undefined" is special-cased, since it needs to be used when JS code
      // is unloading and therefore variable references aren't available.
      // This is because of a bug in Firefox.
      if ("undefined".equals(str)) {
        return;
      }

      if (blacklist != null && blacklist.reset(str).find()) {
        return;
      }

      if (aliasableStrings == null || aliasableStrings.contains(str)) {
        StringOccurrence occurrence = new StringOccurrence(n, parent);
        StringInfo info = getOrCreateStringInfo(str);

        info.occurrences.add(occurrence);
        info.numOccurrences++;

        if (t.inGlobalScope() || isInThrowExpression(n)) {
          info.numOccurrencesInfrequentlyExecuted++;
        }

        // The current module.
        JSModule module = t.getModule();
        if (info.numOccurrences != 1) {
          // Check whether the current module depends on the module containing
          // the declaration.
          if (module != null
              && info.moduleToContainDecl != null
              && module != info.moduleToContainDecl
              && !moduleGraph.dependsOn(module, info.moduleToContainDecl)) {
            // We need to declare this string in the deepest module in the
            // module dependency graph that both of these modules depend on.
            module = moduleGraph.getDeepestCommonDependency(module, info.moduleToContainDecl);
          } else {
            // use the previously saved insertion location.
            return;
          }
        }
        Node varParent = moduleVarParentMap.get(module);
        if (varParent == null) {
          varParent = compiler.getNodeForCodeInsertion(module);
          moduleVarParentMap.put(module, varParent);
        }
        info.moduleToContainDecl = module;
        info.parentForNewVarDecl = varParent;
        info.siblingToInsertVarDeclBefore = varParent.getFirstChild();
      }
    }
  }
  private static void reportIfWasEmpty(NodeTraversal t, Node block) {
    Preconditions.checkState(block.isBlock());

    // A semicolon is distinguished from a block without children by
    // annotating it with EMPTY_BLOCK.  Blocks without children are
    // usually intentional, especially with loops.
    if (!block.hasChildren() && block.isAddedBlock()) {
      t.getCompiler().report(t.makeError(block, SUSPICIOUS_SEMICOLON));
    }
  }
 /** Checks that an assignment is not to the "arguments" object. */
 private static void checkAssignment(NodeTraversal t, Node n) {
   if (n.getFirstChild().isName()) {
     if ("arguments".equals(n.getFirstChild().getString())) {
       t.report(n, ARGUMENTS_ASSIGNMENT);
     } else if ("eval".equals(n.getFirstChild().getString())) {
       // Note that assignment to eval is already illegal because any use of
       // that name is illegal.
       t.report(n, EVAL_ASSIGNMENT);
     }
   }
 }
  public void process(Node externs, Node root) {
    for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) {
      addInvalidatingType(mis.typeA);
      addInvalidatingType(mis.typeB);
    }

    StaticScope<T> scope = typeSystem.getRootScope();
    NodeTraversal.traverse(compiler, externs, new FindExternProperties());
    NodeTraversal.traverse(compiler, root, new FindRenameableProperties());
    renameProperties();
  }
 /** Updates block stack and invokes any additional behavior. */
 @Override
 public void exitScope(NodeTraversal t) {
   pop(blockStack);
   if (t.getScope().isGlobal()) {
     // Update global scope reference lists when we are done with it.
     compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot());
     behavior.afterExitScope(t, compiler.getGlobalVarReferences());
   } else {
     behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap));
   }
 }
    /** Inline a function into the call site. */
    private void inlineFunction(NodeTraversal t, Reference ref, FunctionState fs) {
      Function fn = fs.getFn();
      String fnName = fn.getName();
      Node fnNode = fs.getSafeFnNode();

      Node newExpr = injector.inline(ref, fnName, fnNode);
      if (!newExpr.isEquivalentTo(ref.callNode)) {
        t.getCompiler().reportChangeToEnclosingScope(newExpr);
      }
      t.getCompiler().addToDebugLog("Inlined function: " + fn.getName());
    }
  @Override
  public void hotSwapScript(Node scriptRoot, Node originalRoot) {
    NodeTraversal.traverse(compiler, scriptRoot, new CollectUndeclaredNames());
    NodeTraversal.traverse(compiler, scriptRoot, this);
    NodeTraversal.traverse(compiler, scriptRoot, new RenameReferences());

    LoopClosureTransformer transformer = new LoopClosureTransformer();
    NodeTraversal.traverse(compiler, scriptRoot, transformer);
    transformer.transformLoopClosure();
    varify();
    NodeTraversal.traverse(compiler, scriptRoot, new RewriteBlockScopedFunctionDeclaration());
  }
  @Override
  public void process(Node externs, Node root) {
    NodeTraversal.traverseRoots(compiler, new CollectUndeclaredNames(), externs, root);
    NodeTraversal.traverseRoots(compiler, this, externs, root);
    NodeTraversal.traverseRoots(compiler, new RenameReferences(), externs, root);

    LoopClosureTransformer transformer = new LoopClosureTransformer();
    NodeTraversal.traverseRoots(compiler, transformer, externs, root);
    transformer.transformLoopClosure();
    varify();
    NodeTraversal.traverseRoots(
        compiler, new RewriteBlockScopedFunctionDeclaration(), externs, root);
  }
Example #30
0
    @Override
    public void enterScope(NodeTraversal t) {
      if (t.inGlobalScope()) return;

      Iterator<Var> it = t.getScope().getVars();
      while (it.hasNext()) {
        Var current = it.next();
        if (current.isBleedingFunction()) {
          localBleedingFunctions.add(current);
          localBleedingFunctionsPerScope.put(t.getScope().getParent(), current);
        }
      }
    }