Beispiel #1
0
 @Override
 public void endVisit(JsniFieldRef x, Context ctx) {
   if (isPruned(x.getField())) {
     String ident = x.getIdent();
     JField nullField = program.getNullField();
     JsniFieldRef nullFieldRef =
         new JsniFieldRef(
             x.getSourceInfo(), ident, nullField, x.getEnclosingType(), x.isLvalue());
     ctx.replaceMe(nullFieldRef);
   }
 }
Beispiel #2
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 #3
0
 @Override
 public void endVisit(JsniMethodRef x, Context ctx) {
   // Redirect JSNI refs to uninstantiable types to the null method.
   if (isPruned(x.getTarget())) {
     String ident = x.getIdent();
     JMethod nullMethod = program.getNullMethod();
     JsniMethodRef nullMethodRef =
         new JsniMethodRef(x.getSourceInfo(), ident, nullMethod, program.getJavaScriptObject());
     ctx.replaceMe(nullMethodRef);
   }
 }
Beispiel #4
0
 @Override
 public void endVisit(JReturnStatement x, Context ctx) {
   info(x);
   JReturnStatement toReturn =
       new JReturnStatement(
           x.getSourceInfo(),
           new JMethodCall(
               x.getSourceInfo(),
               new JThisRef(x.getSourceInfo(), enumType),
               enumObfuscatedName));
   ctx.replaceMe(toReturn);
 }
Beispiel #5
0
    @Override
    public void endVisit(JFieldRef x, Context ctx) {
      // Handle l-values at a higher level.
      if (lValues.peek() == x) {
        return;
      }

      if (isPruned(x.getField())) {
        // The field is gone; replace x by a null field reference.
        JFieldRef fieldRef = transformToNullFieldRef(x, program);
        ctx.replaceMe(fieldRef);
      }
    }
Beispiel #6
0
    @Override
    public void endVisit(JParameterRef x, Context ctx) {
      int paramIndex = methodCall.getTarget().getParams().indexOf(x.getParameter());
      assert paramIndex != -1;

      // Replace with a cloned call argument.
      CloneExpressionVisitor cloner = new CloneExpressionVisitor();
      JExpression arg = methodCall.getArgs().get(paramIndex);
      JExpression clone = cloner.cloneExpression(arg);

      clone = maybeCast(clone, x.getType());
      ctx.replaceMe(clone);
    }
Beispiel #7
0
    @Override
    public void endVisit(JMethodCall x, Context ctx) {
      JMethod method = x.getTarget();

      // Is the method pruned entirely?
      if (isPruned(method)) {
        /*
         * We assert that method must be non-static, otherwise it would have
         * been rescued.
         */
        ctx.replaceMe(transformToNullMethodCall(x, program));
        return;
      }

      maybeReplaceForPrunedParameters(x, ctx);
    }
Beispiel #8
0
    @Override
    public void endVisit(JLocalRef x, Context ctx) {
      JLocal originalLocal = x.getLocal();
      JLocal newLocal = newLocalsByOriginalLocal.get(originalLocal);
      if (newLocal == null) {
        newLocal =
            JProgram.createLocal(
                originalLocal.getSourceInfo(),
                originalLocal.getName(),
                originalLocal.getType(),
                originalLocal.isFinal(),
                methodBody);
        newLocalsByOriginalLocal.put(originalLocal, newLocal);
      }

      ctx.replaceMe(new JLocalRef(x.getSourceInfo(), newLocal));
    }
Beispiel #9
0
    @Override
    public void endVisit(JNameOf x, Context ctx) {
      HasName node = x.getNode();
      boolean pruned;
      if (node instanceof JField) {
        pruned = isPruned((JField) node);
      } else if (node instanceof JMethod) {
        pruned = isPruned((JMethod) node);
      } else if (node instanceof JReferenceType) {
        pruned = !program.typeOracle.isInstantiatedType((JReferenceType) node);
      } else {
        throw new InternalCompilerException("Unhandled JNameOf node: " + node);
      }

      if (pruned) {
        ctx.replaceMe(program.getLiteralNull());
      }
    }
    @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 #11
0
    @Override
    public void endVisit(JBinaryOperation x, Context ctx) {
      if (x.getOp() != JBinaryOperator.ASG) {
        return;
      }
      // The LHS of assignments may have been pruned.
      lValues.pop();
      JExpression lhs = x.getLhs();
      if (!(lhs instanceof JVariableRef)) {
        return;
      }

      JVariableRef variableRef = (JVariableRef) lhs;
      if (isVariablePruned(variableRef.getTarget())) {
        // TODO: better null tracking; we might be missing some NPEs here.
        JExpression replacement =
            makeReplacementForAssignment(x.getSourceInfo(), variableRef, x.getRhs());
        ctx.replaceMe(replacement);
      }
    }
    @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 #13
