All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.java.decompiler.main.AssertProcessor Maven / Gradle / Ivy

Go to download

Modern Java & JVM language decompiler aiming to be as accurate as possible, with an emphasis on output quality.

The newest version!
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.main;

import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
import org.jetbrains.java.decompiler.modules.decompiler.SecondaryFunctionsHelper;
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent.FunctionType;
import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.InterpreterUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public final class AssertProcessor {

  private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError");

  public static void buildAssertions(ClassNode node) {

    ClassWrapper wrapper = node.getWrapper();

    StructField field = findAssertionField(node);

    if (field != null) {

      String key = InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor());

      boolean res = false;

      for (MethodWrapper meth : wrapper.getMethods()) {
        RootStatement root = meth.root;
        if (root != null) {
          res |= replaceAssertions(root, wrapper.getClassStruct().qualifiedName, key);
        }
      }

      if (res) {
        // hide the helper field
        wrapper.getHiddenMembers().add(key);
      }
    }
  }

  private static StructField findAssertionField(ClassNode node) {

    ClassWrapper wrapper = node.getWrapper();

    boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);

    for (StructField fd : wrapper.getClassStruct().getFields()) {

      String keyField = InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor());

      // initializer exists
      if (wrapper.getStaticFieldInitializers().containsKey(keyField)) {

        // access flags set
        if (fd.hasModifier(CodeConstants.ACC_STATIC) && fd.hasModifier(CodeConstants.ACC_FINAL) && (noSynthFlag || fd.isSynthetic())) {

          // field type boolean
          FieldDescriptor fdescr = FieldDescriptor.parseDescriptor(fd.getDescriptor());
          if (VarType.VARTYPE_BOOLEAN.equals(fdescr.type)) {

            Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(keyField);
            if (initializer instanceof FunctionExprent) {
              FunctionExprent fexpr = (FunctionExprent)initializer;

              if (fexpr.getFuncType() == FunctionType.BOOL_NOT &&
                  fexpr.getLstOperands().get(0) instanceof InvocationExprent) {

                InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0);

                if (invexpr.getInstance() != null &&
                    invexpr.getInstance() instanceof ConstExprent &&
                    "desiredAssertionStatus".equals(invexpr.getName()) &&
                    "java/lang/Class".equals(invexpr.getClassname()) &&
                    invexpr.getLstParameters().isEmpty()) {

                  ConstExprent cexpr = (ConstExprent)invexpr.getInstance();
                  if (VarType.VARTYPE_CLASS.equals(cexpr.getConstType())) {

                    ClassNode nd = node;
                    while (nd != null) {
                      if (nd.getWrapper().getClassStruct().qualifiedName.equals(cexpr.getValue())) {
                        break;
                      }
                      nd = nd.parent;
                    }

                    if (nd != null) { // found enclosing class with the same name
                      return fd;
                    }
                  }
                }
              }
            }
          }
        }
      }
    }


    return null;
  }


  private static boolean replaceAssertions(Statement statement, String classname, String key) {

    boolean res = false;

    for (Statement st : new ArrayList<>(statement.getStats())) {
      res |= replaceAssertions(st, classname, key);
    }

    boolean replaced = true;
    while (replaced) {
      replaced = false;

      for (Statement st : new ArrayList<>(statement.getStats())) {
        if (st instanceof IfStatement) {
          if (replaceAssertion(statement, (IfStatement)st, classname, key)) {
            replaced = true;
            break;
          }
        }
      }

      res |= replaced;
    }

    return res;
  }

  private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) {

    boolean throwInIf = true;
    Statement ifstat = stat.getIfstat();
    InvocationExprent throwError = isAssertionError(ifstat);

    if (throwError == null) {
      //check else:
      Statement elsestat = stat.getElsestat();
      throwError = isAssertionError(elsestat);

      if (throwError == null) {
          return false;
      } else {
          throwInIf = false;
      }
    }

    Object[] exprres = getAssertionExprent(stat, stat.getHeadexprent().getCondition().copy(), classname, key, throwInIf);
    if (!(Boolean)exprres[1]) {
      return false;
    }

    List lstParams = new ArrayList<>();

    Exprent ascond = null, retcond = null;
    if (throwInIf) {
      if (exprres[0] != null) {
        ascond = new FunctionExprent(FunctionType.BOOL_NOT, (Exprent)exprres[0], throwError.bytecode);
        retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);
      }
    }
    else {
        ascond =  (Exprent) exprres[0];
        retcond = ascond;
    }


    lstParams.add(retcond == null ? ascond : retcond);
    if (!throwError.getLstParameters().isEmpty()) {
      lstParams.add(throwError.getLstParameters().get(0));
    }

    AssertExprent asexpr = new AssertExprent(lstParams);

    Statement newstat = new BasicBlockStatement(new BasicBlock(
      DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
    newstat.setExprents(Arrays.asList(new Exprent[]{asexpr}));

    Statement first = stat.getFirst();

    if (stat.iftype == IfStatement.IFTYPE_IFELSE ||
      (first.getExprents() != null && !first.getExprents().isEmpty())) {

      first.removeSuccessor(stat.getIfEdge());
      first.removeSuccessor(stat.getElseEdge());

      List lstStatements = new ArrayList<>();
      if (first.getExprents() != null && !first.getExprents().isEmpty()) {
        lstStatements.add(first);
      }

      lstStatements.add(newstat);
      if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
        if (throwInIf) {
          lstStatements.add(stat.getElsestat());
        } else {
          lstStatements.add(stat.getIfstat());
        }
      }

      SequenceStatement sequence = new SequenceStatement(lstStatements);
      sequence.setAllParent();

      for (int i = 0; i < sequence.getStats().size() - 1; i++) {
        sequence.getStats().get(i).addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR,
                                                             sequence.getStats().get(i), sequence.getStats().get(i + 1)));
      }

      if (stat.iftype == IfStatement.IFTYPE_IFELSE || !throwInIf) {
        Statement stmts;
        if (throwInIf) {
          stmts = stat.getElsestat();
        }
        else {
          stmts = stat.getIfstat();
        }

        List lstSuccs = stmts.getAllSuccessorEdges();
        if (!lstSuccs.isEmpty()) {
          StatEdge endedge = lstSuccs.get(0);
          if (endedge.closure == stat) {
            sequence.addLabeledEdge(endedge);
          }
        }
      }

      newstat = sequence;
    }

    newstat.getVarDefinitions().addAll(stat.getVarDefinitions());
    parent.replaceStatement(stat, newstat);
    if (exprres.length > 2) {
      if ((Boolean)exprres[2]) {
        parent.getParent().replaceWith(parent);
      }
    }

    return true;
  }

  private static InvocationExprent isAssertionError(Statement stat) {

    if (stat == null || stat.getExprents() == null || stat.getExprents().size() != 1) {
      return null;
    }

    Exprent expr = stat.getExprents().get(0);

    if (expr instanceof ExitExprent) {
      ExitExprent exexpr = (ExitExprent)expr;
      if (exexpr.getExitType() == ExitExprent.Type.THROW && exexpr.getValue() instanceof NewExprent) {
        NewExprent nexpr = (NewExprent)exexpr.getValue();
        if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewType()) && nexpr.getConstructor() != null) {
          return nexpr.getConstructor();
        }
      }
    }

    return null;
  }

  // {Exprent, valid, needsReplacingOfParent}
  private static Object[] getAssertionExprent(Statement stat, Exprent exprent, String classname, String key, boolean throwInIf) {

    if (exprent instanceof FunctionExprent) {
      FunctionType desiredOperation = FunctionType.BOOLEAN_AND;
      if (!throwInIf) {
        desiredOperation = FunctionType.BOOLEAN_OR;
      }

      FunctionExprent fexpr = (FunctionExprent)exprent;
      if (fexpr.getFuncType() == desiredOperation) {

        for (int i = 0; i < 2; i++) {
          Exprent param = fexpr.getLstOperands().get(i);

          if (isAssertionField(param, classname, key, throwInIf)) {
            return new Object[]{fexpr.getLstOperands().get(1 - i), true};
          }
        }

        for (int i = 0; i < 2; i++) {
          Exprent param = fexpr.getLstOperands().get(i);

          Object[] res = getAssertionExprent(stat, param, classname, key, throwInIf);
          if ((Boolean)res[1]) {
            if (param != res[0]) {
              fexpr.getLstOperands().set(i, (Exprent)res[0]);
            }

            return new Object[]{fexpr, true};
          }
        }
      } else if (isAssertionField(fexpr, classname, key, throwInIf)) {
        // assert false;
        return new Object[]{null, true};
      } else if (fexpr.getFuncType() == FunctionType.BOOL_NOT) {
        // Switch expression assert

        Statement parent = parentSkippingSequences(stat);
        if (parent instanceof IfStatement) {
          Exprent param = fexpr.getLstOperands().get(0);

          if (isAssertionField(((IfStatement)parent).getHeadexprent().getCondition(), classname, key, throwInIf)) {
            // Bool not will be propagated out
            return new Object[]{fexpr, true, true};
          }
        }
      }
    }

    return new Object[]{exprent, false};
  }

  private static Statement parentSkippingSequences(Statement stat) {
    stat = stat.getParent();

    while (stat instanceof SequenceStatement) {
      stat = stat.getParent();
    }

    return stat;
  }

  private static boolean isAssertionField(Exprent exprent, String classname, String key, boolean throwInIf) {
    if (throwInIf) {
      if (exprent instanceof FunctionExprent) {
        FunctionExprent fparam = (FunctionExprent)exprent;
        if (fparam.getFuncType() == FunctionType.BOOL_NOT &&
            fparam.getLstOperands().get(0) instanceof FieldExprent) {
          FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);
          return classname.equals(fdparam.getClassname()) &&
                 key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));
        }
      }
    }
    else {
      if (exprent instanceof FieldExprent) {
        FieldExprent fdparam = (FieldExprent) exprent;
        return classname.equals(fdparam.getClassname()) &&
               key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));
      }
    }
    return false;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy