Beispiel #1
0
    /**
     * Creates a JMultiExpression from a set of JExpressionStatements, optionally terminated by a
     * JReturnStatement. If the method doesn't match this pattern, it returns <code>null</code>.
     *
     * <p>If a method has a non-void return statement and can be represented as a multi-expression,
     * the output of the multi-expression will be the return expression of the method. If the method
     * is void, the output of the multi-expression should be considered undefined.
     */
    private JMultiExpression createMultiExpressionFromBody(
        JMethodBody body, boolean ignoringReturnValue) {
      JMultiExpression multi = new JMultiExpression(body.getSourceInfo());
      CloneCalleeExpressionVisitor cloner = new CloneCalleeExpressionVisitor();

      for (JStatement stmt : body.getStatements()) {
        if (stmt instanceof JExpressionStatement) {
          JExpressionStatement exprStmt = (JExpressionStatement) stmt;
          JExpression expr = exprStmt.getExpr();
          JExpression clone = cloner.cloneExpression(expr);
          multi.addExpressions(clone);
        } else if (stmt instanceof JReturnStatement) {
          JReturnStatement returnStatement = (JReturnStatement) stmt;
          JExpression expr = returnStatement.getExpr();
          if (expr != null) {
            if (!ignoringReturnValue || expr.hasSideEffects()) {
              JExpression clone = cloner.cloneExpression(expr);
              clone = maybeCast(clone, body.getMethod().getType());
              multi.addExpressions(clone);
            }
          }
          // We hit an unconditional return; no need to evaluate anything else.
          break;
        } else {
          // Any other kind of statement won't be inlinable.
          return null;
        }
      }

      return multi;
    }
Beispiel #2
0
 /**
  * Returns the ending statement for a method based on an expression. If the return type is void
  * then the ending statement just executes the expression otherwise it returns it.
  */
 public static JStatement makeMethodEndStatement(JType returnType, JExpression expression) {
   // TODO(rluble): Check if something needs to be done here regarding boxing/unboxing/coercions
   // when one of the types of expression and returnType is a boxed type and the other a primitive
   // type or both are primitive of differnent coerceable types. Add the proper tests first.
   return returnType == JPrimitiveType.VOID
       ? expression.makeStatement()
       : expression.makeReturnStatement();
 }
    private JExpression maskUndefined(JExpression lhs) {
      assert ((JReferenceType) lhs.getType()).canBeNull();

      JMethod maskMethod = program.getIndexedMethod("Cast.maskUndefined");
      JMethodCall lhsCall = new JMethodCall(lhs.getSourceInfo(), null, maskMethod, lhs.getType());
      lhsCall.addArg(lhs);
      return lhsCall;
    }
Beispiel #4
0
 @Override
 public boolean hasSideEffects() {
   for (JExpression expression : expressions) {
     if (expression.hasSideEffects()) {
       return true;
     }
   }
   return false;
 }
Beispiel #5
0
 /**
  * Insert an implicit cast if the types differ; it might get optimized out later, but in some
  * cases it will force correct math evaluation.
  */
 private JExpression maybeCast(JExpression exp, JType targetType) {
   if (targetType instanceof JReferenceType) {
     assert exp.getType() instanceof JReferenceType;
     targetType = merge((JReferenceType) exp.getType(), (JReferenceType) targetType);
   }
   if (!program.typeOracle.castSucceedsTrivially(exp.getType(), targetType)) {
     exp = new JCastOperation(exp.getSourceInfo(), targetType, exp);
   }
   return exp;
 }
Beispiel #6
0
 @Override
 public void endVisit(JDeclarationStatement x, Context ctx) {
   super.endVisit(x, ctx);
   lValues.pop();
   // The variable may have been pruned.
   if (isVariablePruned(x.getVariableRef().getTarget())) {
     JExpression replacement =
         makeReplacementForAssignment(x.getSourceInfo(), x.getVariableRef(), x.getInitializer());
     ctx.replaceMe(replacement.makeStatement());
   }
 }
Beispiel #7
0
      @Override
      JsLiteral translate(JExpression literal) {
        SourceInfo sourceInfo = literal.getSourceInfo();
        long[] values = LongLib.getAsLongArray(((JLongLiteral) literal).getValue());
        if (values.length == 1) {
          return new JsNumberLiteral(literal.getSourceInfo(), ((JLongLiteral) literal).getValue());
        }
        JsObjectLiteral objectLiteral = new JsObjectLiteral(sourceInfo);
        objectLiteral.setInternable();

        assert values.length == names.length;
        for (int i = 0; i < names.length; i++) {
          addPropertyToObject(sourceInfo, names[i], values[i], objectLiteral);
        }
        return objectLiteral;
      }
Beispiel #8
0
  /**
   * Transform a call to a pruned instance method (or static impl) into a call to the null method,
   * which will be used to replace <code>x</code>.
   */
  public static JMethodCall transformToNullMethodCall(JMethodCall x, JProgram program) {
    JExpression instance = x.getInstance();
    List<JExpression> args = x.getArgs();
    if (program.isStaticImpl(x.getTarget())) {
      instance = args.get(0);
      args = args.subList(1, args.size());
    } else {
      /*
       * We assert that method must be non-static, otherwise it would have been
       * rescued.
       */
      // assert !x.getTarget().isStatic();
      /*
       * HACK HACK HACK: ControlFlowAnalyzer has special hacks for dealing with
       * ClassLiterals, which causes the body of ClassLiteralHolder's clinit to
       * never be rescured. This in turn causes invalid references to static
       * methods, which violates otherwise good assumptions about compiler
       * operation.
       *
       * TODO: Remove this when ControlFlowAnalyzer doesn't special-case
       * CLH.clinit().
       */
      if (x.getTarget().isStatic() && instance == null) {
        instance = program.getLiteralNull();
      }
    }
    assert (instance != null);
    if (!instance.hasSideEffects()) {
      instance = program.getLiteralNull();
    }

    JMethodCall newCall =
        new JMethodCall(
            x.getSourceInfo(),
            instance,
            program.getNullMethod(),
            primitiveTypeOrNullTypeOrArray(program, x.getType()));
    // Retain the original arguments, they will be evaluated for side effects.
    for (JExpression arg : args) {
      if (arg.hasSideEffects()) {
        newCall.addArg(arg);
      }
    }
    return newCall;
  }
    @Override
    public void endVisit(JThrowStatement x, Context ctx) {
      assert jseType != null;

      JExpression expr = x.getExpr();

      // Optimization: unwrap not needed if "new BlahException()"
      if (expr instanceof JNewInstance && !expr.getType().equals(jseType)) {
        return;
      }

      // Optimization: unwrap not needed if expression can never be JavaScriptException
      if (!program.typeOracle.canTheoreticallyCast((JReferenceType) expr.getType(), jseType)) {
        return;
      }

      // throw x; -> throw Exceptions.unwrap(x);
      ctx.replaceMe(createUnwrappedThrow(x));
    }
Beispiel #10
0
  /**
   * Creates a multi expression from a list of expressions, removing expressions that do not have
   * side effects if possible.
   */
  public static JExpression createOptimizedMultiExpression(
      boolean ignoringResult, List<JExpression> expressions) {

    int numberOfExpressions = expressions.size();
    JExpression result = expressions.get(numberOfExpressions - 1);

    numberOfExpressions = expressions.size();
    if (numberOfExpressions == 0) {
      return new JMultiExpression(SourceOrigin.UNKNOWN);
    }

    expressions =
        Lists.newArrayList(
            Collections2.filter(
                expressions.subList(0, numberOfExpressions - 1),
                Predicates.and(
                    Predicates.notNull(),
                    new Predicate<JExpression>() {
                      @Override
                      public boolean apply(JExpression expression) {
                        return expression.hasSideEffects();
                      }
                    })));

    if (result != null && (!ignoringResult || result.hasSideEffects())) {
      expressions.add(result);
    }

    if (expressions.size() == 1) {
      // Do not create a multi expression if it consists only of the result.
      return expressions.iterator().next();
    }

    SourceInfo info =
        expressions.size() > 0 ? expressions.get(0).getSourceInfo() : SourceOrigin.UNKNOWN;
    return new JMultiExpression(info, expressions);
  }
Beispiel #11
0
  /**
   * Installs the initial load sequence into AsyncFragmentLoader.BROWSER_LOADER. The initializer
   * looks like this:
   *
   * <pre>
   * AsyncFragmentLoader BROWSER_LOADER = makeBrowserLoader(1, new int[]{});
   * </pre>
   *
   * The second argument (<code>new int[]</code>) gets replaced by an array corresponding to <code>
   * initialLoadSequence</code>.
   */
  private static void installInitialLoadSequenceField(
      JProgram program, LinkedHashSet<JRunAsync> initialLoadSequence) {
    // Arg 1 is initialized in the source as "new int[]{}".
    JMethodCall call = ReplaceRunAsyncs.getBrowserLoaderConstructor(program);
    JExpression arg1 = call.getArgs().get(1);
    assert arg1 instanceof JNewArray;
    JArrayType arrayType = program.getTypeArray(JPrimitiveType.INT);
    assert ((JNewArray) arg1).getArrayType() == arrayType;
    List<JExpression> initializers = new ArrayList<JExpression>(initialLoadSequence.size());

    // RunAsyncFramentIndex will later be replaced by the fragment the async is in.
    // TODO(rluble): this approach is not very clean, ideally the load sequence should be
    // installed AFTER code splitting when the fragment ids are known; rather than inserting
    // a placeholder in the AST and patching the ast later.
    for (JRunAsync runAsync : initialLoadSequence) {
      initializers.add(
          new JNumericEntry(
              call.getSourceInfo(), "RunAsyncFragmentIndex", runAsync.getRunAsyncId()));
    }
    JNewArray newArray =
        JNewArray.createArrayWithInitializers(
            arg1.getSourceInfo(), arrayType, Lists.newArrayList(initializers));
    call.setArg(1, newArray);
  }
