Пример #1
0
 @Override
 public void visit(Node n, Node parent) {
   Node value = n.getFirstChild();
   if (value.isNumber() && value.getDouble() == 0) {
     super.visit(n, parent);
   }
 }
 private Node tryReduceVoid(Node n) {
   Node child = n.getFirstChild();
   if ((!child.isNumber() || child.getDouble() != 0.0) && !mayHaveSideEffects(n)) {
     n.replaceChild(child, IR.number(0));
     reportCodeChange();
   }
   return n;
 }
  private Node tryFoldArrayAccess(Node n, Node left, Node right) {
    // If GETPROP/GETELEM is used as assignment target the array literal is
    // acting as a temporary we can't fold it here:
    //    "[][0] += 1"
    if (NodeUtil.isAssignmentTarget(n)) {
      return n;
    }

    if (!right.isNumber()) {
      // Sometimes people like to use complex expressions to index into
      // arrays, or strings to index into array methods.
      return n;
    }

    double index = right.getDouble();
    int intIndex = (int) index;
    if (intIndex != index) {
      report(INVALID_GETELEM_INDEX_ERROR, right);
      return n;
    }

    if (intIndex < 0) {
      report(INDEX_OUT_OF_BOUNDS_ERROR, right);
      return n;
    }

    Node current = left.getFirstChild();
    Node elem = null;
    for (int i = 0; current != null; i++) {
      if (i != intIndex) {
        if (mayHaveSideEffects(current)) {
          return n;
        }
      } else {
        elem = current;
      }

      current = current.getNext();
    }

    if (elem == null) {
      report(INDEX_OUT_OF_BOUNDS_ERROR, right);
      return n;
    }

    if (elem.isEmpty()) {
      elem = NodeUtil.newUndefinedNode(elem);
    } else {
      left.removeChild(elem);
    }

    // Replace the entire GETELEM with the value
    n.getParent().replaceChild(n, elem);
    reportCodeChange();
    return elem;
  }
Пример #4
0
 private void validateNumber(Node n) {
   validateNodeType(Token.NUMBER, n);
   validateChildCount(n);
   try {
     // Validate that getDouble doesn't throw
     n.getDouble();
   } catch (UnsupportedOperationException e) {
     violation("Invalid NUMBER node.", n);
   }
 }
  private Node tryFoldStringArrayAccess(Node n, Node left, Node right) {
    // If GETPROP/GETELEM is used as assignment target the array literal is
    // acting as a temporary we can't fold it here:
    //    "[][0] += 1"
    if (NodeUtil.isAssignmentTarget(n)) {
      return n;
    }

    if (!right.isNumber()) {
      // Sometimes people like to use complex expressions to index into
      // arrays, or strings to index into array methods.
      return n;
    }

    double index = right.getDouble();
    int intIndex = (int) index;
    if (intIndex != index) {
      report(INVALID_GETELEM_INDEX_ERROR, right);
      return n;
    }

    if (intIndex < 0) {
      report(INDEX_OUT_OF_BOUNDS_ERROR, right);
      return n;
    }

    Preconditions.checkState(left.isString());
    String value = left.getString();
    if (intIndex >= value.length()) {
      report(INDEX_OUT_OF_BOUNDS_ERROR, right);
      return n;
    }

    char c = 0;
    // Note: For now skip the strings with unicode
    // characters as I don't understand the differences
    // between Java and JavaScript.
    for (int i = 0; i <= intIndex; i++) {
      c = value.charAt(i);
      if (c < 32 || c > 127) {
        return n;
      }
    }
    Node elem = IR.string(Character.toString(c));

    // Replace the entire GETELEM with the value
    n.getParent().replaceChild(n, elem);
    reportCodeChange();
    return elem;
  }
    /**
     * Remove all references to a parameter if possible otherwise simplify the side-effect free
     * parameters.
     */
    private void tryRemoveArgFromCallSites(Node function, int argIndex, boolean canModifyAllSites) {
      Definition definition = getFunctionDefinition(function);

      for (UseSite site : defFinder.getUseSites(definition)) {
        if (isModifiableCallSite(site)) {
          Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex);
          if (arg != null) {
            Node argParent = arg.getParent();
            // Even if we can't change the signature in general we can always
            // remove an unused value off the end of the parameter list.
            if (canModifyAllSites
                || (arg.getNext() == null && !NodeUtil.mayHaveSideEffects(arg, compiler))) {
              toRemove.add(arg);
            } else {
              // replace the node in the arg with 0
              if (!NodeUtil.mayHaveSideEffects(arg, compiler)
                  && (arg.getType() != Token.NUMBER || arg.getDouble() != 0)) {
                toReplaceWithZero.add(arg);
              }
            }
          }
        }
      }
    }
  /**
   * Tries to optimize all the arguments array access in this scope by assigning a name to each
   * element.
   *
   * @param scope scope of the function
   * @return true if any modification has been done to the AST
   */
  private boolean tryReplaceArguments(Scope scope) {

    Node parametersList = scope.getRootNode().getSecondChild();
    Preconditions.checkState(parametersList.isParamList());

    // Keep track of rather this function modified the AST and needs to be
    // reported back to the compiler later.
    boolean changed = false;

    // Number of parameter that can be accessed without using the arguments
    // array.
    int numNamedParameter = parametersList.getChildCount();

    // We want to guess what the highest index that has been access from the
    // arguments array. We will guess that it does not use anything index higher
    // than the named parameter list first until we see other wise.
    int highestIndex = numNamedParameter - 1;

    // Iterate through all the references to arguments array in the function to
    // determine the real highestIndex.
    for (Node ref : currentArgumentsAccess) {

      Node getElem = ref.getParent();

      // Bail on anything but argument[c] access where c is a constant.
      // TODO(user): We might not need to bail out all the time, there might
      // be more cases that we can cover.
      if (!getElem.isGetElem() || ref != getElem.getFirstChild()) {
        return false;
      }

      Node index = ref.getNext();

      // We have something like arguments[x] where x is not a constant. That
      // means at least one of the access is not known.
      if (!index.isNumber() || index.getDouble() < 0) {
        // TODO(user): Its possible not to give up just yet. The type
        // inference did a 'semi value propagation'. If we know that string
        // is never a subclass of the type of the index. We'd know that
        // it is never 'callee'.
        return false; // Give up.
      }

      Node getElemParent = getElem.getParent();
      // When we have argument[0](), replacing it with a() is semantically
      // different if argument[0] is a function call that refers to 'this'
      if (getElemParent.isCall() && getElemParent.getFirstChild() == getElem) {
        // TODO(user): We can consider using .call() if aliasing that
        // argument allows shorter alias for other arguments.
        return false;
      }

      // Replace the highest index if we see an access that has a higher index
      // than all the one we saw before.
      int value = (int) index.getDouble();
      if (value > highestIndex) {
        highestIndex = value;
      }
    }

    // Number of extra arguments we need.
    // For example: function() { arguments[3] } access index 3 so
    // it will need 4 extra named arguments to changed into:
    // function(a,b,c,d) { d }.
    int numExtraArgs = highestIndex - numNamedParameter + 1;

    // Temporary holds the new names as string for quick access later.
    String[] argNames = new String[numExtraArgs];

    // Insert the formal parameter to the method's signature.
    // Example: function() --> function(r0, r1, r2)
    for (int i = 0; i < numExtraArgs; i++) {
      String name = getNewName();
      argNames[i] = name;
      parametersList.addChildToBack(IR.name(name).useSourceInfoIfMissingFrom(parametersList));
      changed = true;
    }

    // This loop performs the replacement of arguments[x] -> a if x is known.
    for (Node ref : currentArgumentsAccess) {
      Node index = ref.getNext();

      // Skip if it is unknown.
      if (!index.isNumber()) {
        continue;
      }
      int value = (int) index.getDouble();

      // Unnamed parameter.
      if (value >= numNamedParameter) {
        ref.getGrandparent()
            .replaceChild(ref.getParent(), IR.name(argNames[value - numNamedParameter]));
      } else {

        // Here, for no apparent reason, the user is accessing a named parameter
        // with arguments[idx]. We can replace it with the actual name for them.
        Node name = parametersList.getFirstChild();

        // This is a linear search for the actual name from the signature.
        // It is not necessary to make this fast because chances are the user
        // will not deliberately write code like this.
        for (int i = 0; i < value; i++) {
          name = name.getNext();
        }
        ref.getGrandparent().replaceChild(ref.getParent(), IR.name(name.getString()));
      }
      changed = true;
    }

    return changed;
  }
  /** Try to fold shift operations */
  private Node tryFoldShift(Node n, Node left, Node right) {
    if (left.isNumber() && right.isNumber()) {

      double result;
      double lval = left.getDouble();
      double rval = right.getDouble();

      // check ranges.  We do not do anything that would clip the double to
      // a 32-bit range, since the user likely does not intend that.
      if (lval < Integer.MIN_VALUE) {
        report(BITWISE_OPERAND_OUT_OF_RANGE, left);
        return n;
      }
      // only the lower 5 bits are used when shifting, so don't do anything
      // if the shift amount is outside [0,32)
      if (!(rval >= 0 && rval < 32)) {
        report(SHIFT_AMOUNT_OUT_OF_BOUNDS, n);
        return n;
      }

      int rvalInt = (int) rval;
      if (rvalInt != rval) {
        report(FRACTIONAL_BITWISE_OPERAND, right);
        return n;
      }

      switch (n.getType()) {
        case LSH:
        case RSH:
          // Convert the numbers to ints
          if (lval > Integer.MAX_VALUE) {
            report(BITWISE_OPERAND_OUT_OF_RANGE, left);
            return n;
          }
          int lvalInt = (int) lval;
          if (lvalInt != lval) {
            report(FRACTIONAL_BITWISE_OPERAND, left);
            return n;
          }
          if (n.getType() == Token.LSH) {
            result = lvalInt << rvalInt;
          } else {
            result = lvalInt >> rvalInt;
          }
          break;
        case URSH:
          // JavaScript handles zero shifts on signed numbers differently than
          // Java as an Java int can not represent the unsigned 32-bit number
          // where JavaScript can so use a long here.
          long maxUint32 = 0xffffffffL;
          if (lval > maxUint32) {
            report(BITWISE_OPERAND_OUT_OF_RANGE, left);
            return n;
          }
          long lvalLong = (long) lval;
          if (lvalLong != lval) {
            report(FRACTIONAL_BITWISE_OPERAND, left);
            return n;
          }
          result = (lvalLong & maxUint32) >>> rvalInt;
          break;
        default:
          throw new AssertionError("Unknown shift operator: " + n.getType());
      }

      Node newNumber = IR.number(result);
      n.getParent().replaceChild(n, newNumber);
      reportCodeChange();

      return newNumber;
    }

    return n;
  }
  private Node tryFoldUnaryOperator(Node n) {
    Preconditions.checkState(n.hasOneChild(), n);

    Node left = n.getFirstChild();
    Node parent = n.getParent();

    if (left == null) {
      return n;
    }

    TernaryValue leftVal = NodeUtil.getPureBooleanValue(left);
    if (leftVal == TernaryValue.UNKNOWN) {
      return n;
    }

    switch (n.getType()) {
      case NOT:
        // Don't fold !0 and !1 back to false.
        if (late && left.isNumber()) {
          double numValue = left.getDouble();
          if (numValue == 0 || numValue == 1) {
            return n;
          }
        }
        Node replacementNode = NodeUtil.booleanNode(!leftVal.toBoolean(true));
        parent.replaceChild(n, replacementNode);
        reportCodeChange();
        return replacementNode;
      case POS:
        if (NodeUtil.isNumericResult(left)) {
          // POS does nothing to numeric values.
          parent.replaceChild(n, left.detachFromParent());
          reportCodeChange();
          return left;
        }
        return n;
      case NEG:
        if (left.isName()) {
          if (left.getString().equals("Infinity")) {
            // "-Infinity" is valid and a literal, don't modify it.
            return n;
          } else if (left.getString().equals("NaN")) {
            // "-NaN" is "NaN".
            n.removeChild(left);
            parent.replaceChild(n, left);
            reportCodeChange();
            return left;
          }
        }

        if (left.isNumber()) {
          double negNum = -left.getDouble();

          Node negNumNode = IR.number(negNum);
          parent.replaceChild(n, negNumNode);
          reportCodeChange();
          return negNumNode;
        } else {
          // left is not a number node, so do not replace, but warn the
          // user because they can't be doing anything good
          report(NEGATING_A_NON_NUMBER_ERROR, left);
          return n;
        }
      case BITNOT:
        try {
          double val = left.getDouble();
          if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) {
            int intVal = (int) val;
            if (intVal == val) {
              Node notIntValNode = IR.number(~intVal);
              parent.replaceChild(n, notIntValNode);
              reportCodeChange();
              return notIntValNode;
            } else {
              report(FRACTIONAL_BITWISE_OPERAND, left);
              return n;
            }
          } else {
            report(BITWISE_OPERAND_OUT_OF_RANGE, left);
            return n;
          }
        } catch (UnsupportedOperationException ex) {
          // left is not a number node, so do not replace, but warn the
          // user because they can't be doing anything good
          report(NEGATING_A_NON_NUMBER_ERROR, left);
          return n;
        }
      default:
        return n;
    }
  }
  /**
   * Returns whether two nodes are equivalent, taking into account the template parameters that were
   * provided to this matcher. If the template comparison node is a parameter node, then only the
   * types of the node must match. Otherwise, the node must be equal and the child nodes must be
   * equivalent according to the same function. This differs from the built in Node equivalence
   * function with the special comparison.
   */
  private boolean matchesNode(Node template, Node ast) {
    if (isTemplateParameterNode(template)) {
      int paramIndex = (int) (template.getDouble());
      Node previousMatch = paramNodeMatches.get(paramIndex);
      if (previousMatch != null) {
        // If this named node has already been matched against, make sure all
        // subsequent usages of the same named node are equivalent.
        return ast.isEquivalentTo(previousMatch);
      }

      // Only the types need to match for the template parameters, which allows
      // the template function to express arbitrary expressions.
      JSType templateType = template.getJSType();

      Preconditions.checkNotNull(templateType, "null template parameter type.");

      // TODO(johnlenz): We shouldn't spend time checking template whose
      // types whose definitions aren't included (NoResolvedType). Alternately
      // we should treat them as "unknown" and perform loose matches.
      if (templateType.isNoResolvedType()) {
        return false;
      }

      MatchResult matchResult = typeMatchingStrategy.match(templateType, ast.getJSType());
      isLooseMatch = matchResult.isLooseMatch();
      boolean isMatch = matchResult.isMatch();
      if (isMatch && previousMatch == null) {
        paramNodeMatches.set(paramIndex, ast);
      }
      return isMatch;
    } else if (isTemplateLocalNameNode(template)) {
      // If this template name node was already matched against, then make sure
      // all subsequent usages of the same template name node are equivalent in
      // the matched code.
      // For example, this code will handle the case:
      // function template() {
      //   var a = 'str';
      //   fn(a);
      // }
      //
      // will only match test code:
      //   var b = 'str';
      //   fn(b);
      //
      // but it will not match:
      //   var b = 'str';
      //   fn('str');
      int paramIndex = (int) (template.getDouble());
      boolean previouslyMatched = this.localVarMatches.get(paramIndex) != null;
      if (previouslyMatched) {
        // If this named node has already been matched against, make sure all
        // subsequent usages of the same named node are equivalent.
        return ast.getString().equals(this.localVarMatches.get(paramIndex));
      } else {
        this.localVarMatches.set(paramIndex, ast.getString());
      }
    }

    // Template and AST shape has already been checked, but continue look for
    // other template variables (parameters and locals) that must be checked.
    Node templateChild = template.getFirstChild();
    Node astChild = ast.getFirstChild();
    while (templateChild != null) {
      if (!matchesNode(templateChild, astChild)) {
        return false;
      }
      templateChild = templateChild.getNext();
      astChild = astChild.getNext();
    }

    return true;
  }