diff options
Diffstat (limited to 'src/org/jetbrains/java/decompiler/main/AssertProcessor.java')
-rw-r--r-- | src/org/jetbrains/java/decompiler/main/AssertProcessor.java | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java new file mode 100644 index 0000000..86e5c59 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java @@ -0,0 +1,321 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.main; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +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.AssertExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +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; + +public 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.wrapper; + + 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.getHideMembers().add(key); + } + } + + } + + private static StructField findAssertionField(ClassNode node) { + + ClassWrapper wrapper = node.wrapper; + + 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)) { + + int flags = fd.access_flags; + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); + + // access flags set + if((flags & CodeConstants.ACC_STATIC) != 0 && (flags & CodeConstants.ACC_FINAL) != 0 && + (isSynthetic || nosynthflag)) { + + // field type boolean + FieldDescriptor fdescr = FieldDescriptor.parseDescriptor(fd.getDescriptor()); + if(VarType.VARTYPE_BOOLEAN.equals(fdescr.type)) { + + Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(keyField); + if(initializer.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)initializer; + + if(fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && + fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) { + + InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0); + + if(invexpr.getInstance() != null && invexpr.getInstance().type == Exprent.EXPRENT_CONST && "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.wrapper.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 : statement.getStats()) { + res |= replaceAssertions(st, classname, key); + } + + boolean replaced = true; + while(replaced) { + replaced = false; + + for(Statement st : statement.getStats()) { + if(st.type == Statement.TYPE_IF) { + 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) { + + Statement ifstat = stat.getIfstat(); + InvocationExprent throwError = isAssertionError(ifstat); + + if(throwError == null) { + return false; + } + + Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key); + if(!(Boolean)exprres[1]) { + return false; + } + + List<Exprent> lstParams = new ArrayList<Exprent>(); + + Exprent ascond = null, retcond = null; + if(exprres[0] != null) { + ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{(Exprent)exprres[0]})); + retcond = SecondaryFunctionsHelper.propagateBoolNot(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<Statement> lstStatements = new ArrayList<Statement>(); + if(first.getExprents() != null && !first.getExprents().isEmpty()) { + lstStatements.add(first); + } + lstStatements.add(newstat); + if(stat.iftype == IfStatement.IFTYPE_IFELSE) { + lstStatements.add(stat.getElsestat()); + } + + 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) { + Statement ifelse = stat.getElsestat(); + + List<StatEdge> lstSuccs = ifelse.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); + + 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.type == Exprent.EXPRENT_EXIT) { + ExitExprent exexpr = (ExitExprent)expr; + if(exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) { + NewExprent nexpr = (NewExprent)exexpr.getValue(); + if(CLASS_ASSERTION_ERROR.equals(nexpr.getNewtype()) && nexpr.getConstructor() != null) { + return nexpr.getConstructor(); + } + } + } + + return null; + } + + private static Object[] getAssertionExprent(Exprent exprent, String classname, String key) { + + if(exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + if(fexpr.getFunctype() == FunctionExprent.FUNCTION_CADD) { + + for(int i=0;i<2;i++) { + Exprent param = fexpr.getLstOperands().get(i); + + if(isAssertionField(param, classname, key)) { + 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(param, classname, key); + 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)) { + // assert false; + return new Object[] {null, true}; + } + } + + return new Object[] {exprent, false}; + } + + private static boolean isAssertionField(Exprent exprent, String classname, String key) { + + if(exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fparam = (FunctionExprent)exprent; + if(fparam.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && + fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { + FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0); + if(classname.equals(fdparam.getClassname()) + && key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) { + return true; + } + } + } + + return false; + } +} |