Beispiel #12
0
  /**
   * Transform a reference to a pruned instance field into a reference to the null field, which will
   * be used to replace <code>x</code>.
   */
  public static JFieldRef transformToNullFieldRef(JFieldRef x, JProgram program) {
    JExpression instance = x.getInstance();

    /*
     * We assert that field must be non-static if it's an rvalue, otherwise it
     * would have been rescued.
     */
    // assert !x.getField().isStatic();
    /*
     * HACK HACK HACK: ControlFlowAnalyzer has special hacks for dealing with
     * ClassLiterals, which causes the body of ClassLiteralHolder's clinit to
     * never be rescured. This in turn causes invalid references to static
     * methods, which violates otherwise good assumptions about compiler
     * operation.
     *
     * TODO: Remove this when ControlFlowAnalyzer doesn't special-case
     * CLH.clinit().
     */
    if (x.getField().isStatic() && instance == null) {
      instance = program.getLiteralNull();
    }

    assert instance != null;
    if (!instance.hasSideEffects()) {
      instance = program.getLiteralNull();
    }

    JFieldRef fieldRef =
        new JFieldRef(
            x.getSourceInfo(),
            instance,
            program.getNullField(),
            x.getEnclosingType(),
            primitiveTypeOrNullTypeOrArray(program, x.getType()));
    return fieldRef;
  }
Beispiel #13
0
  /** Returns an ast node representing the expression {@code expression != null}. */
  public static JExpression createOptimizedNotNullComparison(
      JProgram program, SourceInfo info, JExpression expression) {
    JReferenceType type = (JReferenceType) expression.getType();
    if (type.isNullType()) {
      return program.getLiteralBoolean(false);
    }

    if (!type.canBeNull()) {
      return createOptimizedMultiExpression(expression, program.getLiteralBoolean(true));
    }

    return new JBinaryOperation(
        info,
        program.getTypePrimitiveBoolean(),
        JBinaryOperator.NEQ,
        expression,
        program.getLiteralNull());
  }
    @Override
    public void endVisit(JBinaryOperation x, Context ctx) {
      JBinaryOperator op = x.getOp();
      if (op != JBinaryOperator.EQ && op != JBinaryOperator.NEQ) {
        return;
      }
      JExpression lhs = x.getLhs();
      JExpression rhs = x.getRhs();
      JType lhsType = lhs.getType();
      JType rhsType = rhs.getType();
      if (!(lhsType instanceof JReferenceType)) {
        assert !(rhsType instanceof JReferenceType);
        return;
      }

      StringStatus lhsStatus = getStringStatus((JReferenceType) lhsType);
      StringStatus rhsStatus = getStringStatus((JReferenceType) rhsType);
      int strat = COMPARISON_STRAT[lhsStatus.getIndex()][rhsStatus.getIndex()];

      switch (strat) {
        case STRAT_TRIPLE:
          {
            if (canBeNull(lhs) && canBeNull(rhs)) {
              /*
               * If it's possible for one side to be null and the other side
               * undefined, then mask both sides.
               */
              lhs = maskUndefined(lhs);
              rhs = maskUndefined(rhs);
            }

            JBinaryOperation binOp =
                new JBinaryOperation(x.getSourceInfo(), x.getType(), x.getOp(), lhs, rhs);
            ctx.replaceMe(binOp);
            break;
          }

        case STRAT_DOUBLE:
          {
            boolean lhsNullLit = lhs == program.getLiteralNull();
            boolean rhsNullLit = rhs == program.getLiteralNull();
            if ((lhsNullLit && rhsStatus == StringStatus.NOTSTRING)
                || (rhsNullLit && lhsStatus == StringStatus.NOTSTRING)) {
              /*
               * If either side is a null literal and the other is non-String,
               * replace with a null-check.
               */
              String methodName;
              if (op == JBinaryOperator.EQ) {
                methodName = "Cast.isNull";
              } else {
                methodName = "Cast.isNotNull";
              }
              JMethod isNullMethod = program.getIndexedMethod(methodName);
              JMethodCall call = new JMethodCall(x.getSourceInfo(), null, isNullMethod);
              call.addArg(lhsNullLit ? rhs : lhs);
              ctx.replaceMe(call);
            } else {
              // Replace with a call to Cast.jsEquals, which does a == internally.
              String methodName;
              if (op == JBinaryOperator.EQ) {
                methodName = "Cast.jsEquals";
              } else {
                methodName = "Cast.jsNotEquals";
              }
              JMethod eqMethod = program.getIndexedMethod(methodName);
              JMethodCall call = new JMethodCall(x.getSourceInfo(), null, eqMethod);
              call.addArgs(lhs, rhs);
              ctx.replaceMe(call);
            }
            break;
          }
      }
    }