0
    /**
     * Inline a call to an expression. Returns {@code InlineResult.BLACKLIST} if the method is
     * deemed not inlineable regardless of call site; {@code InlineResult.DO_NOT_BLACKLIST}
     * otherwise.
     */
    private InlineResult tryInlineBody(
        JMethodCall x,
        Context ctx,
        List<JExpression> bodyAsExpressionList,
        boolean ignoringReturn) {

      if (isTooComplexToInline(bodyAsExpressionList, ignoringReturn)) {
        return InlineResult.BLACKLIST;
      }

      // Do not inline anything that modifies one of its params.
      ExpressionAnalyzer targetAnalyzer = new ExpressionAnalyzer();
      targetAnalyzer.accept(bodyAsExpressionList);
      if (targetAnalyzer.hasAssignmentToParameter()) {
        return InlineResult.BLACKLIST;
      }

      // Make sure the expression we're about to inline doesn't include a call
      // to the target method!
      RecursionCheckVisitor recursionCheckVisitor = new RecursionCheckVisitor(x.getTarget());
      recursionCheckVisitor.accept(bodyAsExpressionList);
      if (recursionCheckVisitor.isRecursive()) {
        return InlineResult.BLACKLIST;
      }

      /*
       * After this point, it's possible that the method might be inlinable at
       * some call sites, depending on its arguments. From here on return 'true'
       * as the method might be inlinable elsewhere.
       */

      /*
       * There are a different number of parameters than args - this is likely a
       * result of parameter pruning. Don't consider this call site a candidate.
       *
       * TODO: would this be possible in the trivial delegation case?
       */
      if (x.getTarget().getParams().size() != x.getArgs().size()) {
        // Could not inline this call but the method might be inlineable at a different call site.
        return InlineResult.DO_NOT_BLACKLIST;
      }

      // Run the order check. This verifies that all the parameters are
      // referenced once and only once, not within a conditionally-executing
      // expression and before any tricky target expressions, such as:
      // - assignments to any variable
      // - expressions that throw exceptions
      // - field references

      /*
       * Ensure correct evaluation order or params relative to each other and to
       * other expressions.
       */
      OrderVisitor orderVisitor = new OrderVisitor(x.getTarget().getParams());
      orderVisitor.accept(bodyAsExpressionList);

      switch (orderVisitor.checkResults()) {
        case NO_REFERENCES:
          /*
           * A method that doesn't touch any parameters is trivially inlinable (this
           * covers the empty method case)
           */
          if (!x.hasSideEffects()) {
            markCallsAsSideEffectFree(bodyAsExpressionList);
          }
          new LocalVariableExtruder(getCurrentMethod()).accept(bodyAsExpressionList);
          List<JExpression> expressions = expressionsIncludingArgs(x);
          expressions.addAll(bodyAsExpressionList);
          ctx.replaceMe(JjsUtils.createOptimizedMultiExpression(ignoringReturn, expressions));
          return InlineResult.DO_NOT_BLACKLIST;
        case FAILS:
          /*
           * We can still inline in the case where all of the actual arguments are
           * "safe". They must have no side effects, and also have values which
           * could not be affected by the execution of any code within the callee.
           */
          for (JExpression arg : x.getArgs()) {
            ExpressionAnalyzer argAnalyzer = new ExpressionAnalyzer();
            argAnalyzer.accept(arg);

            if (argAnalyzer.hasAssignment()
                || argAnalyzer.accessesField()
                || argAnalyzer.createsObject()
                || argAnalyzer.canThrowException()) {

              /*
               * This argument evaluation could affect or be affected by the
               * callee so we cannot inline here.
               */
              // Could not inline this call but the method is potentially inlineable.
              return InlineResult.DO_NOT_BLACKLIST;
            }
          }
          // Fall through!
        case CORRECT_ORDER:
        default:
          if (!x.hasSideEffects()) {
            markCallsAsSideEffectFree(bodyAsExpressionList);
          }
          new LocalVariableExtruder(getCurrentMethod()).accept(bodyAsExpressionList);
          // Replace all params in the target expression with the actual arguments.
          ParameterReplacer replacer = new ParameterReplacer(x);
          replacer.accept(bodyAsExpressionList);
          bodyAsExpressionList.add(0, x.getInstance());
          bodyAsExpressionList.add(1, createClinitCall(x));
          ctx.replaceMe(
              JjsUtils.createOptimizedMultiExpression(ignoringReturn, bodyAsExpressionList));
          return InlineResult.DO_NOT_BLACKLIST;
      }
    }
 @Override
 public void endVisit(JParameterRef x, Context ctx) {
   if (x.getParameter() == parameter) {
     ctx.replaceMe(cloner.cloneExpression(expression));
   }
 }
Beispiel #15
0
 /**
  * Replace the current expression with a given multi-expression and mark the method as modified.
  * The dead-code elimination pass will optimize this if necessary.
  */
 private void replaceWithMulti(Context ctx, JMultiExpression multi) {
   ctx.replaceMe(multi);
   modifiedMethods.add(currentMethod);
 }
Beispiel #16
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);
    }
Beispiel #17
0
 @Override
 public void endVisit(JRuntimeTypeReference x, Context ctx) {
   if (!program.typeOracle.isInstantiatedType(x.getReferredType())) {
     ctx.replaceMe(program.getLiteralNull());
   }
 }