@Override
    public void visit(NodeTraversal t, Node n, Node parent) {
      if ((t.inGlobalScope() && inlineGlobalFunctions)
          || (!t.inGlobalScope() && inlineLocalFunctions)) {
        findNamedFunctions(t, n, parent);

        findFunctionExpressions(t, n);
      }
    }
 public void enterScope(NodeTraversal t) {
   if (t.inGlobalScope()) {
     scopes.push(typeSystem.getRootScope());
   } else {
     scopes.push(typeSystem.getFunctionScope(t.getScopeRoot()));
   }
 }
Пример #3
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();
      }
    }
  }
Пример #4
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);
        }
      }
    }
  /**
   * Determines whether a function can be inlined at a particular call site. - Don't inline if the
   * calling function contains an inner function and inlining would introduce new globals.
   */
  private boolean callMeetsBlockInliningRequirements(
      NodeTraversal t, Node callNode, Node fnNode, Set<String> namesToAlias) {
    // Note: functions that contain function definitions are filtered out
    // in isCanidateFunction.

    // TODO(johnlenz): Determining if the called function contains VARs
    // or if the caller contains inner functions accounts for 20% of the
    // runtime cost of this pass.

    // Don't inline functions with var declarations into a scope with inner
    // functions as the new vars would leak into the inner function and
    // cause memory leaks.
    boolean fnContainsVars =
        NodeUtil.has(
            NodeUtil.getFunctionBody(fnNode),
            new NodeUtil.MatchDeclaration(),
            new NodeUtil.MatchShallowStatement());
    boolean callerContainsFunction = false;
    if (!t.inGlobalScope()) {
      Node fnCaller = t.getScopeRoot();
      Node fnCallerBody = fnCaller.getLastChild();

      callerContainsFunction = NodeUtil.containsFunction(fnCallerBody);
    }

    if (fnContainsVars && callerContainsFunction) {
      return false;
    }

    // If the caller contains functions, verify we aren't adding any
    // additional VAR declarations because aliasing is needed.
    if (callerContainsFunction) {
      Map<String, Node> args =
          FunctionArgumentInjector.getFunctionCallParameterMap(
              fnNode, callNode, this.safeNameIdSupplier);
      boolean hasArgs = !args.isEmpty();
      if (hasArgs) {
        // Limit the inlining
        Set<String> allNamesToAlias = Sets.newHashSet(namesToAlias);
        FunctionArgumentInjector.maybeAddTempsForCallArguments(
            fnNode, args, allNamesToAlias, compiler.getCodingConvention());
        if (!allNamesToAlias.isEmpty()) {
          return false;
        }
      }
    }

    return true;
  }
  /**
   * @param t The traversal use to reach the call site.
   * @param callNode The CALL node.
   * @param fnNode The function to evaluate for inlining.
   * @param needAliases A set of function parameter names that can not be used without aliasing.
   *     Returned by getUnsafeParameterNames().
   * @param mode Inlining mode to be used.
   * @param referencesThis Whether fnNode contains references to its this object.
   * @param containsFunctions Whether fnNode contains inner functions.
   * @return Whether the inlining can occur.
   */
  CanInlineResult canInlineReferenceToFunction(
      NodeTraversal t,
      Node callNode,
      Node fnNode,
      Set<String> needAliases,
      InliningMode mode,
      boolean referencesThis,
      boolean containsFunctions) {
    // TODO(johnlenz): This function takes too many parameter, without
    // context.  Modify the API to take a structure describing the function.

    // Allow direct function calls or "fn.call" style calls.
    if (!isSupportedCallType(callNode)) {
      return CanInlineResult.NO;
    }

    // Limit where functions that contain functions can be inline.  Introducing
    // an inner function into another function can capture a variable and cause
    // a memory leak.  This isn't a problem in the global scope as those values
    // last until explicitly cleared.
    if (containsFunctions && !t.inGlobalScope()) {
      // TODO(johnlenz): Allow inlining into any scope without local names or
      // inner functions.
      return CanInlineResult.NO;
    }

    // TODO(johnlenz): Add support for 'apply'
    if (referencesThis && !NodeUtil.isFunctionObjectCall(callNode)) {
      // TODO(johnlenz): Allow 'this' references to be replaced with a
      // global 'this' object.
      return CanInlineResult.NO;
    }

    if (mode == InliningMode.DIRECT) {
      return canInlineReferenceDirectly(callNode, fnNode);
    } else {
      return canInlineReferenceAsStatementBlock(t, callNode, fnNode, needAliases);
    }
  }
 /** Returns the function the traversal is currently traversing, or null if in the global scope. */
 private static Node getContainingFunction(NodeTraversal t) {
   return (t.inGlobalScope()) ? null : t.getScopeRoot();
 }
 @Override
 public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) {
   // Don't traverse into function bodies
   // if we aren't inlining local functions.
   return inlineLocalFunctions || nodeTraversal.inGlobalScope();
 }
Пример #9
0
    @SuppressWarnings("incomplete-switch")
    @Override
    public void visit(NodeTraversal t, Node n, Node parent) {
      if (!n.isCall()) {
        return;
      }

      String callName = n.getFirstChild().getQualifiedName();
      TweakFunction tweakFunc = TWEAK_FUNCTIONS_MAP.get(callName);
      if (tweakFunc == null) {
        return;
      }

      if (tweakFunc == TweakFunction.GET_COMPILER_OVERRIDES) {
        getOverridesCalls.add(new TweakFunctionCall(tweakFunc, n));
        return;
      }

      // Ensure the first parameter (the tweak ID) is a string literal.
      Node tweakIdNode = n.getFirstChild().getNext();
      if (!tweakIdNode.isString()) {
        compiler.report(t.makeError(tweakIdNode, NON_LITERAL_TWEAK_ID_ERROR));
        return;
      }
      String tweakId = tweakIdNode.getString();

      // Make sure there is a TweakInfo structure for it.
      TweakInfo tweakInfo = allTweaks.get(tweakId);
      if (tweakInfo == null) {
        tweakInfo = new TweakInfo(tweakId);
        allTweaks.put(tweakId, tweakInfo);
      }

      switch (tweakFunc) {
        case REGISTER_BOOLEAN:
        case REGISTER_NUMBER:
        case REGISTER_STRING:
          // Ensure the ID contains only valid characters.
          if (!ID_MATCHER.matchesAllOf(tweakId)) {
            compiler.report(t.makeError(tweakIdNode, INVALID_TWEAK_ID_ERROR));
          }

          // Ensure tweaks are registered in the global scope.
          if (!t.inGlobalScope()) {
            compiler.report(t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId));
            break;
          }

          // Ensure tweaks are registered only once.
          if (tweakInfo.isRegistered()) {
            compiler.report(t.makeError(n, TWEAK_MULTIPLY_REGISTERED_ERROR, tweakId));
            break;
          }

          Node tweakDefaultValueNode = tweakIdNode.getNext().getNext();
          tweakInfo.addRegisterCall(t.getSourceName(), tweakFunc, n, tweakDefaultValueNode);
          break;
        case OVERRIDE_DEFAULT_VALUE:
          // Ensure tweaks overrides occur in the global scope.
          if (!t.inGlobalScope()) {
            compiler.report(t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId));
            break;
          }
          // Ensure tweak overrides occur before the tweak is registered.
          if (tweakInfo.isRegistered()) {
            compiler.report(t.makeError(n, TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR, tweakId));
            break;
          }

          tweakDefaultValueNode = tweakIdNode.getNext();
          tweakInfo.addOverrideDefaultValueCall(
              t.getSourceName(), tweakFunc, n, tweakDefaultValueNode);
          break;
        case GET_BOOLEAN:
        case GET_NUMBER:
        case GET_STRING:
          tweakInfo.addGetterCall(t.getSourceName(), tweakFunc, n);
      }
    }