Beispiel #15
0
 @Override
 JsLiteral translate(JExpression literal) {
   return new JsStringLiteral(literal.getSourceInfo(), ((JStringLiteral) literal).getValue());
 }
Beispiel #16
0
 @Override
 JsLiteral translate(JExpression literal) {
   return new JsNumberLiteral(literal.getSourceInfo(), ((JIntLiteral) literal).getValue());
 }
Beispiel #17
0
 public static void replaceMethodBody(JMethod method, JExpression returnValue) {
   JMethodBody body = (JMethodBody) method.getBody();
   JBlock block = body.getBlock();
   block.clear();
   block.addStmt(returnValue.makeReturnStatement());
 }
 private static boolean canBeNull(JExpression x) {
   return ((JReferenceType) x.getType()).canBeNull();
 }
Beispiel #19
0
    // Arguments for pruned parameters will be pushed right into a multiexpression that will be
    // evaluated with the next arg, e.g. m(arg1, (prunnedArg2, prunnedArg3, arg4)).
    private void maybeReplaceForPrunedParameters(JMethodCall x, Context ctx) {
      if (!priorParametersByMethod.containsKey(x.getTarget())) {
        // No parameter was pruned.
        return;
      }

      JMethodCall replacementCall = x.cloneWithoutParameters();

      assert !x.getTarget().canBePolymorphic();
      List<JParameter> originalParams = priorParametersByMethod.get(x.getTarget());

      // The method and the call agree in the number of parameters.
      assert originalParams.size() == x.getArgs().size();

      // Traverse the call arguments left to right.
      SourceInfo sourceInfo = x.getSourceInfo();
      JMultiExpression unevaluatedArgumentsForPrunedParameters = new JMultiExpression(sourceInfo);
      List<JExpression> args = x.getArgs();
      for (int currentArgumentIndex = 0;
          currentArgumentIndex < args.size();
          ++currentArgumentIndex) {
        JExpression arg = args.get(currentArgumentIndex);

        // If the parameter was not pruned .
        if (referencedNonTypes.contains(originalParams.get(currentArgumentIndex))) {
          // Add the current argument to the list of unevaluated arguments and pass the multi
          // expression to the call.
          unevaluatedArgumentsForPrunedParameters.addExpressions(arg);
          replacementCall.addArg(unevaluatedArgumentsForPrunedParameters);
          // Reset the accumulating multi expression.
          unevaluatedArgumentsForPrunedParameters = new JMultiExpression(sourceInfo);
        } else if (arg.hasSideEffects()) {
          // If the argument was pruned and has sideffects accumulate it; otherwise discard.
          unevaluatedArgumentsForPrunedParameters.addExpressions(arg);
        }
      }

      if (unevaluatedArgumentsForPrunedParameters.isEmpty()) {
        // We are done, all (side effectful) parameters have been evaluated.
        ctx.replaceMe(replacementCall);
        return;
      }

      // If the last few parameters where pruned, we need to evaluate the (side effectful) arguments
      // for those parameters.
      if (replacementCall.getArgs().isEmpty()) {
        // All parameters have been pruned, replace by (prunedArg1, ..., prunedArgn, m()).
        unevaluatedArgumentsForPrunedParameters.addExpressions(replacementCall);
        ctx.replaceMe(unevaluatedArgumentsForPrunedParameters);
        return;
      }
      // Some parameters have been pruned from the end, replace by
      // m(arg1,..., (lastArg = lastUnprunedArg, remainingArgs, lastArg))
      JExpression lastArg = Iterables.getLast(replacementCall.getArgs());
      JLocal tempVar =
          createTempLocal(
              sourceInfo,
              Iterables.getLast(Iterables.filter(originalParams, Predicates.in(referencedNonTypes)))
                  .getType());
      unevaluatedArgumentsForPrunedParameters.addExpressions(
          0,
          JProgram.createAssignment(
              lastArg.getSourceInfo(), new JLocalRef(sourceInfo, tempVar), lastArg));
      unevaluatedArgumentsForPrunedParameters.addExpressions(new JLocalRef(sourceInfo, tempVar));
      replacementCall.setArg(
          replacementCall.getArgs().size() - 1, unevaluatedArgumentsForPrunedParameters);
      ctx.replaceMe(replacementCall);
    }